summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/loader
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/loader
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/loader')
-rw-r--r--Source/WebCore/loader/CachedMetadata.h125
-rw-r--r--Source/WebCore/loader/CrossOriginAccessControl.cpp128
-rw-r--r--Source/WebCore/loader/CrossOriginAccessControl.h42
-rw-r--r--Source/WebCore/loader/CrossOriginPreflightResultCache.cpp192
-rw-r--r--Source/WebCore/loader/CrossOriginPreflightResultCache.h85
-rw-r--r--Source/WebCore/loader/DocumentLoadTiming.h64
-rw-r--r--Source/WebCore/loader/DocumentLoader.cpp840
-rw-r--r--Source/WebCore/loader/DocumentLoader.h338
-rw-r--r--Source/WebCore/loader/DocumentThreadableLoader.cpp372
-rw-r--r--Source/WebCore/loader/DocumentThreadableLoader.h104
-rw-r--r--Source/WebCore/loader/DocumentWriter.cpp256
-rw-r--r--Source/WebCore/loader/DocumentWriter.h91
-rw-r--r--Source/WebCore/loader/EmptyClients.h586
-rw-r--r--Source/WebCore/loader/FTPDirectoryParser.cpp1715
-rw-r--r--Source/WebCore/loader/FTPDirectoryParser.h157
-rw-r--r--Source/WebCore/loader/FormState.cpp50
-rw-r--r--Source/WebCore/loader/FormState.h66
-rw-r--r--Source/WebCore/loader/FormSubmission.cpp246
-rw-r--r--Source/WebCore/loader/FormSubmission.h128
-rw-r--r--Source/WebCore/loader/FrameLoader.cpp3566
-rw-r--r--Source/WebCore/loader/FrameLoader.h510
-rw-r--r--Source/WebCore/loader/FrameLoaderClient.h314
-rw-r--r--Source/WebCore/loader/FrameLoaderStateMachine.cpp73
-rw-r--r--Source/WebCore/loader/FrameLoaderStateMachine.h67
-rw-r--r--Source/WebCore/loader/FrameLoaderTypes.h129
-rw-r--r--Source/WebCore/loader/FrameNetworkingContext.h51
-rw-r--r--Source/WebCore/loader/HistoryController.cpp681
-rw-r--r--Source/WebCore/loader/HistoryController.h105
-rw-r--r--Source/WebCore/loader/ImageLoader.cpp375
-rw-r--r--Source/WebCore/loader/ImageLoader.h90
-rw-r--r--Source/WebCore/loader/MainResourceLoader.cpp624
-rw-r--r--Source/WebCore/loader/MainResourceLoader.h115
-rw-r--r--Source/WebCore/loader/NavigationAction.cpp83
-rw-r--r--Source/WebCore/loader/NavigationAction.h61
-rw-r--r--Source/WebCore/loader/NavigationScheduler.cpp442
-rw-r--r--Source/WebCore/loader/NavigationScheduler.h108
-rw-r--r--Source/WebCore/loader/NetscapePlugInStreamLoader.cpp141
-rw-r--r--Source/WebCore/loader/NetscapePlugInStreamLoader.h70
-rw-r--r--Source/WebCore/loader/PingLoader.cpp111
-rw-r--r--Source/WebCore/loader/PingLoader.h77
-rw-r--r--Source/WebCore/loader/PlaceholderDocument.cpp39
-rw-r--r--Source/WebCore/loader/PlaceholderDocument.h48
-rw-r--r--Source/WebCore/loader/PolicyCallback.cpp134
-rw-r--r--Source/WebCore/loader/PolicyCallback.h82
-rw-r--r--Source/WebCore/loader/PolicyChecker.cpp197
-rw-r--r--Source/WebCore/loader/PolicyChecker.h97
-rw-r--r--Source/WebCore/loader/ProgressTracker.cpp263
-rw-r--r--Source/WebCore/loader/ProgressTracker.h80
-rw-r--r--Source/WebCore/loader/ResourceLoadNotifier.cpp185
-rw-r--r--Source/WebCore/loader/ResourceLoadNotifier.h74
-rw-r--r--Source/WebCore/loader/ResourceLoadScheduler.cpp289
-rw-r--r--Source/WebCore/loader/ResourceLoadScheduler.h117
-rw-r--r--Source/WebCore/loader/ResourceLoader.cpp527
-rw-r--r--Source/WebCore/loader/ResourceLoader.h179
-rw-r--r--Source/WebCore/loader/SinkDocument.cpp62
-rw-r--r--Source/WebCore/loader/SinkDocument.h49
-rw-r--r--Source/WebCore/loader/SubframeLoader.cpp382
-rw-r--r--Source/WebCore/loader/SubframeLoader.h99
-rw-r--r--Source/WebCore/loader/SubresourceLoader.cpp279
-rw-r--r--Source/WebCore/loader/SubresourceLoader.h68
-rw-r--r--Source/WebCore/loader/SubresourceLoaderClient.h62
-rw-r--r--Source/WebCore/loader/SubstituteData.h69
-rw-r--r--Source/WebCore/loader/SubstituteResource.h64
-rw-r--r--Source/WebCore/loader/TextResourceDecoder.cpp694
-rw-r--r--Source/WebCore/loader/TextResourceDecoder.h101
-rw-r--r--Source/WebCore/loader/ThreadableLoader.cpp72
-rw-r--r--Source/WebCore/loader/ThreadableLoader.h85
-rw-r--r--Source/WebCore/loader/ThreadableLoaderClient.h59
-rw-r--r--Source/WebCore/loader/ThreadableLoaderClientWrapper.h117
-rw-r--r--Source/WebCore/loader/WorkerThreadableLoader.cpp248
-rw-r--r--Source/WebCore/loader/WorkerThreadableLoader.h147
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCache.cpp204
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCache.h116
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp1181
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheGroup.h217
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheHost.cpp466
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheHost.h208
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheResource.cpp90
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheResource.h75
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp1306
-rw-r--r--Source/WebCore/loader/appcache/ApplicationCacheStorage.h147
-rw-r--r--Source/WebCore/loader/appcache/DOMApplicationCache.cpp130
-rw-r--r--Source/WebCore/loader/appcache/DOMApplicationCache.h98
-rw-r--r--Source/WebCore/loader/appcache/DOMApplicationCache.idl69
-rw-r--r--Source/WebCore/loader/appcache/ManifestParser.cpp188
-rw-r--r--Source/WebCore/loader/appcache/ManifestParser.h50
-rw-r--r--Source/WebCore/loader/archive/Archive.h62
-rw-r--r--Source/WebCore/loader/archive/ArchiveFactory.cpp96
-rw-r--r--Source/WebCore/loader/archive/ArchiveFactory.h50
-rw-r--r--Source/WebCore/loader/archive/ArchiveResource.cpp62
-rw-r--r--Source/WebCore/loader/archive/ArchiveResource.h62
-rw-r--r--Source/WebCore/loader/archive/ArchiveResourceCollection.cpp89
-rw-r--r--Source/WebCore/loader/archive/ArchiveResourceCollection.h59
-rw-r--r--Source/WebCore/loader/archive/cf/LegacyWebArchive.cpp591
-rw-r--r--Source/WebCore/loader/archive/cf/LegacyWebArchive.h70
-rw-r--r--Source/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm76
-rw-r--r--Source/WebCore/loader/cache/CachePolicy.h41
-rw-r--r--Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp151
-rw-r--r--Source/WebCore/loader/cache/CachedCSSStyleSheet.h67
-rw-r--r--Source/WebCore/loader/cache/CachedFont.cpp217
-rw-r--r--Source/WebCore/loader/cache/CachedFont.h89
-rw-r--r--Source/WebCore/loader/cache/CachedImage.cpp386
-rw-r--r--Source/WebCore/loader/cache/CachedImage.h105
-rw-r--r--Source/WebCore/loader/cache/CachedResource.cpp596
-rw-r--r--Source/WebCore/loader/cache/CachedResource.h296
-rw-r--r--Source/WebCore/loader/cache/CachedResourceClient.h71
-rw-r--r--Source/WebCore/loader/cache/CachedResourceClientWalker.cpp53
-rw-r--r--Source/WebCore/loader/cache/CachedResourceClientWalker.h49
-rw-r--r--Source/WebCore/loader/cache/CachedResourceHandle.cpp42
-rw-r--r--Source/WebCore/loader/cache/CachedResourceHandle.h104
-rw-r--r--Source/WebCore/loader/cache/CachedResourceLoader.cpp733
-rw-r--r--Source/WebCore/loader/cache/CachedResourceLoader.h156
-rw-r--r--Source/WebCore/loader/cache/CachedResourceRequest.cpp280
-rw-r--r--Source/WebCore/loader/cache/CachedResourceRequest.h65
-rw-r--r--Source/WebCore/loader/cache/CachedScript.cpp124
-rw-r--r--Source/WebCore/loader/cache/CachedScript.h65
-rw-r--r--Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp100
-rw-r--r--Source/WebCore/loader/cache/CachedXSLStyleSheet.h62
-rw-r--r--Source/WebCore/loader/cache/MemoryCache.cpp670
-rw-r--r--Source/WebCore/loader/cache/MemoryCache.h231
-rw-r--r--Source/WebCore/loader/cf/ResourceLoaderCFNet.cpp48
-rw-r--r--Source/WebCore/loader/icon/IconDatabase.cpp2096
-rw-r--r--Source/WebCore/loader/icon/IconDatabase.h244
-rw-r--r--Source/WebCore/loader/icon/IconDatabaseClient.h50
-rw-r--r--Source/WebCore/loader/icon/IconDatabaseNone.cpp218
-rw-r--r--Source/WebCore/loader/icon/IconLoader.cpp174
-rw-r--r--Source/WebCore/loader/icon/IconLoader.h69
-rw-r--r--Source/WebCore/loader/icon/IconRecord.cpp106
-rw-r--r--Source/WebCore/loader/icon/IconRecord.h117
-rw-r--r--Source/WebCore/loader/icon/PageURLRecord.cpp63
-rw-r--r--Source/WebCore/loader/icon/PageURLRecord.h85
-rw-r--r--Source/WebCore/loader/icon/wince/IconDatabaseWinCE.cpp81
-rw-r--r--Source/WebCore/loader/mac/DocumentLoaderMac.cpp84
-rw-r--r--Source/WebCore/loader/mac/LoaderNSURLExtras.h39
-rw-r--r--Source/WebCore/loader/mac/LoaderNSURLExtras.mm102
-rw-r--r--Source/WebCore/loader/mac/ResourceLoaderMac.mm49
-rw-r--r--Source/WebCore/loader/win/DocumentLoaderWin.cpp46
-rw-r--r--Source/WebCore/loader/win/FrameLoaderWin.cpp63
138 files changed, 32595 insertions, 0 deletions
diff --git a/Source/WebCore/loader/CachedMetadata.h b/Source/WebCore/loader/CachedMetadata.h
new file mode 100644
index 0000000..120e4c0
--- /dev/null
+++ b/Source/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/Source/WebCore/loader/CrossOriginAccessControl.cpp b/Source/WebCore/loader/CrossOriginAccessControl.cpp
new file mode 100644
index 0000000..8ca821e
--- /dev/null
+++ b/Source/WebCore/loader/CrossOriginAccessControl.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+#include "CrossOriginAccessControl.h"
+
+#include "HTTPParsers.h"
+#include "ResourceResponse.h"
+#include "SecurityOrigin.h"
+#include <wtf/HashSet.h>
+#include <wtf/Threading.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+bool isOnAccessControlSimpleRequestMethodWhitelist(const String& method)
+{
+ return method == "GET" || method == "HEAD" || method == "POST";
+}
+
+bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name, const String& value)
+{
+ if (equalIgnoringCase(name, "accept") || equalIgnoringCase(name, "accept-language") || equalIgnoringCase(name, "content-language"))
+ return true;
+
+ // Preflight is required for MIME types that can not be sent via form submission.
+ if (equalIgnoringCase(name, "content-type")) {
+ String mimeType = extractMIMETypeFromMediaType(value);
+ return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded")
+ || equalIgnoringCase(mimeType, "multipart/form-data")
+ || equalIgnoringCase(mimeType, "text/plain");
+ }
+
+ return false;
+}
+
+bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap& headerMap)
+{
+ if (!isOnAccessControlSimpleRequestMethodWhitelist(method))
+ return false;
+
+ HTTPHeaderMap::const_iterator end = headerMap.end();
+ for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) {
+ if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->first, it->second))
+ return false;
+ }
+
+ return true;
+}
+
+typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet;
+static PassOwnPtr<HTTPHeaderSet> createAllowedCrossOriginResponseHeadersSet()
+{
+ OwnPtr<HTTPHeaderSet> headerSet = adoptPtr(new HashSet<String, CaseFoldingHash>);
+
+ headerSet->add("cache-control");
+ headerSet->add("content-language");
+ headerSet->add("content-type");
+ headerSet->add("expires");
+ headerSet->add("last-modified");
+ headerSet->add("pragma");
+
+ return headerSet.release();
+}
+
+bool isOnAccessControlResponseHeaderWhitelist(const String& name)
+{
+ AtomicallyInitializedStatic(HTTPHeaderSet*, allowedCrossOriginResponseHeaders = createAllowedCrossOriginResponseHeadersSet().leakPtr());
+
+ return allowedCrossOriginResponseHeaders->contains(name);
+}
+
+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.
+ const String& accessControlOriginString = response.httpHeaderField("Access-Control-Allow-Origin");
+ if (accessControlOriginString == "*" && !includeCredentials)
+ return true;
+
+ 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)) {
+ 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") {
+ errorDescription = "Credentials flag is true, but Access-Control-Allow-Credentials is not \"true\".";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/CrossOriginAccessControl.h b/Source/WebCore/loader/CrossOriginAccessControl.h
new file mode 100644
index 0000000..c44963b
--- /dev/null
+++ b/Source/WebCore/loader/CrossOriginAccessControl.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+ class HTTPHeaderMap;
+ class ResourceResponse;
+ class SecurityOrigin;
+
+ 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*, String& errorDescription);
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/CrossOriginPreflightResultCache.cpp b/Source/WebCore/loader/CrossOriginPreflightResultCache.cpp
new file mode 100644
index 0000000..18e4be2
--- /dev/null
+++ b/Source/WebCore/loader/CrossOriginPreflightResultCache.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+#include "CrossOriginPreflightResultCache.h"
+
+#include "CrossOriginAccessControl.h"
+#include "ResourceResponse.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Threading.h>
+
+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.
+
+static bool parseAccessControlMaxAge(const String& string, unsigned& expiryDelta)
+{
+ // FIXME: this will not do the correct thing for a number starting with a '+'
+ bool ok = false;
+ expiryDelta = string.toUIntStrict(&ok);
+ return ok;
+}
+
+template<class HashType>
+static void addToAccessControlAllowList(const String& string, unsigned start, unsigned end, HashSet<String, HashType>& set)
+{
+ StringImpl* stringImpl = string.impl();
+ if (!stringImpl)
+ return;
+
+ // Skip white space from start.
+ while (start <= end && isSpaceOrNewline((*stringImpl)[start]))
+ ++start;
+
+ // only white space
+ if (start > end)
+ return;
+
+ // Skip white space from end.
+ while (end && isSpaceOrNewline((*stringImpl)[end]))
+ --end;
+
+ set.add(string.substring(start, end - start + 1));
+}
+
+template<class HashType>
+static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>& set)
+{
+ 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 != string.length())
+ addToAccessControlAllowList(string, start, string.length() - 1, set);
+
+ return true;
+}
+
+bool CrossOriginPreflightResultCacheItem::parse(const ResourceResponse& response, String& errorDescription)
+{
+ m_methods.clear();
+ 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)) {
+ errorDescription = "Cannot parse Access-Control-Allow-Headers response header field.";
+ return false;
+ }
+
+ unsigned expiryDelta;
+ if (parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) {
+ if (expiryDelta > maxPreflightCacheTimeoutSeconds)
+ expiryDelta = maxPreflightCacheTimeoutSeconds;
+ } else
+ expiryDelta = defaultPreflightCacheTimeoutSeconds;
+
+ m_absoluteExpiryTime = currentTime() + expiryDelta;
+ return true;
+}
+
+bool CrossOriginPreflightResultCacheItem::allowsCrossOriginMethod(const String& method, String& errorDescription) const
+{
+ 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, 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)) {
+ 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, ignoredExplanation))
+ return false;
+ if (!allowsCrossOriginHeaders(requestHeaders, ignoredExplanation))
+ return false;
+ return true;
+}
+
+CrossOriginPreflightResultCache& CrossOriginPreflightResultCache::shared()
+{
+ DEFINE_STATIC_LOCAL(CrossOriginPreflightResultCache, cache, ());
+ ASSERT(isMainThread());
+ return cache;
+}
+
+void CrossOriginPreflightResultCache::appendEntry(const String& origin, const KURL& url, PassOwnPtr<CrossOriginPreflightResultCacheItem> preflightResult)
+{
+ ASSERT(isMainThread());
+ 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)
+{
+ ASSERT(isMainThread());
+ CrossOriginPreflightResultHashMap::iterator cacheIt = m_preflightHashMap.find(std::make_pair(origin, url));
+ if (cacheIt == m_preflightHashMap.end())
+ return false;
+
+ if (cacheIt->second->allowsRequest(includeCredentials, method, requestHeaders))
+ return true;
+
+ delete cacheIt->second;
+ m_preflightHashMap.remove(cacheIt);
+ return false;
+}
+
+void CrossOriginPreflightResultCache::empty()
+{
+ ASSERT(isMainThread());
+ deleteAllValues(m_preflightHashMap);
+ m_preflightHashMap.clear();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/CrossOriginPreflightResultCache.h b/Source/WebCore/loader/CrossOriginPreflightResultCache.h
new file mode 100644
index 0000000..1016aed
--- /dev/null
+++ b/Source/WebCore/loader/CrossOriginPreflightResultCache.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef CrossOriginPreflightResultCache_h
+#define CrossOriginPreflightResultCache_h
+
+#include "KURLHash.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+ class HTTPHeaderMap;
+ class ResourceResponse;
+
+ class CrossOriginPreflightResultCacheItem : public Noncopyable {
+ public:
+ CrossOriginPreflightResultCacheItem(bool credentials)
+ : m_absoluteExpiryTime(0)
+ , m_credentials(credentials)
+ {
+ }
+
+ 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:
+ typedef HashSet<String, CaseFoldingHash> HeadersSet;
+
+ // FIXME: A better solution to holding onto the absolute expiration time might be
+ // to start a timer for the expiration delta that removes this from the cache when
+ // it fires.
+ double m_absoluteExpiryTime;
+ bool m_credentials;
+ HashSet<String> m_methods;
+ HeadersSet m_headers;
+ };
+
+ class CrossOriginPreflightResultCache : public Noncopyable {
+ public:
+ static CrossOriginPreflightResultCache& shared();
+
+ 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();
+
+ private:
+ CrossOriginPreflightResultCache() { }
+
+ typedef HashMap<std::pair<String, KURL>, CrossOriginPreflightResultCacheItem*> CrossOriginPreflightResultHashMap;
+
+ CrossOriginPreflightResultHashMap m_preflightHashMap;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/loader/DocumentLoadTiming.h b/Source/WebCore/loader/DocumentLoadTiming.h
new file mode 100644
index 0000000..d4aa7d0
--- /dev/null
+++ b/Source/WebCore/loader/DocumentLoadTiming.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ *
+ * 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 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
+ * 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 DocumentLoadTiming_h
+#define DocumentLoadTiming_h
+
+namespace WebCore {
+
+struct DocumentLoadTiming {
+ DocumentLoadTiming()
+ : navigationStart(0.0)
+ , unloadEventStart(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)
+ , hasCrossOriginRedirect(false)
+ , hasSameOriginAsPreviousDocument(false)
+ {
+ }
+
+ double navigationStart;
+ double unloadEventStart;
+ double unloadEventEnd;
+ double redirectStart;
+ double redirectEnd;
+ short redirectCount;
+ double fetchStart;
+ double responseEnd;
+ double loadEventStart;
+ double loadEventEnd;
+ bool hasCrossOriginRedirect;
+ bool hasSameOriginAsPreviousDocument;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/DocumentLoader.cpp b/Source/WebCore/loader/DocumentLoader.cpp
new file mode 100644
index 0000000..4e7c656
--- /dev/null
+++ b/Source/WebCore/loader/DocumentLoader.cpp
@@ -0,0 +1,840 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DocumentLoader.h"
+
+#include "ApplicationCacheHost.h"
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+#include "ArchiveFactory.h"
+#include "ArchiveResourceCollection.h"
+#else
+#include "SubstituteResource.h"
+#endif
+#include "CachedPage.h"
+#include "CachedResourceLoader.h"
+#include "DOMWindow.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"
+#include "MainResourceLoader.h"
+#include "Page.h"
+#include "PlatformString.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+
+#include <wtf/Assertions.h>
+#include <wtf/text/CString.h>
+#include <wtf/unicode/Unicode.h>
+
+namespace WebCore {
+
+static void cancelAll(const ResourceLoaderSet& loaders)
+{
+ Vector<RefPtr<ResourceLoader> > loadersCopy;
+ copyToVector(loaders, loadersCopy);
+ size_t size = loadersCopy.size();
+ for (size_t i = 0; i < size; ++i)
+ loadersCopy[i]->cancel();
+}
+
+static void setAllDefersLoading(const ResourceLoaderSet& loaders, bool defers)
+{
+ Vector<RefPtr<ResourceLoader> > loadersCopy;
+ copyToVector(loaders, loadersCopy);
+ size_t size = loadersCopy.size();
+ for (size_t i = 0; i < size; ++i)
+ loadersCopy[i]->setDefersLoading(defers);
+}
+
+DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& substituteData)
+ : m_deferMainResourceDataLoad(true)
+ , m_frame(0)
+ , m_originalRequest(req)
+ , m_substituteData(substituteData)
+ , m_originalRequestCopy(req)
+ , m_request(req)
+ , m_committed(false)
+ , m_isStopping(false)
+ , m_loading(false)
+ , 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(adoptPtr(new ApplicationCacheHost(this)))
+#endif
+{
+}
+
+FrameLoader* DocumentLoader::frameLoader() const
+{
+ if (!m_frame)
+ return 0;
+ return m_frame->loader();
+}
+
+DocumentLoader::~DocumentLoader()
+{
+ ASSERT(!m_frame || frameLoader()->activeDocumentLoader() != this || !frameLoader()->isLoading());
+}
+
+PassRefPtr<SharedBuffer> DocumentLoader::mainResourceData() const
+{
+ if (m_mainResourceData)
+ return m_mainResourceData;
+ if (m_mainResourceLoader)
+ return m_mainResourceLoader->resourceData();
+ return 0;
+}
+
+const ResourceRequest& DocumentLoader::originalRequest() const
+{
+ return m_originalRequest;
+}
+
+const ResourceRequest& DocumentLoader::originalRequestCopy() const
+{
+ return m_originalRequestCopy;
+}
+
+const ResourceRequest& DocumentLoader::request() const
+{
+ return m_request;
+}
+
+ResourceRequest& DocumentLoader::request()
+{
+ return m_request;
+}
+
+const KURL& DocumentLoader::url() const
+{
+ return request().url();
+}
+
+void DocumentLoader::replaceRequestURLForSameDocumentNavigation(const KURL& url)
+{
+ m_originalRequestCopy.setURL(url);
+ m_request.setURL(url);
+}
+
+void DocumentLoader::setRequest(const ResourceRequest& req)
+{
+ // Replacing an unreachable URL with alternate content looks like a server-side
+ // redirect at this point, but we can replace a committed dataSource.
+ bool handlingUnreachableURL = false;
+
+ handlingUnreachableURL = m_substituteData.isValid() && !m_substituteData.failingURL().isEmpty();
+
+ if (handlingUnreachableURL)
+ m_committed = false;
+
+ // We should never be getting a redirect callback after the data
+ // source is committed, except in the unreachable URL case. It
+ // would be a WebFoundation bug if it sent a redirect callback after commit.
+ ASSERT(!m_committed);
+
+ KURL oldURL = m_request.url();
+ m_request = req;
+
+ // Only send webView:didReceiveServerRedirectForProvisionalLoadForFrame: if URL changed.
+ // Also, don't send it when replacing unreachable URLs with alternate content.
+ if (!handlingUnreachableURL && oldURL != req.url())
+ frameLoader()->didReceiveServerRedirectForProvisionalLoadForFrame();
+}
+
+void DocumentLoader::setMainDocumentError(const ResourceError& error)
+{
+ m_mainDocumentError = error;
+ frameLoader()->setMainDocumentError(this, error);
+ }
+
+void DocumentLoader::clearErrors()
+{
+ m_mainDocumentError = ResourceError();
+}
+
+void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComplete)
+{
+ ASSERT(!error.isNull());
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ m_applicationCacheHost->failedLoadingMainResource();
+#endif
+
+ if (!frameLoader())
+ return;
+ setMainDocumentError(error);
+ if (isComplete)
+ frameLoader()->mainReceivedCompleteError(this, error);
+}
+
+// Cancels the data source's pending loads. Conceptually, a data source only loads
+// one document at a time, but one document may have many related resources.
+// stopLoading will stop all loads initiated by the data source,
+// but not loads initiated by child frames' data sources -- that's the WebFrame's job.
+void DocumentLoader::stopLoading(DatabasePolicy databasePolicy)
+{
+ // In some rare cases, calling FrameLoader::stopLoading could set m_loading to false.
+ // (This can happen when there's a single XMLHttpRequest currently loading and stopLoading causes it
+ // to stop loading. Because of this, we need to save it so we don't return early.
+ bool loading = m_loading;
+
+ if (m_committed) {
+ // Attempt to stop the frame if the document loader is loading, or if it is done loading but
+ // still parsing. Failure to do so can cause a world leak.
+ Document* doc = m_frame->document();
+
+ if (loading || doc->parsing())
+ m_frame->loader()->stopLoading(UnloadEventPolicyNone, 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;
+
+ RefPtr<Frame> protectFrame(m_frame);
+ RefPtr<DocumentLoader> protectLoader(this);
+
+ m_isStopping = true;
+
+ FrameLoader* frameLoader = DocumentLoader::frameLoader();
+
+ if (m_mainResourceLoader)
+ // Stop the main resource loader and let it send the cancelled message.
+ m_mainResourceLoader->cancel();
+ else if (!m_subresourceLoaders.isEmpty())
+ // The main resource loader already finished loading. Set the cancelled error on the
+ // document and let the subresourceLoaders send individual cancelled messages below.
+ setMainDocumentError(frameLoader->cancelledError(m_request));
+ else
+ // If there are no resource loaders, we need to manufacture a cancelled message.
+ // (A back/forward navigation has no resource loaders because its resources are cached.)
+ mainReceivedError(frameLoader->cancelledError(m_request), true);
+
+ stopLoadingSubresources();
+ stopLoadingPlugIns();
+
+ m_isStopping = false;
+}
+
+void DocumentLoader::setupForReplace()
+{
+ frameLoader()->setupForReplace();
+ m_committed = false;
+}
+
+void DocumentLoader::commitIfReady()
+{
+ if (m_gotFirstByte && !m_committed) {
+ m_committed = true;
+ frameLoader()->commitProvisionalLoad();
+ }
+}
+
+void DocumentLoader::finishedLoading()
+{
+ m_gotFirstByte = true;
+ commitIfReady();
+ if (FrameLoader* loader = frameLoader()) {
+ loader->finishedLoadingDocument(this);
+ loader->writer()->end();
+ }
+}
+
+void DocumentLoader::commitLoad(const char* data, int length)
+{
+ // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
+ // by starting a new load, so retain temporarily.
+ RefPtr<DocumentLoader> protect(this);
+
+ commitIfReady();
+ 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
+{
+ return !frameLoader()->isReplacing() || MIMEType == "text/html";
+}
+
+void DocumentLoader::receivedData(const char* data, int length)
+{
+ m_gotFirstByte = true;
+ if (doesProgressiveLoad(m_response.mimeType()))
+ commitLoad(data, length);
+}
+
+void DocumentLoader::setupForReplaceByMIMEType(const String& newMIMEType)
+{
+ if (!m_gotFirstByte)
+ return;
+
+ String oldMIMEType = m_response.mimeType();
+
+ if (!doesProgressiveLoad(oldMIMEType)) {
+ frameLoader()->revertToProvisional(this);
+ setupForReplace();
+ RefPtr<SharedBuffer> resourceData = mainResourceData();
+ commitLoad(resourceData->data(), resourceData->size());
+ }
+
+ frameLoader()->finishedLoadingDocument(this);
+ m_frame->loader()->writer()->end();
+
+ frameLoader()->setReplacing();
+ m_gotFirstByte = false;
+
+ if (doesProgressiveLoad(newMIMEType)) {
+ frameLoader()->revertToProvisional(this);
+ setupForReplace();
+ }
+
+ stopLoadingSubresources();
+ stopLoadingPlugIns();
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ clearArchiveResources();
+#endif
+}
+
+void DocumentLoader::updateLoading()
+{
+ if (!m_frame) {
+ setLoading(false);
+ return;
+ }
+ ASSERT(this == frameLoader()->activeDocumentLoader());
+ bool wasLoading = m_loading;
+ setLoading(frameLoader()->isLoading());
+
+ if (wasLoading && !m_loading) {
+ if (DOMWindow* window = m_frame->existingDOMWindow())
+ window->finishedLoading();
+ }
+}
+
+void DocumentLoader::setFrame(Frame* frame)
+{
+ if (m_frame == frame)
+ return;
+ ASSERT(frame && !m_frame);
+ m_frame = frame;
+ attachToFrame();
+}
+
+void DocumentLoader::attachToFrame()
+{
+ ASSERT(m_frame);
+}
+
+void DocumentLoader::detachFromFrame()
+{
+ ASSERT(m_frame);
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ m_applicationCacheHost->setDOMApplicationCache(0);
+#endif
+ m_frame = 0;
+}
+
+void DocumentLoader::prepareForLoadStart()
+{
+ ASSERT(!m_isStopping);
+ setPrimaryLoadComplete(false);
+ ASSERT(frameLoader());
+ clearErrors();
+
+ setLoading(true);
+
+ frameLoader()->prepareForLoadStart();
+}
+
+void DocumentLoader::setPrimaryLoadComplete(bool flag)
+{
+ m_primaryLoadComplete = flag;
+ if (flag) {
+ if (m_mainResourceLoader) {
+ m_mainResourceData = m_mainResourceLoader->resourceData();
+ m_mainResourceLoader = 0;
+ }
+
+ if (this == frameLoader()->activeDocumentLoader())
+ updateLoading();
+ }
+}
+
+bool DocumentLoader::isLoadingInAPISense() const
+{
+ // Once a frame has loaded, we no longer need to consider subresources,
+ // but we still need to consider subframes.
+ if (frameLoader()->state() != FrameStateComplete) {
+ if (!m_primaryLoadComplete && isLoading())
+ return true;
+ if (!m_subresourceLoaders.isEmpty())
+ return true;
+ Document* doc = m_frame->document();
+ if (doc->cachedResourceLoader()->requestCount())
+ return true;
+ if (DocumentParser* parser = doc->parser())
+ if (parser->processingData())
+ return true;
+ }
+ return frameLoader()->subframeIsLoading();
+}
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+void DocumentLoader::addAllArchiveResources(Archive* archive)
+{
+ if (!m_archiveResourceCollection)
+ m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection);
+
+ ASSERT(archive);
+ if (!archive)
+ return;
+
+ m_archiveResourceCollection->addAllResources(archive);
+}
+
+// FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on.
+// Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps?
+void DocumentLoader::addArchiveResource(PassRefPtr<ArchiveResource> resource)
+{
+ if (!m_archiveResourceCollection)
+ m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection);
+
+ ASSERT(resource);
+ if (!resource)
+ return;
+
+ m_archiveResourceCollection->addResource(resource);
+}
+
+ArchiveResource* DocumentLoader::archiveResourceForURL(const KURL& url) const
+{
+ if (!m_archiveResourceCollection)
+ return 0;
+
+ ArchiveResource* resource = m_archiveResourceCollection->archiveResourceForURL(url);
+
+ return resource && !resource->shouldIgnoreWhenUnarchiving() ? resource : 0;
+}
+
+PassRefPtr<Archive> DocumentLoader::popArchiveForSubframe(const String& frameName)
+{
+ return m_archiveResourceCollection ? m_archiveResourceCollection->popSubframeArchive(frameName) : 0;
+}
+
+void DocumentLoader::clearArchiveResources()
+{
+ m_archiveResourceCollection.clear();
+ m_substituteResourceDeliveryTimer.stop();
+}
+
+void DocumentLoader::setParsedArchiveData(PassRefPtr<SharedBuffer> data)
+{
+ m_parsedArchiveData = data;
+}
+
+SharedBuffer* DocumentLoader::parsedArchiveData() const
+{
+ return m_parsedArchiveData.get();
+}
+
+PassRefPtr<ArchiveResource> DocumentLoader::mainResource() const
+{
+ const ResourceResponse& r = response();
+ RefPtr<SharedBuffer> mainResourceBuffer = mainResourceData();
+ if (!mainResourceBuffer)
+ mainResourceBuffer = SharedBuffer::create();
+
+ return ArchiveResource::create(mainResourceBuffer, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree()->uniqueName());
+}
+
+PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const
+{
+ if (!isCommitted())
+ return 0;
+
+ CachedResource* resource = m_frame->document()->cachedResourceLoader()->cachedResource(url);
+ if (!resource || !resource->isLoaded())
+ return archiveResourceForURL(url);
+
+ // FIXME: This has the side effect of making the resource non-purgeable.
+ // It would be better if it didn't have this permanent effect.
+ if (!resource->makePurgeable(false))
+ return 0;
+
+ RefPtr<SharedBuffer> data = resource->data();
+ if (!data)
+ return 0;
+
+ return ArchiveResource::create(data.release(), url, resource->response());
+}
+
+void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subresources) const
+{
+ if (!isCommitted())
+ return;
+
+ Document* document = m_frame->document();
+
+ 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());
+ }
+
+ return;
+}
+#endif
+
+void DocumentLoader::deliverSubstituteResourcesAfterDelay()
+{
+ if (m_pendingSubstituteResources.isEmpty())
+ return;
+ ASSERT(m_frame && m_frame->page());
+ if (m_frame->page()->defersLoading())
+ return;
+ if (!m_substituteResourceDeliveryTimer.isActive())
+ m_substituteResourceDeliveryTimer.startOneShot(0);
+}
+
+void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*)
+{
+ if (m_pendingSubstituteResources.isEmpty())
+ return;
+ ASSERT(m_frame && m_frame->page());
+ if (m_frame->page()->defersLoading())
+ return;
+
+ SubstituteResourceMap copy;
+ copy.swap(m_pendingSubstituteResources);
+
+ SubstituteResourceMap::const_iterator end = copy.end();
+ for (SubstituteResourceMap::const_iterator it = copy.begin(); it != end; ++it) {
+ RefPtr<ResourceLoader> loader = it->first;
+ SubstituteResource* resource = it->second.get();
+
+ if (resource) {
+ SharedBuffer* data = resource->data();
+
+ loader->didReceiveResponse(resource->response());
+ loader->didReceiveData(data->data(), data->size(), data->size(), true);
+ loader->didFinishLoading(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".
+ loader->didFail(loader->cannotShowURLError());
+ }
+ }
+}
+
+#ifndef NDEBUG
+bool DocumentLoader::isSubstituteLoadPending(ResourceLoader* loader) const
+{
+ return m_pendingSubstituteResources.contains(loader);
+}
+#endif
+
+void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader)
+{
+ if (m_pendingSubstituteResources.isEmpty())
+ return;
+ m_pendingSubstituteResources.remove(loader);
+ if (m_pendingSubstituteResources.isEmpty())
+ m_substituteResourceDeliveryTimer.stop();
+}
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+bool DocumentLoader::scheduleArchiveLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL)
+{
+ ArchiveResource* resource = 0;
+
+ if (request.url() == originalURL)
+ resource = archiveResourceForURL(originalURL);
+
+ if (!resource) {
+ // WebArchiveDebugMode means we fail loads instead of trying to fetch them from the network if they're not in the archive.
+ bool shouldFailLoad = m_frame->settings()->webArchiveDebugModeEnabled() && ArchiveFactory::isArchiveMimeType(responseMIMEType());
+
+ if (!shouldFailLoad)
+ return false;
+ }
+
+ m_pendingSubstituteResources.set(loader, resource);
+ deliverSubstituteResourcesAfterDelay();
+
+ return true;
+}
+#endif
+
+void DocumentLoader::addResponse(const ResourceResponse& r)
+{
+ if (!m_stopRecordingResponses)
+ m_responses.append(r);
+}
+
+void DocumentLoader::stopRecordingResponses()
+{
+ m_stopRecordingResponses = true;
+}
+
+void DocumentLoader::setTitle(const String& title)
+{
+ if (title.isEmpty())
+ return;
+
+ if (m_pageTitle != title) {
+ frameLoader()->willChangeTitle(this);
+ m_pageTitle = title;
+ frameLoader()->didChangeTitle(this);
+ }
+}
+
+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.
+ // Returns nil for WebDataProtocol URLs that aren't alternates
+ // for unreachable URLs, because these can't be stored in history.
+ if (m_substituteData.isValid())
+ return unreachableURL();
+
+ return m_originalRequestCopy.url();
+}
+
+bool DocumentLoader::urlForHistoryReflectsFailure() const
+{
+ return m_substituteData.isValid() || m_response.httpStatusCode() >= 400;
+}
+
+const KURL& DocumentLoader::originalURL() const
+{
+ return m_originalRequestCopy.url();
+}
+
+const KURL& DocumentLoader::requestURL() const
+{
+ return request().url();
+}
+
+const KURL& DocumentLoader::responseURL() const
+{
+ return m_response.url();
+}
+
+const String& DocumentLoader::responseMIMEType() const
+{
+ return m_response.mimeType();
+}
+
+const KURL& DocumentLoader::unreachableURL() const
+{
+ return m_substituteData.failingURL();
+}
+
+void DocumentLoader::setDefersLoading(bool defers)
+{
+ if (m_mainResourceLoader)
+ m_mainResourceLoader->setDefersLoading(defers);
+ setAllDefersLoading(m_subresourceLoaders, defers);
+ setAllDefersLoading(m_plugInStreamLoaders, defers);
+ if (!defers)
+ deliverSubstituteResourcesAfterDelay();
+}
+
+void DocumentLoader::stopLoadingPlugIns()
+{
+ cancelAll(m_plugInStreamLoaders);
+}
+
+void DocumentLoader::stopLoadingSubresources()
+{
+ cancelAll(m_subresourceLoaders);
+}
+
+void DocumentLoader::addSubresourceLoader(ResourceLoader* loader)
+{
+ m_subresourceLoaders.add(loader);
+ setLoading(true);
+}
+
+void DocumentLoader::removeSubresourceLoader(ResourceLoader* loader)
+{
+ m_subresourceLoaders.remove(loader);
+ updateLoading();
+ if (Frame* frame = m_frame)
+ frame->loader()->checkLoadComplete();
+}
+
+void DocumentLoader::addPlugInStreamLoader(ResourceLoader* loader)
+{
+ m_plugInStreamLoaders.add(loader);
+ setLoading(true);
+}
+
+void DocumentLoader::removePlugInStreamLoader(ResourceLoader* loader)
+{
+ m_plugInStreamLoaders.remove(loader);
+ updateLoading();
+}
+
+bool DocumentLoader::isLoadingMainResource() const
+{
+ return !!m_mainResourceLoader;
+}
+
+bool DocumentLoader::isLoadingSubresources() const
+{
+ return !m_subresourceLoaders.isEmpty();
+}
+
+bool DocumentLoader::isLoadingPlugIns() const
+{
+ return !m_plugInStreamLoaders.isEmpty();
+}
+
+bool DocumentLoader::isLoadingMultipartContent() const
+{
+ return m_mainResourceLoader && m_mainResourceLoader->isLoadingMultipartContent();
+}
+
+bool DocumentLoader::startLoadingMainResource(unsigned long identifier)
+{
+ ASSERT(!m_mainResourceLoader);
+ m_mainResourceLoader = MainResourceLoader::create(m_frame);
+ m_mainResourceLoader->setIdentifier(identifier);
+
+ // FIXME: Is there any way the extra fields could have not been added by now?
+ // If not, it would be great to remove this line of code.
+ frameLoader()->addExtraFieldsToMainResourceRequest(m_request);
+
+ if (!m_mainResourceLoader->load(m_request, m_substituteData)) {
+ // FIXME: If this should really be caught, we should just ASSERT this doesn't happen;
+ // should it be caught by other parts of WebKit or other parts of the app?
+ LOG_ERROR("could not create WebResourceHandle for URL %s -- should be caught by policy handler level", m_request.url().string().ascii().data());
+ m_mainResourceLoader = 0;
+ return false;
+ }
+
+ return true;
+}
+
+void DocumentLoader::cancelMainResourceLoad(const ResourceError& error)
+{
+ m_mainResourceLoader->cancel(error);
+}
+
+void DocumentLoader::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
+{
+ m_multipartSubresourceLoaders.add(loader);
+ m_subresourceLoaders.remove(loader);
+ updateLoading();
+ if (Frame* frame = m_frame)
+ frame->loader()->checkLoadComplete();
+}
+
+void DocumentLoader::transferLoadingResourcesFromPage(Page* oldPage)
+{
+ ASSERT(oldPage != m_frame->page());
+
+ FrameLoader* loader = frameLoader();
+ ASSERT(loader);
+
+ const ResourceRequest& request = originalRequest();
+ if (isLoadingMainResource()) {
+ loader->dispatchTransferLoadingResourceFromPage(
+ 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) {
+ loader->dispatchTransferLoadingResourceFromPage(
+ (*it)->identifier(), this, request, oldPage);
+ }
+ }
+}
+
+void DocumentLoader::iconLoadDecisionAvailable()
+{
+ if (m_frame)
+ m_frame->loader()->iconLoadDecisionAvailable();
+}
+
+}
diff --git a/Source/WebCore/loader/DocumentLoader.h b/Source/WebCore/loader/DocumentLoader.h
new file mode 100644
index 0000000..2328160
--- /dev/null
+++ b/Source/WebCore/loader/DocumentLoader.h
@@ -0,0 +1,338 @@
+/*
+ * 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.
+ * 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 DocumentLoader_h
+#define DocumentLoader_h
+
+#include "DocumentLoadTiming.h"
+#include "NavigationAction.h"
+#include "ResourceError.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "SubstituteData.h"
+#include "Timer.h"
+
+namespace WebCore {
+
+ class ApplicationCacheHost;
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ class Archive;
+ class ArchiveResource;
+ class ArchiveResourceCollection;
+#endif
+ class Frame;
+ class FrameLoader;
+ class MainResourceLoader;
+ class Page;
+ class ResourceLoader;
+ class SchedulePair;
+ class SharedBuffer;
+ class SubstituteResource;
+
+ typedef HashSet<RefPtr<ResourceLoader> > ResourceLoaderSet;
+ typedef Vector<ResourceResponse> ResponseVector;
+
+ class DocumentLoader : public RefCounted<DocumentLoader> {
+ public:
+ static PassRefPtr<DocumentLoader> create(const ResourceRequest& request, const SubstituteData& data)
+ {
+ return adoptRef(new DocumentLoader(request, data));
+ }
+ virtual ~DocumentLoader();
+
+ void setFrame(Frame*);
+ Frame* frame() const { return m_frame; }
+
+ virtual void attachToFrame();
+ virtual void detachFromFrame();
+
+ FrameLoader* frameLoader() const;
+ MainResourceLoader* mainResourceLoader() const { return m_mainResourceLoader.get(); }
+ PassRefPtr<SharedBuffer> mainResourceData() const;
+
+ const ResourceRequest& originalRequest() const;
+ const ResourceRequest& originalRequestCopy() const;
+
+ const ResourceRequest& request() const;
+ ResourceRequest& request();
+ void setRequest(const ResourceRequest&);
+
+ const SubstituteData& substituteData() const { return m_substituteData; }
+
+ const KURL& url() const;
+ const KURL& unreachableURL() const;
+
+ const KURL& originalURL() const;
+ const KURL& requestURL() const;
+ const KURL& responseURL() const;
+ const String& responseMIMEType() const;
+
+ void replaceRequestURLForSameDocumentNavigation(const KURL&);
+ bool isStopping() const { return m_isStopping; }
+ void stopLoading(DatabasePolicy = DatabasePolicyStop);
+ void setCommitted(bool committed) { m_committed = committed; }
+ bool isCommitted() const { return m_committed; }
+ bool isLoading() const { return m_loading; }
+ void setLoading(bool loading) { m_loading = loading; }
+ void updateLoading();
+ void receivedData(const char*, int);
+ void setupForReplaceByMIMEType(const String& newMIMEType);
+ void finishedLoading();
+ const ResourceResponse& response() const { return m_response; }
+ const ResourceError& mainDocumentError() const { return m_mainDocumentError; }
+ void mainReceivedError(const ResourceError&, bool isComplete);
+ void setResponse(const ResourceResponse& response) { m_response = response; }
+ 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)
+ void schedule(SchedulePair*);
+ void unschedule(SchedulePair*);
+#endif
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ void addAllArchiveResources(Archive*);
+ void addArchiveResource(PassRefPtr<ArchiveResource>);
+
+ // Return an ArchiveResource for the URL, either creating from live data or
+ // pulling from the ArchiveResourceCollection
+ PassRefPtr<ArchiveResource> subresource(const KURL&) const;
+ // Return the ArchiveResource for the URL only when loading an Archive
+ ArchiveResource* archiveResourceForURL(const KURL&) const;
+
+ PassRefPtr<Archive> popArchiveForSubframe(const String& frameName);
+ void clearArchiveResources();
+ void setParsedArchiveData(PassRefPtr<SharedBuffer>);
+ SharedBuffer* parsedArchiveData() const;
+
+ PassRefPtr<ArchiveResource> mainResource() const;
+ void getSubresources(Vector<PassRefPtr<ArchiveResource> >&) const;
+
+ bool scheduleArchiveLoad(ResourceLoader*, const ResourceRequest&, const KURL&);
+#endif
+#ifndef NDEBUG
+ bool isSubstituteLoadPending(ResourceLoader*) const;
+#endif
+ void cancelPendingSubstituteLoad(ResourceLoader*);
+
+ void addResponse(const ResourceResponse&);
+ const ResponseVector& responses() const { return m_responses; }
+
+ const NavigationAction& triggeringAction() const { return m_triggeringAction; }
+ void setTriggeringAction(const NavigationAction& action) { m_triggeringAction = action; }
+ void setOverrideEncoding(const String& encoding) { m_overrideEncoding = encoding; }
+ void setLastCheckedRequest(const ResourceRequest& request) { m_lastCheckedRequest = request; }
+ const ResourceRequest& lastCheckedRequest() { return m_lastCheckedRequest; }
+
+ void stopRecordingResponses();
+ const String& title() const { return m_pageTitle; }
+ const String& iconURL() const { return m_pageIconURL; }
+
+ KURL urlForHistory() const;
+ bool urlForHistoryReflectsFailure() const;
+
+ // These accessors accommodate WebCore's somewhat fickle custom of creating history
+ // items for redirects, but only sometimes. For "source" and "destination",
+ // these accessors return the URL that would have been used if a history
+ // item were created. This allows WebKit to link history items reflecting
+ // redirects into a chain from start to finish.
+ String clientRedirectSourceForHistory() const { return m_clientRedirectSourceForHistory; } // null if no client redirect occurred.
+ String clientRedirectDestinationForHistory() const { return urlForHistory(); }
+ void setClientRedirectSourceForHistory(const String& clientedirectSourceForHistory) { m_clientRedirectSourceForHistory = clientedirectSourceForHistory; }
+
+ String serverRedirectSourceForHistory() const { return urlForHistory() == url() ? String() : urlForHistory(); } // null if no server redirect occurred.
+ String serverRedirectDestinationForHistory() const { return url(); }
+
+ bool didCreateGlobalHistoryEntry() const { return m_didCreateGlobalHistoryEntry; }
+ void setDidCreateGlobalHistoryEntry(bool didCreateGlobalHistoryEntry) { m_didCreateGlobalHistoryEntry = didCreateGlobalHistoryEntry; }
+
+ void setDefersLoading(bool);
+
+ bool startLoadingMainResource(unsigned long identifier);
+ void cancelMainResourceLoad(const ResourceError&);
+
+ void iconLoadDecisionAvailable();
+
+ bool isLoadingMainResource() const;
+ bool isLoadingSubresources() const;
+ bool isLoadingPlugIns() const;
+ bool isLoadingMultipartContent() const;
+
+ void stopLoadingPlugIns();
+ void stopLoadingSubresources();
+
+ void addSubresourceLoader(ResourceLoader*);
+ void removeSubresourceLoader(ResourceLoader*);
+ void addPlugInStreamLoader(ResourceLoader*);
+ 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)
+ {
+ 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
+
+ protected:
+ DocumentLoader(const ResourceRequest&, const SubstituteData&);
+
+ bool m_deferMainResourceDataLoad;
+
+ private:
+ void setupForReplace();
+ void commitIfReady();
+ void clearErrors();
+ void setMainDocumentError(const ResourceError&);
+ void commitLoad(const char*, int);
+ bool doesProgressiveLoad(const String& MIMEType) const;
+
+ void deliverSubstituteResourcesAfterDelay();
+ void substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*);
+
+ Frame* m_frame;
+
+ RefPtr<MainResourceLoader> m_mainResourceLoader;
+ ResourceLoaderSet m_subresourceLoaders;
+ ResourceLoaderSet m_multipartSubresourceLoaders;
+ ResourceLoaderSet m_plugInStreamLoaders;
+
+ RefPtr<SharedBuffer> m_mainResourceData;
+
+ // A reference to actual request used to create the data source.
+ // This should only be used by the resourceLoadDelegate's
+ // identifierForInitialRequest:fromDatasource: method. It is
+ // not guaranteed to remain unchanged, as requests are mutable.
+ ResourceRequest m_originalRequest;
+
+ SubstituteData m_substituteData;
+
+ // A copy of the original request used to create the data source.
+ // We have to copy the request because requests are mutable.
+ ResourceRequest m_originalRequestCopy;
+
+ // The 'working' request. It may be mutated
+ // several times from the original request to include additional
+ // headers, cookie information, canonicalization and redirects.
+ ResourceRequest m_request;
+
+ ResourceResponse m_response;
+
+ ResourceError m_mainDocumentError;
+
+ bool m_committed;
+ bool m_isStopping;
+ bool m_loading;
+ bool m_gotFirstByte;
+ bool m_primaryLoadComplete;
+ bool m_isClientRedirect;
+ bool m_wasOnloadHandled;
+
+ String m_pageTitle;
+ String m_pageIconURL;
+
+ String m_overrideEncoding;
+
+ // The action that triggered loading - we keep this around for the
+ // benefit of the various policy handlers.
+ NavigationAction m_triggeringAction;
+
+ // The last request that we checked click policy for - kept around
+ // so we can avoid asking again needlessly.
+ ResourceRequest m_lastCheckedRequest;
+
+ // We retain all the received responses so we can play back the
+ // WebResourceLoadDelegate messages if the item is loaded from the
+ // page cache.
+ ResponseVector m_responses;
+ bool m_stopRecordingResponses;
+
+ typedef HashMap<RefPtr<ResourceLoader>, RefPtr<SubstituteResource> > SubstituteResourceMap;
+ SubstituteResourceMap m_pendingSubstituteResources;
+ Timer<DocumentLoader> m_substituteResourceDeliveryTimer;
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ OwnPtr<ArchiveResourceCollection> m_archiveResourceCollection;
+ RefPtr<SharedBuffer> m_parsedArchiveData;
+#endif
+
+ HashSet<String> m_resourcesClientKnowsAbout;
+ Vector<String> m_resourcesLoadedFromMemoryCacheForClientNotification;
+
+ 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;
+#endif
+ };
+
+ inline void DocumentLoader::recordMemoryCacheLoadForFutureClientNotification(const String& url)
+ {
+ m_resourcesLoadedFromMemoryCacheForClientNotification.append(url);
+ }
+
+ inline void DocumentLoader::takeMemoryCacheLoadsForClientNotification(Vector<String>& loadsSet)
+ {
+ loadsSet.swap(m_resourcesLoadedFromMemoryCacheForClientNotification);
+ m_resourcesLoadedFromMemoryCacheForClientNotification.clear();
+ }
+
+}
+
+#endif // DocumentLoader_h
diff --git a/Source/WebCore/loader/DocumentThreadableLoader.cpp b/Source/WebCore/loader/DocumentThreadableLoader.cpp
new file mode 100644
index 0000000..dee5001
--- /dev/null
+++ b/Source/WebCore/loader/DocumentThreadableLoader.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DocumentThreadableLoader.h"
+
+#include "AuthenticationChallenge.h"
+#include "CrossOriginAccessControl.h"
+#include "CrossOriginPreflightResultCache.h"
+#include "Document.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "ResourceHandle.h"
+#include "ResourceLoadScheduler.h"
+#include "ResourceRequest.h"
+#include "SecurityOrigin.h"
+#include "SubresourceLoader.h"
+#include "ThreadableLoaderClient.h"
+#include <wtf/UnusedParam.h>
+
+namespace WebCore {
+
+void DocumentThreadableLoader::loadResourceSynchronously(Document* document, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options)
+{
+ // The loader will be deleted as soon as this function exits.
+ RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, &client, LoadSynchronously, request, options));
+ ASSERT(loader->hasOneRef());
+}
+
+PassRefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document* document, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options)
+{
+ RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, LoadAsynchronously, request, options));
+ if (!loader->m_loader)
+ loader = 0;
+ return loader.release();
+}
+
+DocumentThreadableLoader::DocumentThreadableLoader(Document* document, ThreadableLoaderClient* client, BlockingBehavior blockingBehavior, const ResourceRequest& request, const ThreadableLoaderOptions& options)
+ : m_client(client)
+ , m_document(document)
+ , m_options(options)
+ , m_sameOriginRequest(document->securityOrigin()->canRequest(request.url()))
+ , m_async(blockingBehavior == LoadAsynchronously)
+{
+ ASSERT(document);
+ ASSERT(client);
+
+ if (m_sameOriginRequest || m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) {
+ loadRequest(request, DoSecurityCheck);
+ return;
+ }
+
+ if (m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) {
+ m_client->didFail(ResourceError(errorDomainWebKitInternal, 0, request.url().string(), "Cross origin requests are not supported."));
+ return;
+ }
+
+ ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
+
+ 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 = crossOriginRequest.release();
+
+ if (CrossOriginPreflightResultCache::shared().canSkipPreflight(document->securityOrigin()->toString(), m_actualRequest->url(), m_options.allowCredentials, m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields()))
+ preflightSuccess();
+ else
+ makeCrossOriginAccessRequestWithPreflight(*m_actualRequest);
+ }
+}
+
+void DocumentThreadableLoader::makeSimpleCrossOriginAccessRequest(const ResourceRequest& request)
+{
+ ASSERT(isSimpleCrossOriginAccessRequest(request.httpMethod(), request.httpHeaderFields()));
+
+ // 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(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.setHTTPOrigin(m_document->securityOrigin()->toString());
+
+ loadRequest(crossOriginRequest, DoSecurityCheck);
+}
+
+void DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight(const ResourceRequest& request)
+{
+ ResourceRequest preflightRequest(request.url());
+ preflightRequest.removeCredentials();
+ preflightRequest.setHTTPOrigin(m_document->securityOrigin()->toString());
+ preflightRequest.setAllowCookies(m_options.allowCredentials);
+ preflightRequest.setHTTPMethod("OPTIONS");
+ preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", request.httpMethod());
+
+ const HTTPHeaderMap& requestHeaderFields = request.httpHeaderFields();
+
+ if (requestHeaderFields.size() > 0) {
+ Vector<UChar> headerBuffer;
+ HTTPHeaderMap::const_iterator it = requestHeaderFields.begin();
+ append(headerBuffer, it->first);
+ ++it;
+
+ HTTPHeaderMap::const_iterator end = requestHeaderFields.end();
+ for (; it != end; ++it) {
+ headerBuffer.append(',');
+ headerBuffer.append(' ');
+ append(headerBuffer, it->first);
+ }
+
+ preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", String::adopt(headerBuffer));
+ }
+
+ loadRequest(preflightRequest, DoSecurityCheck);
+}
+
+DocumentThreadableLoader::~DocumentThreadableLoader()
+{
+ if (m_loader)
+ m_loader->clearClient();
+}
+
+void DocumentThreadableLoader::cancel()
+{
+ if (!m_loader)
+ return;
+
+ m_loader->cancel();
+ m_loader->clearClient();
+ m_loader = 0;
+ m_client = 0;
+}
+
+void DocumentThreadableLoader::willSendRequest(SubresourceLoader* loader, ResourceRequest& request, const ResourceResponse&)
+{
+ ASSERT(m_client);
+ ASSERT_UNUSED(loader, loader == m_loader);
+
+ if (!isAllowedRedirect(request.url())) {
+ RefPtr<DocumentThreadableLoader> protect(this);
+ m_client->didFailRedirectCheck();
+ request = ResourceRequest();
+ }
+}
+
+void DocumentThreadableLoader::didSendData(SubresourceLoader* loader, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+{
+ ASSERT(m_client);
+ ASSERT_UNUSED(loader, loader == m_loader);
+
+ m_client->didSendData(bytesSent, totalBytesToBeSent);
+}
+
+void DocumentThreadableLoader::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
+{
+ ASSERT(m_client);
+ ASSERT_UNUSED(loader, loader == m_loader);
+
+ String accessControlErrorDescription;
+ if (m_actualRequest) {
+ if (!passesAccessControlCheck(response, m_options.allowCredentials, m_document->securityOrigin(), accessControlErrorDescription)) {
+ preflightFailure(response.url(), accessControlErrorDescription);
+ return;
+ }
+
+ 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(), accessControlErrorDescription)) {
+ m_client->didFail(ResourceError(errorDomainWebKitInternal, 0, response.url().string(), accessControlErrorDescription));
+ return;
+ }
+ }
+
+ m_client->didReceiveResponse(response);
+ }
+}
+
+void DocumentThreadableLoader::didReceiveData(SubresourceLoader* loader, const char* data, int lengthReceived)
+{
+ ASSERT(m_client);
+ ASSERT_UNUSED(loader, loader == m_loader);
+
+ // Ignore response body of preflight requests.
+ if (m_actualRequest)
+ return;
+
+ m_client->didReceiveData(data, lengthReceived);
+}
+
+void DocumentThreadableLoader::didFinishLoading(SubresourceLoader* loader)
+{
+ ASSERT(loader == m_loader);
+ ASSERT(m_client);
+ didFinishLoading(loader->identifier());
+}
+
+void DocumentThreadableLoader::didFinishLoading(unsigned long identifier)
+{
+ if (m_actualRequest) {
+ ASSERT(!m_sameOriginRequest);
+ ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
+ preflightSuccess();
+ } else
+ m_client->didFinishLoading(identifier);
+}
+
+void DocumentThreadableLoader::didFail(SubresourceLoader* loader, const ResourceError& error)
+{
+ ASSERT(m_client);
+ // m_loader may be null if we arrive here via SubresourceLoader::create in the ctor
+ ASSERT_UNUSED(loader, loader == m_loader || !m_loader);
+
+ m_client->didFail(error);
+}
+
+bool DocumentThreadableLoader::getShouldUseCredentialStorage(SubresourceLoader* loader, bool& shouldUseCredentialStorage)
+{
+ ASSERT_UNUSED(loader, loader == m_loader || !m_loader);
+
+ if (!m_options.allowCredentials) {
+ shouldUseCredentialStorage = false;
+ return true;
+ }
+
+ return false; // Only FrameLoaderClient can ultimately permit credential use.
+}
+
+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
+ }
+}
+
+void DocumentThreadableLoader::receivedCancellation(SubresourceLoader* loader, const AuthenticationChallenge& challenge)
+{
+ ASSERT(m_client);
+ ASSERT_UNUSED(loader, loader == m_loader);
+ m_client->didReceiveAuthenticationCancellation(challenge.failureResponse());
+}
+
+void DocumentThreadableLoader::preflightSuccess()
+{
+ OwnPtr<ResourceRequest> actualRequest;
+ actualRequest.swap(m_actualRequest);
+
+ // It should be ok to skip the security check since we already asked about the preflight request.
+ loadRequest(*actualRequest, SkipSecurityCheck);
+}
+
+void DocumentThreadableLoader::preflightFailure(const String& url, const String& errorDescription)
+{
+ m_actualRequest = 0; // Prevent didFinishLoading() from bypassing access check.
+ 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;
+ bool sniffContent = m_options.sniffContent && !m_actualRequest;
+
+ // Clear the loader so that any callbacks from SubresourceLoader::create will not have the old loader.
+ m_loader = 0;
+ m_loader = resourceLoadScheduler()->scheduleSubresourceLoad(m_document->frame(), this, request, ResourceLoadPriorityMedium, securityCheck, sendLoadCallbacks, sniffContent);
+ return;
+ }
+
+ // FIXME: ThreadableLoaderOptions.sniffContent is not supported for synchronous requests.
+ StoredCredentials storedCredentials = m_options.allowCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials;
+
+ Vector<char> data;
+ ResourceError error;
+ ResourceResponse response;
+ unsigned long identifier = std::numeric_limits<unsigned long>::max();
+ if (m_document->frame())
+ identifier = m_document->frame()->loader()->loadResourceSynchronously(request, storedCredentials, error, response, data);
+
+ // No exception for file:/// resources, see <rdar://problem/4962298>.
+ // Also, if we have an HTTP response, then it wasn't a network error in fact.
+ if (!error.isNull() && !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. 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;
+ }
+
+ didReceiveResponse(0, response);
+
+ const char* bytes = static_cast<const char*>(data.data());
+ int len = static_cast<int>(data.size());
+ didReceiveData(0, bytes, len);
+
+ didFinishLoading(identifier);
+}
+
+bool DocumentThreadableLoader::isAllowedRedirect(const KURL& url)
+{
+ if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests)
+ return true;
+
+ // FIXME: We need to implement access control for each redirect. This will require some refactoring though, because the code
+ // that processes redirects doesn't know about access control and expects a synchronous answer from its client about whether
+ // a redirect should proceed.
+ return m_sameOriginRequest && m_document->securityOrigin()->canRequest(url);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/DocumentThreadableLoader.h b/Source/WebCore/loader/DocumentThreadableLoader.h
new file mode 100644
index 0000000..ebf3a25
--- /dev/null
+++ b/Source/WebCore/loader/DocumentThreadableLoader.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DocumentThreadableLoader_h
+#define DocumentThreadableLoader_h
+
+#include "FrameLoaderTypes.h"
+#include "SubresourceLoaderClient.h"
+#include "ThreadableLoader.h"
+#include <wtf/Forward.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+ class Document;
+ class KURL;
+ class ResourceRequest;
+ class ThreadableLoaderClient;
+
+ class DocumentThreadableLoader : public RefCounted<DocumentThreadableLoader>, public ThreadableLoader, private SubresourceLoaderClient {
+ public:
+ static void loadResourceSynchronously(Document*, const ResourceRequest&, ThreadableLoaderClient&, const ThreadableLoaderOptions&);
+ static PassRefPtr<DocumentThreadableLoader> create(Document*, ThreadableLoaderClient*, const ResourceRequest&, const ThreadableLoaderOptions&);
+ virtual ~DocumentThreadableLoader();
+
+ virtual void cancel();
+
+ using RefCounted<DocumentThreadableLoader>::ref;
+ using RefCounted<DocumentThreadableLoader>::deref;
+
+ protected:
+ virtual void refThreadableLoader() { ref(); }
+ virtual void derefThreadableLoader() { deref(); }
+
+ private:
+ enum BlockingBehavior {
+ LoadSynchronously,
+ LoadAsynchronously
+ };
+
+ DocumentThreadableLoader(Document*, ThreadableLoaderClient*, BlockingBehavior blockingBehavior, const ResourceRequest&, const ThreadableLoaderOptions& options);
+
+ virtual void willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual void didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
+
+ virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&);
+ virtual void didReceiveData(SubresourceLoader*, const char*, int lengthReceived);
+ virtual void didFinishLoading(SubresourceLoader*);
+ virtual void didFail(SubresourceLoader*, const ResourceError&);
+
+ virtual bool getShouldUseCredentialStorage(SubresourceLoader*, bool& shouldUseCredentialStorage);
+ virtual void didReceiveAuthenticationChallenge(SubresourceLoader*, const AuthenticationChallenge&);
+ virtual void receivedCancellation(SubresourceLoader*, const AuthenticationChallenge&);
+
+ void didFinishLoading(unsigned long identifier);
+ void makeSimpleCrossOriginAccessRequest(const ResourceRequest& request);
+ void makeCrossOriginAccessRequestWithPreflight(const ResourceRequest& request);
+ void preflightSuccess();
+ void preflightFailure(const String& url, const String& errorDescription);
+
+ void loadRequest(const ResourceRequest&, SecurityCheckPolicy);
+ bool isAllowedRedirect(const KURL&);
+
+ RefPtr<SubresourceLoader> m_loader;
+ ThreadableLoaderClient* m_client;
+ Document* m_document;
+ ThreadableLoaderOptions m_options;
+ bool m_sameOriginRequest;
+ bool m_async;
+ OwnPtr<ResourceRequest> m_actualRequest; // non-null during Access Control preflight checks
+ };
+
+} // namespace WebCore
+
+#endif // DocumentThreadableLoader_h
diff --git a/Source/WebCore/loader/DocumentWriter.cpp b/Source/WebCore/loader/DocumentWriter.cpp
new file mode 100644
index 0000000..5b03cd7
--- /dev/null
+++ b/Source/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/Source/WebCore/loader/DocumentWriter.h b/Source/WebCore/loader/DocumentWriter.h
new file mode 100644
index 0000000..5fb3dc1
--- /dev/null
+++ b/Source/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/Source/WebCore/loader/EmptyClients.h b/Source/WebCore/loader/EmptyClients.h
new file mode 100644
index 0000000..a9541b3
--- /dev/null
+++ b/Source/WebCore/loader/EmptyClients.h
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2006 Eric Seidel (eric@webkit.org)
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef EmptyClients_h
+#define EmptyClients_h
+
+#include "ChromeClient.h"
+#include "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 "FrameLoaderClient.h"
+#include "FrameNetworkingContext.h"
+#include "InspectorClient.h"
+#include "PluginHalterClient.h"
+#include "PopupMenu.h"
+#include "ResourceError.h"
+#include "SearchPopupMenu.h"
+
+/*
+ This file holds empty Client stubs for use by WebCore.
+ Viewless element needs to create a dummy Page->Frame->FrameView tree for use in parsing or executing JavaScript.
+ This tree depends heavily on Clients (usually provided by WebKit classes).
+
+ This file was first created for SVGImage as it had no way to access the current Page (nor should it,
+ since Images are not tied to a page).
+ See http://bugs.webkit.org/show_bug.cgi?id=5971 for the original discussion about this file.
+
+ Ideally, whenever you change a Client class, you should add a stub here.
+ Brittle, yes. Unfortunate, yes. Hopefully temporary.
+*/
+
+namespace WebCore {
+
+class 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() { }
+ virtual void chromeDestroyed() { }
+
+ virtual void setWindowRect(const FloatRect&) { }
+ virtual FloatRect windowRect() { return FloatRect(); }
+
+ virtual FloatRect pageRect() { return FloatRect(); }
+
+ virtual float scaleFactor() { return 1.f; }
+
+#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&, const NavigationAction&) { return 0; }
+ virtual void show() { }
+
+ virtual bool canRunModal() { return false; }
+ virtual void runModal() { }
+
+ virtual void setToolbarsVisible(bool) { }
+ virtual bool toolbarsVisible() { return false; }
+
+ virtual void setStatusbarVisible(bool) { }
+ virtual bool statusbarVisible() { return false; }
+
+ virtual void setScrollbarsVisible(bool) { }
+ virtual bool scrollbarsVisible() { return false; }
+
+ virtual void setMenubarVisible(bool) { }
+ virtual bool menubarVisible() { return false; }
+
+ virtual void setResizable(bool) { }
+
+ virtual void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String&, unsigned, const String&) { }
+
+ virtual bool canRunBeforeUnloadConfirmPanel() { return false; }
+ virtual bool runBeforeUnloadConfirmPanel(const String&, Frame*) { return true; }
+
+ virtual void closeWindowSoon() { }
+
+ virtual void runJavaScriptAlert(Frame*, const String&) { }
+ virtual bool runJavaScriptConfirm(Frame*, const String&) { return false; }
+ virtual bool runJavaScriptPrompt(Frame*, const String&, 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 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; }
+ virtual void contentsSizeChanged(Frame*, const IntSize&) const { }
+
+ virtual void scrollbarsModeDidChange() const { }
+ virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned) { }
+
+ virtual void setToolTip(const String&, TextDirection) { }
+
+ virtual void print(Frame*) { }
+
+#if ENABLE(DATABASE)
+ virtual void exceededDatabaseQuota(Frame*, const String&) { }
+#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t) { }
+ virtual void reachedApplicationCacheOriginQuota(SecurityOrigin*) { }
+#endif
+
+#if ENABLE(NOTIFICATIONS)
+ virtual NotificationPresenter* notificationPresenter() const { return 0; }
+#endif
+
+ virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) { }
+ virtual void chooseIconForFiles(const Vector<String>&, FileChooser*) { }
+
+ virtual void formStateDidChange(const Node*) { }
+
+ virtual void formDidFocus(const Node*) { }
+ virtual void formDidBlur(const Node*) { }
+
+ virtual PassOwnPtr<HTMLParserQuirks> createHTMLParserQuirks() { return 0; }
+
+ virtual void setCursor(const Cursor&) { }
+
+ virtual void scrollRectIntoView(const IntRect&, const ScrollView*) const {}
+
+ virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*) {}
+ virtual void cancelGeolocationPermissionRequestForFrame(Frame*, Geolocation*) {}
+
+#if USE(ACCELERATED_COMPOSITING)
+ 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
+};
+
+class EmptyFrameLoaderClient : public FrameLoaderClient, public Noncopyable {
+public:
+ virtual ~EmptyFrameLoaderClient() { }
+ virtual void frameLoaderDestroyed() { }
+
+ virtual bool hasWebView() const { return true; } // mainly for assertions
+
+ virtual void makeRepresentation(DocumentLoader*) { }
+ virtual void forceLayout() { }
+ virtual void forceLayoutForNonHTML() { }
+
+ virtual void setCopiesOnScroll() { }
+
+ virtual void detachedFromParent2() { }
+ virtual void detachedFromParent3() { }
+
+ virtual void download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&) { }
+
+ virtual void assignIdentifierToInitialRequest(unsigned long, DocumentLoader*, const ResourceRequest&) { }
+ virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long) { return false; }
+ virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long, ResourceRequest&, const ResourceResponse&) { }
+ virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) { }
+ virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) { }
+#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 dispatchDidHandleOnloadEvents() { }
+ virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() { }
+ virtual void dispatchDidCancelClientRedirect() { }
+ virtual void dispatchWillPerformClientRedirect(const KURL&, double, double) { }
+ virtual void dispatchDidChangeLocationWithinPage() { }
+ virtual void dispatchDidPushStateWithinPage() { }
+ virtual void dispatchDidReplaceStateWithinPage() { }
+ virtual void dispatchDidPopStateWithinPage() { }
+ virtual void dispatchWillClose() { }
+ 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&) { }
+ virtual void dispatchDidFinishDocumentLoad() { }
+ virtual void dispatchDidFinishLoad() { }
+ virtual void dispatchDidFirstLayout() { }
+ virtual void dispatchDidFirstVisuallyNonEmptyLayout() { }
+
+ virtual Frame* dispatchCreatePage(const NavigationAction&) { return 0; }
+ virtual void dispatchShow() { }
+
+ virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String&, const ResourceRequest&) { }
+ virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String&) { }
+ virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>) { }
+ virtual void cancelPolicyCheck() { }
+
+ virtual void dispatchUnableToImplementPolicy(const ResourceError&) { }
+
+ virtual void dispatchWillSendSubmitEvent(HTMLFormElement*) { }
+ virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>) { }
+
+ virtual void dispatchDidLoadMainResource(DocumentLoader*) { }
+ virtual void revertToProvisionalState(DocumentLoader*) { }
+ virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) { }
+
+ virtual void willChangeEstimatedProgress() { }
+ virtual void didChangeEstimatedProgress() { }
+ virtual void postProgressStartedNotification() { }
+ virtual void postProgressEstimateChangedNotification() { }
+ virtual void postProgressFinishedNotification() { }
+
+ virtual void setMainFrameDocumentReady(bool) { }
+
+ virtual void startDownload(const ResourceRequest&) { }
+
+ virtual void willChangeTitle(DocumentLoader*) { }
+ virtual void didChangeTitle(DocumentLoader*) { }
+
+ virtual void committedLoad(DocumentLoader*, const char*, int) { }
+ virtual void finishedLoading(DocumentLoader*) { }
+
+ virtual ResourceError cancelledError(const ResourceRequest&) { ResourceError error("", 0, "", ""); error.setIsCancellation(true); return error; }
+ virtual ResourceError blockedError(const ResourceRequest&) { return ResourceError("", 0, "", ""); }
+ virtual ResourceError cannotShowURLError(const ResourceRequest&) { return ResourceError("", 0, "", ""); }
+ virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&) { return ResourceError("", 0, "", ""); }
+
+ virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) { return ResourceError("", 0, "", ""); }
+ virtual ResourceError fileDoesNotExistError(const ResourceResponse&) { return ResourceError("", 0, "", ""); }
+ virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) { return ResourceError("", 0, "", ""); }
+
+ virtual bool shouldFallBack(const ResourceError&) { return false; }
+
+ 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 ""; }
+
+ virtual void frameLoadCompleted() { }
+ virtual void restoreViewState() { }
+ virtual void provisionalLoadStarted() { }
+ virtual bool shouldTreatURLAsSameAsCurrent(const KURL&) const { return false; }
+ virtual void didFinishLoad() { }
+ virtual void prepareForDataSourceReplacement() { }
+
+ virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest& request, const SubstituteData& substituteData) { return DocumentLoader::create(request, substituteData); }
+ virtual void setTitle(const String&, const KURL&) { }
+
+ virtual String userAgent(const KURL&) { return ""; }
+
+ virtual void savePlatformDataToCachedFrame(CachedFrame*) { }
+ virtual void transitionToCommittedFromCachedFrame(CachedFrame*) { }
+ virtual void transitionToCommittedForNewPage() { }
+
+ virtual void didSaveToPageCache() { }
+ virtual void didRestoreFromPageCache() { }
+
+ virtual void dispatchDidBecomeFrameset(bool) { }
+
+ virtual void updateGlobalHistory() { }
+ virtual void updateGlobalHistoryRedirectLinks() { }
+ virtual bool shouldGoToHistoryItem(HistoryItem*) const { return false; }
+ virtual void dispatchDidAddBackForwardItem(HistoryItem*) const { }
+ virtual void dispatchDidRemoveBackForwardItem(HistoryItem*) const { };
+ virtual void dispatchDidChangeBackForwardIndex() const { }
+ virtual void saveViewStateToItem(HistoryItem*) { }
+ virtual bool canCachePage() const { return false; }
+ 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(); }
+
+ virtual void redirectDataToPlugin(Widget*) { }
+ virtual void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*) { }
+ virtual void documentElementAvailable() { }
+ virtual void didPerformFirstNavigation() const { }
+
+#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)
+ virtual RemoteAXObjectRef accessibilityRemoteObject() { return 0; }
+ virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long, NSCachedURLResponse* response) const { return response; }
+#endif
+#if USE(CFNETWORK)
+ virtual bool shouldCacheResponse(DocumentLoader*, unsigned long, const ResourceResponse&, const unsigned char*, unsigned long long) { return true; }
+#endif
+
+ virtual PassRefPtr<FrameNetworkingContext> createNetworkingContext() { return PassRefPtr<FrameNetworkingContext>(); }
+};
+
+class EmptyEditorClient : public EditorClient, public Noncopyable {
+public:
+ virtual ~EmptyEditorClient() { }
+ virtual void pageDestroyed() { }
+
+ virtual bool shouldDeleteRange(Range*) { return false; }
+ virtual bool shouldShowDeleteInterface(HTMLElement*) { return false; }
+ virtual bool smartInsertDeleteEnabled() { return false; }
+ virtual bool isSelectTrailingWhitespaceEnabled() { return false; }
+ virtual bool isContinuousSpellCheckingEnabled() { return false; }
+ virtual void toggleContinuousSpellChecking() { }
+ virtual bool isGrammarCheckingEnabled() { return false; }
+ virtual void toggleGrammarChecking() { }
+ virtual int spellCheckerDocumentTag() { return -1; }
+
+ virtual bool selectWordBeforeMenuEvent() { return false; }
+ virtual bool isEditable() { return false; }
+
+ virtual bool shouldBeginEditing(Range*) { return false; }
+ virtual bool shouldEndEditing(Range*) { return false; }
+ virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction) { return false; }
+ // virtual bool shouldInsertNode(Node*, Range* replacingRange, WebViewInsertAction) { return false; }
+ virtual bool shouldInsertText(const String&, Range*, EditorInsertAction) { return false; }
+ virtual bool shouldChangeSelectedRange(Range*, Range*, EAffinity, bool) { return false; }
+
+ virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*) { return false; }
+ virtual bool shouldMoveRangeAfterDelete(Range*, Range*) { return false; }
+ // virtual bool shouldChangeTypingStyle(CSSStyleDeclaration* fromStyle, CSSStyleDeclaration* toStyle) { return false; }
+ // virtual bool doCommandBySelector(SEL selector) { return false; }
+ //
+ virtual void didBeginEditing() { }
+ virtual void respondToChangedContents() { }
+ virtual void respondToChangedSelection() { }
+ virtual void didEndEditing() { }
+ virtual void didWriteSelectionToPasteboard() { }
+ virtual void didSetSelectionTypesForPasteboard() { }
+ // virtual void webViewDidChangeTypingStyle:(NSNotification *)notification { }
+ // virtual void webViewDidChangeSelection:(NSNotification *)notification { }
+ // virtual NSUndoManager* undoManagerForWebView:(WebView *)webView { return 0; }
+
+ virtual void registerCommandForUndo(PassRefPtr<EditCommand>) { }
+ virtual void registerCommandForRedo(PassRefPtr<EditCommand>) { }
+ virtual void clearUndoRedoOperations() { }
+
+ virtual bool canUndo() const { return false; }
+ virtual bool canRedo() const { return false; }
+
+ virtual void undo() { }
+ virtual void redo() { }
+
+ virtual void handleKeyboardEvent(KeyboardEvent*) { }
+ virtual void handleInputMethodKeydown(KeyboardEvent*) { }
+
+ virtual void textFieldDidBeginEditing(Element*) { }
+ virtual void textFieldDidEndEditing(Element*) { }
+ virtual void textDidChangeInTextField(Element*) { }
+ virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*) { return false; }
+ virtual void textWillBeDeletedInTextField(Element*) { }
+ virtual void textDidChangeInTextArea(Element*) { }
+
+#if PLATFORM(MAC)
+ virtual void markedTextAbandoned(Frame*) { }
+
+ virtual NSString* userVisibleString(NSURL*) { return 0; }
+ virtual DocumentFragment* documentFragmentFromAttributedString(NSAttributedString*, Vector<RefPtr<ArchiveResource> >&) { return 0; };
+ virtual void setInsertionPasteboard(NSPasteboard*) { };
+#ifdef BUILDING_ON_TIGER
+ virtual NSArray* pasteboardTypesForSelection(Frame*) { return 0; }
+#endif
+#endif
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ virtual void uppercaseWord() { }
+ virtual void lowercaseWord() { }
+ virtual void capitalizeWord() { }
+ virtual void showSubstitutionsPanel(bool) { }
+ virtual bool substitutionsPanelIsShowing() { return false; }
+ virtual void toggleSmartInsertDelete() { }
+ virtual bool isAutomaticQuoteSubstitutionEnabled() { return false; }
+ virtual void toggleAutomaticQuoteSubstitution() { }
+ virtual bool isAutomaticLinkDetectionEnabled() { return false; }
+ virtual void toggleAutomaticLinkDetection() { }
+ virtual bool isAutomaticDashSubstitutionEnabled() { return false; }
+ virtual void toggleAutomaticDashSubstitution() { }
+ virtual bool isAutomaticTextReplacementEnabled() { return false; }
+ virtual void toggleAutomaticTextReplacement() { }
+ virtual bool isAutomaticSpellingCorrectionEnabled() { return false; }
+ virtual void toggleAutomaticSpellingCorrection() { }
+#endif
+ virtual void ignoreWordInSpellDocument(const String&) { }
+ virtual void learnWord(const String&) { }
+ virtual void checkSpellingOfString(const UChar*, int, int*, int*) { }
+ virtual String getAutoCorrectSuggestionForMisspelledWord(const String&) { return String(); }
+ virtual void checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*) { }
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ virtual void checkTextOfParagraph(const UChar*, int, uint64_t, Vector<TextCheckingResult>&) { };
+#endif
+#if SUPPORT_AUTOCORRECTION_PANEL
+ virtual void showCorrectionPanel(CorrectionPanelInfo::PanelType, const FloatRect&, const String&, const String&, const Vector<String>&, Editor*) { }
+ virtual void dismissCorrectionPanel(ReasonForDismissingCorrectionPanel) { }
+ 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&, const String&, Vector<String>&) { }
+ virtual void willSetInputMethodState() { }
+ virtual void setInputMethodState(bool) { }
+ virtual void requestCheckingOfString(SpellChecker*, int, const String&) { }
+};
+
+#if ENABLE(CONTEXT_MENUS)
+class EmptyContextMenuClient : public ContextMenuClient, public Noncopyable {
+public:
+ virtual ~EmptyContextMenuClient() { }
+ virtual void contextMenuDestroyed() { }
+
+#if USE(CROSS_PLATFORM_CONTEXT_MENUS)
+ virtual PassOwnPtr<ContextMenu> customizeMenu(PassOwnPtr<ContextMenu>) { return 0; }
+#else
+ virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*) { return 0; }
+#endif
+ virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) { }
+
+ virtual void downloadURL(const KURL&) { }
+ virtual void copyImageToClipboard(const HitTestResult&) { }
+ virtual void searchWithGoogle(const Frame*) { }
+ virtual void lookUpInDictionary(Frame*) { }
+ virtual bool isSpeaking() { return false; }
+ virtual void speak(const String&) { }
+ virtual void stopSpeaking() { }
+
+#if PLATFORM(MAC)
+ virtual void searchWithSpotlight() { }
+#endif
+};
+#endif // ENABLE(CONTEXT_MENUS)
+
+#if ENABLE(DRAG_SUPPORT)
+class EmptyDragClient : public DragClient, public Noncopyable {
+public:
+ virtual ~EmptyDragClient() {}
+ virtual void willPerformDragDestinationAction(DragDestinationAction, DragData*) { }
+ virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard*) { }
+ virtual DragDestinationAction actionMaskForDrag(DragData*) { return DragDestinationActionNone; }
+ virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint&) { return DragSourceActionNone; }
+ virtual void startDrag(DragImageRef, const IntPoint&, const IntPoint&, Clipboard*, Frame*, bool) { }
+ virtual DragImageRef createDragImageForLink(KURL&, const String&, Frame*) { return 0; }
+ virtual void dragControllerDestroyed() { }
+};
+#endif // ENABLE(DRAG_SUPPORT)
+
+class EmptyInspectorClient : public InspectorClient, public Noncopyable {
+public:
+ virtual ~EmptyInspectorClient() { }
+
+ virtual void inspectorDestroyed() { }
+
+ virtual void openInspectorFrontend(InspectorController*) { }
+
+ virtual void highlight(Node*) { }
+ virtual void hideHighlight() { }
+
+ virtual void populateSetting(const String&, String*) { }
+ virtual void storeSetting(const String&, const String&) { }
+ virtual bool sendMessageToFrontend(const String&) { return false; }
+};
+
+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/Source/WebCore/loader/FTPDirectoryParser.cpp b/Source/WebCore/loader/FTPDirectoryParser.cpp
new file mode 100644
index 0000000..6a05c4c
--- /dev/null
+++ b/Source/WebCore/loader/FTPDirectoryParser.cpp
@@ -0,0 +1,1715 @@
+/*
+ * Copyright (C) 2002 Cyrus Patel <cyp@fb14.uni-mainz.de>
+ * (C) 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License 2.1 as published by the Free Software Foundation.
+ *
+ * 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 was originally Mozilla code, titled ParseFTPList.cpp
+// Original version of this file can currently be found at: http://mxr.mozilla.org/mozilla1.8/source/netwerk/streamconv/converters/ParseFTPList.cpp
+
+#include "config.h"
+#if ENABLE(FTPDIR)
+#include "FTPDirectoryParser.h"
+
+#if PLATFORM(QT)
+#include <QDateTime>
+// On Windows, use the threadsafe *_r functions provided by pthread.
+#elif OS(WINDOWS) && (USE(PTHREADS) || HAVE(PTHREAD_H))
+#include <pthread.h>
+#endif
+
+#include <wtf/ASCIICType.h>
+#include <stdio.h>
+
+using namespace WTF;
+
+namespace WebCore {
+#if PLATFORM(QT) && defined(Q_WS_WIN32)
+
+// Replacement for gmtime_r() which is not available on MinGW.
+// We use this on Win32 Qt platform for portability.
+struct tm gmtimeQt(const QDateTime& input)
+{
+ tm result;
+
+ QDate date(input.date());
+ result.tm_year = date.year() - 1900;
+ result.tm_mon = date.month();
+ result.tm_mday = date.day();
+ result.tm_wday = date.dayOfWeek();
+ result.tm_yday = date.dayOfYear();
+
+ QTime time(input.time());
+ result.tm_sec = time.second();
+ result.tm_min = time.minute();
+ result.tm_hour = time.hour();
+
+ return result;
+}
+
+static struct tm *gmtimeQt(const time_t *const timep, struct tm *result)
+{
+ const QDateTime dt(QDateTime::fromTime_t(*timep));
+ *result = WebCore::gmtimeQt(dt);
+ return result;
+}
+
+#define gmtime_r(x, y) gmtimeQt(x, y)
+#elif OS(WINDOWS) && !defined(gmtime_r)
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define gmtime_r(x, y) gmtime_s((y), (x))
+#else /* !_MSC_VER */
+#define gmtime_r(x,y) (gmtime(x)?(*(y)=*gmtime(x),(y)):0)
+#endif
+#endif
+
+static inline FTPEntryType ParsingFailed(ListState& state)
+{
+ if (state.parsedOne || state.listStyle) /* junk if we fail to parse */
+ return FTPJunkEntry; /* this time but had previously parsed sucessfully */
+ return FTPMiscEntry; /* its part of a comment or error message */
+}
+
+FTPEntryType parseOneFTPLine(const char* line, ListState& state, ListResult& result)
+{
+ result.clear();
+
+ if (!line)
+ return FTPJunkEntry;
+
+ state.numLines++;
+
+ /* carry buffer is only valid from one line to the next */
+ unsigned int carry_buf_len = state.carryBufferLength;
+ state.carryBufferLength = 0;
+
+ unsigned linelen = 0;
+
+ /* strip leading whitespace */
+ while (*line == ' ' || *line == '\t')
+ line++;
+
+ /* line is terminated at first '\0' or '\n' */
+ const char* p = line;
+ while (*p && *p != '\n')
+ p++;
+ linelen = p - line;
+
+ if (linelen > 0 && *p == '\n' && *(p-1) == '\r')
+ linelen--;
+
+ /* DON'T strip trailing whitespace. */
+
+ if (linelen > 0)
+ {
+ static const char *month_names = "JanFebMarAprMayJunJulAugSepOctNovDec";
+ const char *tokens[16]; /* 16 is more than enough */
+ unsigned int toklen[WTF_ARRAY_LENGTH(tokens)];
+ unsigned int linelen_sans_wsp; // line length sans whitespace
+ unsigned int numtoks = 0;
+ unsigned int tokmarker = 0; /* extra info for lstyle handler */
+ unsigned int month_num = 0;
+ char tbuf[4];
+ int lstyle = 0;
+
+ if (carry_buf_len) /* VMS long filename carryover buffer */
+ {
+ tokens[0] = state.carryBuffer;
+ toklen[0] = carry_buf_len;
+ numtoks++;
+ }
+
+ unsigned int pos = 0;
+ while (pos < linelen && numtoks < WTF_ARRAY_LENGTH(tokens))
+ {
+ while (pos < linelen &&
+ (line[pos] == ' ' || line[pos] == '\t' || line[pos] == '\r'))
+ pos++;
+ if (pos < linelen)
+ {
+ tokens[numtoks] = &line[pos];
+ while (pos < linelen &&
+ (line[pos] != ' ' && line[pos] != '\t' && line[pos] != '\r'))
+ pos++;
+ if (tokens[numtoks] != &line[pos])
+ {
+ toklen[numtoks] = (&line[pos] - tokens[numtoks]);
+ numtoks++;
+ }
+ }
+ }
+
+ if (!numtoks)
+ return ParsingFailed(state);
+
+ linelen_sans_wsp = &(tokens[numtoks-1][toklen[numtoks-1]]) - tokens[0];
+ if (numtoks == WTF_ARRAY_LENGTH(tokens))
+ {
+ pos = linelen;
+ while (pos > 0 && (line[pos-1] == ' ' || line[pos-1] == '\t'))
+ pos--;
+ linelen_sans_wsp = pos;
+ }
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+#if defined(SUPPORT_EPLF)
+ /* EPLF handling must come somewhere before /bin/dls handling. */
+ if (!lstyle && (!state.listStyle || state.listStyle == 'E'))
+ {
+ if (*line == '+' && linelen > 4 && numtoks >= 2)
+ {
+ pos = 1;
+ while (pos < (linelen-1))
+ {
+ p = &line[pos++];
+ if (*p == '/')
+ result.type = FTPDirectoryEntry; /* its a dir */
+ else if (*p == 'r')
+ result.type = FTPFileEntry; /* its a file */
+ else if (*p == 'm')
+ {
+ if (isASCIIDigit(line[pos]))
+ {
+ while (pos < linelen && isASCIIDigit(line[pos]))
+ pos++;
+ 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;
+ }
+ }
+ }
+ else if (*p == 's')
+ {
+ if (isASCIIDigit(line[pos]))
+ {
+ while (pos < linelen && isASCIIDigit(line[pos]))
+ pos++;
+ if (pos < linelen && line[pos] == ',')
+ result.fileSize = String(p + 1, &line[pos] - p + 1);
+ }
+ }
+ else if (isASCIIAlpha(*p)) /* 'i'/'up' or unknown "fact" (property) */
+ {
+ while (pos < linelen && *++p != ',')
+ pos++;
+ }
+ else if (*p != '\t' || (p+1) != tokens[1])
+ {
+ break; /* its not EPLF after all */
+ }
+ else
+ {
+ state.parsedOne = true;
+ state.listStyle = lstyle = 'E';
+
+ p = &(line[linelen_sans_wsp]);
+ result.filename = tokens[1];
+ result.filenameLength = p - tokens[1];
+
+ if (!result.type) /* access denied */
+ {
+ result.type = FTPFileEntry; /* is assuming 'f'ile correct? */
+ return FTPJunkEntry; /* NO! junk it. */
+ }
+ return result.type;
+ }
+ if (pos >= (linelen-1) || line[pos] != ',')
+ break;
+ pos++;
+ } /* while (pos < linelen) */
+ result.clear();
+ } /* if (*line == '+' && linelen > 4 && numtoks >= 2) */
+ } /* if (!lstyle && (!state.listStyle || state.listStyle == 'E')) */
+#endif /* SUPPORT_EPLF */
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+
+#if defined(SUPPORT_VMS)
+ if (!lstyle && (!state.listStyle || state.listStyle == 'V'))
+ { /* try VMS Multinet/UCX/CMS server */
+ /*
+ * Legal characters in a VMS file/dir spec are [A-Z0-9$.-_~].
+ * '$' cannot begin a filename and `-' cannot be used as the first
+ * or last character. '.' is only valid as a directory separator
+ * and <file>.<type> separator. A canonical filename spec might look
+ * like this: DISK$VOL:[DIR1.DIR2.DIR3]FILE.TYPE;123
+ * All VMS FTP servers LIST in uppercase.
+ *
+ * We need to be picky about this in order to support
+ * multi-line listings correctly.
+ */
+ if (!state.parsedOne &&
+ (numtoks == 1 || (numtoks == 2 && toklen[0] == 9 &&
+ memcmp(tokens[0], "Directory", 9)==0 )))
+ {
+ /* If no dirstyle has been detected yet, and this line is a
+ * VMS list's dirname, then turn on VMS dirstyle.
+ * eg "ACA:[ANONYMOUS]", "DISK$FTP:[ANONYMOUS]", "SYS$ANONFTP:"
+ */
+ p = tokens[0];
+ pos = toklen[0];
+ if (numtoks == 2)
+ {
+ p = tokens[1];
+ pos = toklen[1];
+ }
+ pos--;
+ if (pos >= 3)
+ {
+ while (pos > 0 && p[pos] != '[')
+ {
+ pos--;
+ if (p[pos] == '-' || p[pos] == '$')
+ {
+ if (pos == 0 || p[pos-1] == '[' || p[pos-1] == '.' ||
+ (p[pos] == '-' && (p[pos+1] == ']' || p[pos+1] == '.')))
+ break;
+ }
+ else if (p[pos] != '.' && p[pos] != '~' &&
+ !isASCIIDigit(p[pos]) && !isASCIIAlpha(p[pos]))
+ break;
+ else if (isASCIIAlpha(p[pos]) && p[pos] != toASCIIUpper(p[pos]))
+ break;
+ }
+ if (pos > 0)
+ {
+ pos--;
+ if (p[pos] != ':' || p[pos+1] != '[')
+ pos = 0;
+ }
+ }
+ if (pos > 0 && p[pos] == ':')
+ {
+ while (pos > 0)
+ {
+ pos--;
+ if (p[pos] != '$' && p[pos] != '_' && p[pos] != '-' &&
+ p[pos] != '~' && !isASCIIDigit(p[pos]) && !isASCIIAlpha(p[pos]))
+ break;
+ else if (isASCIIAlpha(p[pos]) && p[pos] != toASCIIUpper(p[pos]))
+ break;
+ }
+ if (pos == 0)
+ {
+ state.listStyle = 'V';
+ return FTPJunkEntry; /* its junk */
+ }
+ }
+ /* fallthrough */
+ }
+ else if ((tokens[0][toklen[0]-1]) != ';')
+ {
+ if (numtoks == 1 && (state.listStyle == 'V' && !carry_buf_len))
+ lstyle = 'V';
+ else if (numtoks < 4)
+ ;
+ else if (toklen[1] >= 10 && memcmp(tokens[1], "%RMS-E-PRV", 10) == 0)
+ lstyle = 'V';
+ else if ((&line[linelen] - tokens[1]) >= 22 &&
+ memcmp(tokens[1], "insufficient privilege", 22) == 0)
+ lstyle = 'V';
+ else if (numtoks != 4 && numtoks != 6)
+ ;
+ else if (numtoks == 6 && (
+ toklen[5] < 4 || *tokens[5] != '(' || /* perms */
+ (tokens[5][toklen[5]-1]) != ')' ))
+ ;
+ else if ( (toklen[2] == 10 || toklen[2] == 11) &&
+ (tokens[2][toklen[2]-5]) == '-' &&
+ (tokens[2][toklen[2]-9]) == '-' &&
+ (((toklen[3]==4 || toklen[3]==5 || toklen[3]==7 || toklen[3]==8) &&
+ (tokens[3][toklen[3]-3]) == ':' ) ||
+ ((toklen[3]==10 || toklen[3]==11 ) &&
+ (tokens[3][toklen[3]-3]) == '.' )
+ ) && /* time in [H]H:MM[:SS[.CC]] format */
+ isASCIIDigit(*tokens[1]) && /* size */
+ isASCIIDigit(*tokens[2]) && /* date */
+ isASCIIDigit(*tokens[3]) /* time */
+ )
+ {
+ lstyle = 'V';
+ }
+ if (lstyle == 'V')
+ {
+ /*
+ * MultiNet FTP:
+ * LOGIN.COM;2 1 4-NOV-1994 04:09 [ANONYMOUS] (RWE,RWE,,)
+ * PUB.DIR;1 1 27-JAN-1994 14:46 [ANONYMOUS] (RWE,RWE,RE,RWE)
+ * README.FTP;1 %RMS-E-PRV, insufficient privilege or file protection violation
+ * ROUSSOS.DIR;1 1 27-JAN-1994 14:48 [CS,ROUSSOS] (RWE,RWE,RE,R)
+ * S67-50903.JPG;1 328 22-SEP-1998 16:19 [ANONYMOUS] (RWED,RWED,,)
+ * UCX FTP:
+ * CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)
+ * CMU/VMS-IP FTP
+ * [VMSSERV.FILES]ALARM.DIR;1 1/3 5-MAR-1993 18:09
+ * TCPware FTP
+ * FOO.BAR;1 4 5-MAR-1993 18:09:01.12
+ * Long filename example:
+ * THIS-IS-A-LONG-VMS-FILENAME.AND-THIS-IS-A-LONG-VMS-FILETYPE\r\n
+ * 213[/nnn] 29-JAN-1996 03:33[:nn] [ANONYMOU,ANONYMOUS] (RWED,RWED,,)
+ */
+ tokmarker = 0;
+ p = tokens[0];
+ pos = 0;
+ if (*p == '[' && toklen[0] >= 4) /* CMU style */
+ {
+ if (p[1] != ']')
+ {
+ p++;
+ pos++;
+ }
+ while (lstyle && pos < toklen[0] && *p != ']')
+ {
+ if (*p != '$' && *p != '.' && *p != '_' && *p != '-' &&
+ *p != '~' && !isASCIIDigit(*p) && !isASCIIAlpha(*p))
+ lstyle = 0;
+ pos++;
+ p++;
+ }
+ if (lstyle && pos < (toklen[0]-1))
+ {
+ /* ']' was found and there is at least one character after it */
+ ASSERT(*p == ']');
+ pos++;
+ p++;
+ tokmarker = pos; /* length of leading "[DIR1.DIR2.etc]" */
+ } else {
+ /* not a CMU style listing */
+ lstyle = 0;
+ }
+ }
+ while (lstyle && pos < toklen[0] && *p != ';')
+ {
+ if (*p != '$' && *p != '.' && *p != '_' && *p != '-' &&
+ *p != '~' && !isASCIIDigit(*p) && !isASCIIAlpha(*p))
+ lstyle = 0;
+ else if (isASCIIAlpha(*p) && *p != toASCIIUpper(*p))
+ lstyle = 0;
+ p++;
+ pos++;
+ }
+ if (lstyle && *p == ';')
+ {
+ if (pos == 0 || pos == (toklen[0]-1))
+ lstyle = 0;
+ for (pos++;lstyle && pos < toklen[0];pos++)
+ {
+ if (!isASCIIDigit(tokens[0][pos]))
+ lstyle = 0;
+ }
+ }
+ pos = (p - tokens[0]); /* => fnlength sans ";####" */
+ pos -= tokmarker; /* => fnlength sans "[DIR1.DIR2.etc]" */
+ p = &(tokens[0][tokmarker]); /* offset of basename */
+
+ if (!lstyle || pos == 0 || pos > 80) /* VMS filenames can't be longer than that */
+ {
+ lstyle = 0;
+ }
+ else if (numtoks == 1)
+ {
+ /* if VMS has been detected and there is only one token and that
+ * token was a VMS filename then this is a multiline VMS LIST entry.
+ */
+ if (pos >= (sizeof(state.carryBuffer)-1))
+ pos = (sizeof(state.carryBuffer)-1); /* shouldn't happen */
+ memcpy( state.carryBuffer, p, pos );
+ state.carryBufferLength = pos;
+ return FTPJunkEntry; /* tell caller to treat as junk */
+ }
+ else if (isASCIIDigit(*tokens[1])) /* not no-privs message */
+ {
+ for (pos = 0; lstyle && pos < (toklen[1]); pos++)
+ {
+ if (!isASCIIDigit((tokens[1][pos])) && (tokens[1][pos]) != '/')
+ lstyle = 0;
+ }
+ if (lstyle && numtoks > 4) /* Multinet or UCX but not CMU */
+ {
+ for (pos = 1; lstyle && pos < (toklen[5]-1); pos++)
+ {
+ p = &(tokens[5][pos]);
+ if (*p!='R' && *p!='W' && *p!='E' && *p!='D' && *p!=',')
+ lstyle = 0;
+ }
+ }
+ }
+ } /* passed initial tests */
+ } /* else if ((tokens[0][toklen[0]-1]) != ';') */
+
+ if (lstyle == 'V')
+ {
+ state.parsedOne = true;
+ state.listStyle = lstyle;
+
+ if (isASCIIDigit(*tokens[1])) /* not permission denied etc */
+ {
+ /* strip leading directory name */
+ if (*tokens[0] == '[') /* CMU server */
+ {
+ pos = toklen[0]-1;
+ p = tokens[0]+1;
+ while (*p != ']')
+ {
+ p++;
+ pos--;
+ }
+ toklen[0] = --pos;
+ tokens[0] = ++p;
+ }
+ pos = 0;
+ while (pos < toklen[0] && (tokens[0][pos]) != ';')
+ pos++;
+
+ result.caseSensitive = true;
+ result.type = FTPFileEntry;
+ result.filename = tokens[0];
+ result.filenameLength = pos;
+
+ if (pos > 4)
+ {
+ p = &(tokens[0][pos-4]);
+ if (p[0] == '.' && p[1] == 'D' && p[2] == 'I' && p[3] == 'R')
+ {
+ result.filenameLength -= 4;
+ result.type = FTPDirectoryEntry;
+ }
+ }
+
+ if (result.type != FTPDirectoryEntry)
+ {
+ /* #### or used/allocated form. If used/allocated form, then
+ * 'used' is the size in bytes if and only if 'used'<=allocated.
+ * If 'used' is size in bytes then it can be > 2^32
+ * If 'used' is not size in bytes then it is size in blocks.
+ */
+ pos = 0;
+ while (pos < toklen[1] && (tokens[1][pos]) != '/')
+ pos++;
+
+/*
+ * I've never seen size come back in bytes, its always in blocks, and
+ * the following test fails. So, always perform the "size in blocks".
+ * I'm leaving the "size in bytes" code if'd out in case we ever need
+ * to re-instate it.
+*/
+#if 0
+ if (pos < toklen[1] && ( (pos<<1) > (toklen[1]-1) ||
+ (strtoul(tokens[1], (char **)0, 10) >
+ strtoul(tokens[1]+pos+1, (char **)0, 10)) ))
+ { /* size is in bytes */
+ if (pos > (sizeof(result.fe_size)-1))
+ pos = sizeof(result.fe_size)-1;
+ memcpy( result.fe_size, tokens[1], pos );
+ result.fe_size[pos] = '\0';
+ }
+ else /* size is in blocks */
+#endif
+ {
+ /* size requires multiplication by blocksize.
+ *
+ * We could assume blocksize is 512 (like Lynx does) and
+ * shift by 9, but that might not be right. Even if it
+ * were, doing that wouldn't reflect what the file's
+ * real size was. The sanest thing to do is not use the
+ * LISTing's filesize, so we won't (like ftpmirror).
+ *
+ * ulltoa(((unsigned long long)fsz)<<9, result.fe_size, 10);
+ *
+ * A block is always 512 bytes on OpenVMS, compute size.
+ * So its rounded up to the next block, so what, its better
+ * than not showing the size at all.
+ * A block is always 512 bytes on OpenVMS, compute size.
+ * So its rounded up to the next block, so what, its better
+ * than not showing the size at all.
+ */
+ uint64_t size = strtoul(tokens[1], NULL, 10) * 512;
+ result.fileSize = String::number(size);
+ }
+
+ } /* if (result.type != FTPDirectoryEntry) */
+
+ p = tokens[2] + 2;
+ if (*p == '-')
+ p++;
+ tbuf[0] = p[0];
+ tbuf[1] = toASCIILower(p[1]);
+ tbuf[2] = toASCIILower(p[2]);
+ month_num = 0;
+ for (pos = 0; pos < (12*3); pos+=3)
+ {
+ if (tbuf[0] == month_names[pos+0] &&
+ tbuf[1] == month_names[pos+1] &&
+ tbuf[2] == month_names[pos+2])
+ break;
+ month_num++;
+ }
+ if (month_num >= 12)
+ month_num = 0;
+ result.modifiedTime.tm_mon = month_num;
+ result.modifiedTime.tm_mday = atoi(tokens[2]);
+ result.modifiedTime.tm_year = atoi(p+4); // NSPR wants year as XXXX
+
+ p = tokens[3] + 2;
+ if (*p == ':')
+ p++;
+ if (p[2] == ':')
+ result.modifiedTime.tm_sec = atoi(p+3);
+ result.modifiedTime.tm_hour = atoi(tokens[3]);
+ result.modifiedTime.tm_min = atoi(p);
+
+ return result.type;
+
+ } /* if (isASCIIDigit(*tokens[1])) */
+
+ return FTPJunkEntry; /* junk */
+
+ } /* if (lstyle == 'V') */
+ } /* if (!lstyle && (!state.listStyle || state.listStyle == 'V')) */
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+
+#if defined(SUPPORT_CMS)
+ /* Virtual Machine/Conversational Monitor System (IBM Mainframe) */
+ if (!lstyle && (!state.listStyle || state.listStyle == 'C')) /* VM/CMS */
+ {
+ /* LISTing according to mirror.pl
+ * Filename FileType Fm Format Lrecl Records Blocks Date Time
+ * LASTING GLOBALV A1 V 41 21 1 9/16/91 15:10:32
+ * J43401 NETLOG A0 V 77 1 1 9/12/91 12:36:04
+ * PROFILE EXEC A1 V 17 3 1 9/12/91 12:39:07
+ * DIRUNIX SCRIPT A1 V 77 1216 17 1/04/93 20:30:47
+ * MAIL PROFILE A2 F 80 1 1 10/14/92 16:12:27
+ * BADY2K TEXT A0 V 1 1 1 1/03/102 10:11:12
+ * AUTHORS A1 DIR - - - 9/20/99 10:31:11
+ *
+ * LISTing from vm.marist.edu and vm.sc.edu
+ * 220-FTPSERVE IBM VM Level 420 at VM.MARIST.EDU, 04:58:12 EDT WEDNESDAY 2002-07-10
+ * AUTHORS DIR - - - 1999-09-20 10:31:11 -
+ * HARRINGTON DIR - - - 1997-02-12 15:33:28 -
+ * PICS DIR - - - 2000-10-12 15:43:23 -
+ * SYSFILE DIR - - - 2000-07-20 17:48:01 -
+ * WELCNVT EXEC V 72 9 1 1999-09-20 17:16:18 -
+ * WELCOME EREADME F 80 21 1 1999-12-27 16:19:00 -
+ * WELCOME README V 82 21 1 1999-12-27 16:19:04 -
+ * README ANONYMOU V 71 26 1 1997-04-02 12:33:20 TCP291
+ * README ANONYOLD V 71 15 1 1995-08-25 16:04:27 TCP291
+ */
+ if (numtoks >= 7 && (toklen[0]+toklen[1]) <= 16)
+ {
+ for (pos = 1; !lstyle && (pos+5) < numtoks; pos++)
+ {
+ p = tokens[pos];
+ if ((toklen[pos] == 1 && (*p == 'F' || *p == 'V')) ||
+ (toklen[pos] == 3 && *p == 'D' && p[1] == 'I' && p[2] == 'R'))
+ {
+ if (toklen[pos+5] == 8 && (tokens[pos+5][2]) == ':' &&
+ (tokens[pos+5][5]) == ':' )
+ {
+ p = tokens[pos+4];
+ if ((toklen[pos+4] == 10 && p[4] == '-' && p[7] == '-') ||
+ (toklen[pos+4] >= 7 && toklen[pos+4] <= 9 &&
+ p[((p[1]!='/')?(2):(1))] == '/' &&
+ p[((p[1]!='/')?(5):(4))] == '/'))
+ /* Y2K bugs possible ("7/06/102" or "13/02/101") */
+ {
+ if ( (*tokens[pos+1] == '-' &&
+ *tokens[pos+2] == '-' &&
+ *tokens[pos+3] == '-') ||
+ (isASCIIDigit(*tokens[pos+1]) &&
+ isASCIIDigit(*tokens[pos+2]) &&
+ isASCIIDigit(*tokens[pos+3])) )
+ {
+ lstyle = 'C';
+ tokmarker = pos;
+ }
+ }
+ }
+ }
+ } /* for (pos = 1; !lstyle && (pos+5) < numtoks; pos++) */
+ } /* if (numtoks >= 7) */
+
+ /* extra checking if first pass */
+ if (lstyle && !state.listStyle)
+ {
+ for (pos = 0, p = tokens[0]; lstyle && pos < toklen[0]; pos++, p++)
+ {
+ if (isASCIIAlpha(*p) && toASCIIUpper(*p) != *p)
+ lstyle = 0;
+ }
+ for (pos = tokmarker+1; pos <= tokmarker+3; pos++)
+ {
+ if (!(toklen[pos] == 1 && *tokens[pos] == '-'))
+ {
+ for (p = tokens[pos]; lstyle && p<(tokens[pos]+toklen[pos]); p++)
+ {
+ if (!isASCIIDigit(*p))
+ lstyle = 0;
+ }
+ }
+ }
+ for (pos = 0, p = tokens[tokmarker+4];
+ lstyle && pos < toklen[tokmarker+4]; pos++, p++)
+ {
+ if (*p == '/')
+ {
+ /* There may be Y2K bugs in the date. Don't simplify to
+ * pos != (len-3) && pos != (len-6) like time is done.
+ */
+ if ((tokens[tokmarker+4][1]) == '/')
+ {
+ if (pos != 1 && pos != 4)
+ lstyle = 0;
+ }
+ else if (pos != 2 && pos != 5)
+ lstyle = 0;
+ }
+ else if (*p != '-' && !isASCIIDigit(*p))
+ lstyle = 0;
+ else if (*p == '-' && pos != 4 && pos != 7)
+ lstyle = 0;
+ }
+ for (pos = 0, p = tokens[tokmarker+5];
+ lstyle && pos < toklen[tokmarker+5]; pos++, p++)
+ {
+ if (*p != ':' && !isASCIIDigit(*p))
+ lstyle = 0;
+ else if (*p == ':' && pos != (toklen[tokmarker+5]-3)
+ && pos != (toklen[tokmarker+5]-6))
+ lstyle = 0;
+ }
+ } /* initial if() */
+
+ if (lstyle == 'C')
+ {
+ state.parsedOne = true;
+ state.listStyle = lstyle;
+
+ p = tokens[tokmarker+4];
+ if (toklen[tokmarker+4] == 10) /* newstyle: YYYY-MM-DD format */
+ {
+ result.modifiedTime.tm_year = atoi(p+0) - 1900;
+ result.modifiedTime.tm_mon = atoi(p+5) - 1;
+ result.modifiedTime.tm_mday = atoi(p+8);
+ }
+ else /* oldstyle: [M]M/DD/YY format */
+ {
+ pos = toklen[tokmarker+4];
+ result.modifiedTime.tm_mon = atoi(p) - 1;
+ result.modifiedTime.tm_mday = atoi((p+pos)-5);
+ result.modifiedTime.tm_year = atoi((p+pos)-2);
+ if (result.modifiedTime.tm_year < 70)
+ result.modifiedTime.tm_year += 100;
+ }
+
+ p = tokens[tokmarker+5];
+ pos = toklen[tokmarker+5];
+ result.modifiedTime.tm_hour = atoi(p);
+ result.modifiedTime.tm_min = atoi((p+pos)-5);
+ result.modifiedTime.tm_sec = atoi((p+pos)-2);
+
+ result.caseSensitive = true;
+ result.filename = tokens[0];
+ result.filenameLength = toklen[0];
+ result.type = FTPFileEntry;
+
+ p = tokens[tokmarker];
+ if (toklen[tokmarker] == 3 && *p=='D' && p[1]=='I' && p[2]=='R')
+ result.type = FTPDirectoryEntry;
+
+ if ((/*newstyle*/ toklen[tokmarker+4] == 10 && tokmarker > 1) ||
+ (/*oldstyle*/ toklen[tokmarker+4] != 10 && tokmarker > 2))
+ { /* have a filetype column */
+ char *dot;
+ p = &(tokens[0][toklen[0]]);
+ memcpy( &dot, &p, sizeof(dot) ); /* NASTY! */
+ *dot++ = '.';
+ p = tokens[1];
+ for (pos = 0; pos < toklen[1]; pos++)
+ *dot++ = *p++;
+ result.filenameLength += 1 + toklen[1];
+ }
+
+ /* oldstyle LISTING:
+ * files/dirs not on the 'A' minidisk are not RETRievable/CHDIRable
+ if (toklen[tokmarker+4] != 10 && *tokens[tokmarker-1] != 'A')
+ return FTPJunkEntry;
+ */
+
+ /* VM/CMS LISTings have no usable filesize field.
+ * Have to use the 'SIZE' command for that.
+ */
+ return result.type;
+
+ } /* if (lstyle == 'C' && (!state.listStyle || state.listStyle == lstyle)) */
+ } /* VM/CMS */
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+
+#if defined(SUPPORT_DOS) /* WinNT DOS dirstyle */
+ if (!lstyle && (!state.listStyle || state.listStyle == 'W'))
+ {
+ /*
+ * "10-23-00 01:27PM <DIR> veronist"
+ * "06-15-00 07:37AM <DIR> zoe"
+ * "07-14-00 01:35PM 2094926 canprankdesk.tif"
+ * "07-21-00 01:19PM 95077 Jon Kauffman Enjoys the Good Life.jpg"
+ * "07-21-00 01:19PM 52275 Name Plate.jpg"
+ * "07-14-00 01:38PM 2250540 Valentineoffprank-HiRes.jpg"
+ */
+ if ((numtoks >= 4) && toklen[0] == 8 && toklen[1] == 7 &&
+ (*tokens[2] == '<' || isASCIIDigit(*tokens[2])) )
+ {
+ p = tokens[0];
+ if ( isASCIIDigit(p[0]) && isASCIIDigit(p[1]) && p[2]=='-' &&
+ isASCIIDigit(p[3]) && isASCIIDigit(p[4]) && p[5]=='-' &&
+ isASCIIDigit(p[6]) && isASCIIDigit(p[7]) )
+ {
+ p = tokens[1];
+ if ( isASCIIDigit(p[0]) && isASCIIDigit(p[1]) && p[2]==':' &&
+ isASCIIDigit(p[3]) && isASCIIDigit(p[4]) &&
+ (p[5]=='A' || p[5]=='P') && p[6]=='M')
+ {
+ lstyle = 'W';
+ if (!state.listStyle)
+ {
+ p = tokens[2];
+ /* <DIR> or <JUNCTION> */
+ if (*p != '<' || p[toklen[2]-1] != '>')
+ {
+ for (pos = 1; (lstyle && pos < toklen[2]); pos++)
+ {
+ if (!isASCIIDigit(*++p))
+ lstyle = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (lstyle == 'W')
+ {
+ state.parsedOne = true;
+ state.listStyle = lstyle;
+
+ p = &(line[linelen]); /* line end */
+ result.caseSensitive = true;
+ result.filename = tokens[3];
+ result.filenameLength = p - tokens[3];
+ result.type = FTPDirectoryEntry;
+
+ if (*tokens[2] != '<') /* not <DIR> or <JUNCTION> */
+ {
+ // try to handle correctly spaces at the beginning of the filename
+ // filesize (token[2]) must end at offset 38
+ if (tokens[2] + toklen[2] - line == 38) {
+ result.filename = &(line[39]);
+ result.filenameLength = p - result.filename;
+ }
+ result.type = FTPFileEntry;
+ pos = toklen[2];
+ result.fileSize = String(tokens[2], pos);
+ }
+ else {
+ // try to handle correctly spaces at the beginning of the filename
+ // token[2] must begin at offset 24, the length is 5 or 10
+ // token[3] must begin at offset 39 or higher
+ if (tokens[2] - line == 24 && (toklen[2] == 5 || toklen[2] == 10) &&
+ tokens[3] - line >= 39) {
+ result.filename = &(line[39]);
+ result.filenameLength = p - result.filename;
+ }
+
+ if ((tokens[2][1]) != 'D') /* not <DIR> */
+ {
+ result.type = FTPJunkEntry; /* unknown until junc for sure */
+ if (result.filenameLength > 4)
+ {
+ p = result.filename;
+ for (pos = result.filenameLength - 4; pos > 0; pos--)
+ {
+ if (p[0] == ' ' && p[3] == ' ' && p[2] == '>' &&
+ (p[1] == '=' || p[1] == '-'))
+ {
+ result.type = FTPLinkEntry;
+ result.filenameLength = p - result.filename;
+ result.linkname = p + 4;
+ result.linknameLength = &(line[linelen])
+ - result.linkname;
+ break;
+ }
+ p++;
+ }
+ }
+ }
+ }
+
+ result.modifiedTime.tm_mon = atoi(tokens[0]+0);
+ if (result.modifiedTime.tm_mon != 0)
+ {
+ result.modifiedTime.tm_mon--;
+ result.modifiedTime.tm_mday = atoi(tokens[0]+3);
+ result.modifiedTime.tm_year = atoi(tokens[0]+6);
+ /* if year has only two digits then assume that
+ 00-79 is 2000-2079
+ 80-99 is 1980-1999 */
+ if (result.modifiedTime.tm_year < 80)
+ result.modifiedTime.tm_year += 2000;
+ else if (result.modifiedTime.tm_year < 100)
+ result.modifiedTime.tm_year += 1900;
+ }
+
+ result.modifiedTime.tm_hour = atoi(tokens[1]+0);
+ result.modifiedTime.tm_min = atoi(tokens[1]+3);
+ if ((tokens[1][5]) == 'P' && result.modifiedTime.tm_hour < 12)
+ result.modifiedTime.tm_hour += 12;
+
+ /* the caller should do this (if dropping "." and ".." is desired)
+ if (result.type == FTPDirectoryEntry && result.filename[0] == '.' &&
+ (result.filenameLength == 1 || (result.filenameLength == 2 &&
+ result.filename[1] == '.')))
+ return FTPJunkEntry;
+ */
+
+ return result.type;
+ } /* if (lstyle == 'W' && (!state.listStyle || state.listStyle == lstyle)) */
+ } /* if (!lstyle && (!state.listStyle || state.listStyle == 'W')) */
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+
+#if defined(SUPPORT_OS2)
+ if (!lstyle && (!state.listStyle || state.listStyle == 'O')) /* OS/2 test */
+ {
+ /* 220 server IBM TCP/IP for OS/2 - FTP Server ver 23:04:36 on Jan 15 1997 ready.
+ * fixed position, space padded columns. I have only a vague idea
+ * of what the contents between col 18 and 34 might be: All I can infer
+ * is that there may be attribute flags in there and there may be
+ * a " DIR" in there.
+ *
+ * 1 2 3 4 5 6
+ *0123456789012345678901234567890123456789012345678901234567890123456789
+ *----- size -------|??????????????? MM-DD-YY| HH:MM| nnnnnnnnn....
+ * 0 DIR 04-11-95 16:26 .
+ * 0 DIR 04-11-95 16:26 ..
+ * 0 DIR 04-11-95 16:26 ADDRESS
+ * 612 RHSA 07-28-95 16:45 air_tra1.bag
+ * 195 A 08-09-95 10:23 Alfa1.bag
+ * 0 RHS DIR 04-11-95 16:26 ATTACH
+ * 372 A 08-09-95 10:26 Aussie_1.bag
+ * 310992 06-28-94 09:56 INSTALL.EXE
+ * 1 2 3 4
+ * 01234567890123456789012345678901234567890123456789
+ * dirlist from the mirror.pl project, col positions from Mozilla.
+ */
+ p = &(line[toklen[0]]);
+ /* \s(\d\d-\d\d-\d\d)\s+(\d\d:\d\d)\s */
+ if (numtoks >= 4 && toklen[0] <= 18 && isASCIIDigit(*tokens[0]) &&
+ (linelen - toklen[0]) >= (53-18) &&
+ p[18-18] == ' ' && p[34-18] == ' ' &&
+ p[37-18] == '-' && p[40-18] == '-' && p[43-18] == ' ' &&
+ p[45-18] == ' ' && p[48-18] == ':' && p[51-18] == ' ' &&
+ isASCIIDigit(p[35-18]) && isASCIIDigit(p[36-18]) &&
+ isASCIIDigit(p[38-18]) && isASCIIDigit(p[39-18]) &&
+ isASCIIDigit(p[41-18]) && isASCIIDigit(p[42-18]) &&
+ isASCIIDigit(p[46-18]) && isASCIIDigit(p[47-18]) &&
+ isASCIIDigit(p[49-18]) && isASCIIDigit(p[50-18])
+ )
+ {
+ lstyle = 'O'; /* OS/2 */
+ if (!state.listStyle)
+ {
+ for (pos = 1; lstyle && pos < toklen[0]; pos++)
+ {
+ if (!isASCIIDigit(tokens[0][pos]))
+ lstyle = 0;
+ }
+ }
+ }
+
+ if (lstyle == 'O')
+ {
+ state.parsedOne = true;
+ state.listStyle = lstyle;
+
+ p = &(line[toklen[0]]);
+
+ result.caseSensitive = true;
+ result.filename = &p[53-18];
+ result.filenameLength = (&(line[linelen_sans_wsp]))
+ - (result.filename);
+ result.type = FTPFileEntry;
+
+ /* I don't have a real listing to determine exact pos, so scan. */
+ for (pos = (18-18); pos < ((35-18)-4); pos++)
+ {
+ if (p[pos+0] == ' ' && p[pos+1] == 'D' &&
+ p[pos+2] == 'I' && p[pos+3] == 'R')
+ {
+ result.type = FTPDirectoryEntry;
+ break;
+ }
+ }
+
+ if (result.type != FTPDirectoryEntry)
+ {
+ pos = toklen[0];
+ result.fileSize = String(tokens[0], pos);
+ }
+
+ result.modifiedTime.tm_mon = atoi(&p[35-18]) - 1;
+ result.modifiedTime.tm_mday = atoi(&p[38-18]);
+ result.modifiedTime.tm_year = atoi(&p[41-18]);
+ if (result.modifiedTime.tm_year < 80)
+ result.modifiedTime.tm_year += 100;
+ result.modifiedTime.tm_hour = atoi(&p[46-18]);
+ result.modifiedTime.tm_min = atoi(&p[49-18]);
+
+ /* the caller should do this (if dropping "." and ".." is desired)
+ if (result.type == FTPDirectoryEntry && result.filename[0] == '.' &&
+ (result.filenameLength == 1 || (result.filenameLength == 2 &&
+ result.filename[1] == '.')))
+ return FTPJunkEntry;
+ */
+
+ return result.type;
+ } /* if (lstyle == 'O') */
+
+ } /* if (!lstyle && (!state.listStyle || state.listStyle == 'O')) */
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+
+#if defined(SUPPORT_LSL)
+ if (!lstyle && (!state.listStyle || state.listStyle == 'U')) /* /bin/ls & co. */
+ {
+ /* UNIX-style listing, without inum and without blocks
+ * "-rw-r--r-- 1 root other 531 Jan 29 03:26 README"
+ * "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc"
+ * "dr-xr-xr-x 2 root 512 Apr 8 1994 etc"
+ * "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin"
+ * Also produced by Microsoft's FTP servers for Windows:
+ * "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z"
+ * "d--------- 1 owner group 0 May 9 19:45 Softlib"
+ * Also WFTPD for MSDOS:
+ * "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp"
+ * Hellsoft for NetWare:
+ * "d[RWCEMFA] supervisor 512 Jan 16 18:53 login"
+ * "-[RWCEMFA] rhesus 214059 Oct 20 15:27 cx.exe"
+ * Newer Hellsoft for NetWare: (netlab2.usu.edu)
+ * - [RWCEAFMS] NFAUUser 192 Apr 27 15:21 HEADER.html
+ * d [RWCEAFMS] jrd 512 Jul 11 03:01 allupdates
+ * Also NetPresenz for the Mac:
+ * "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit"
+ * "drwxrwxr-x folder 2 May 10 1996 network"
+ * Protected directory:
+ * "drwx-wx-wt 2 root wheel 512 Jul 1 02:15 incoming"
+ * uid/gid instead of username/groupname:
+ * "drwxr-xr-x 2 0 0 512 May 28 22:17 etc"
+ */
+
+ bool isOldHellsoft = false;
+
+ if (numtoks >= 6)
+ {
+ /* there are two perm formats (Hellsoft/NetWare and *IX strmode(3)).
+ * Scan for size column only if the perm format is one or the other.
+ */
+ if (toklen[0] == 1 || (tokens[0][1]) == '[')
+ {
+ if (*tokens[0] == 'd' || *tokens[0] == '-')
+ {
+ pos = toklen[0]-1;
+ p = tokens[0] + 1;
+ if (pos == 0)
+ {
+ p = tokens[1];
+ pos = toklen[1];
+ }
+ if ((pos == 9 || pos == 10) &&
+ (*p == '[' && p[pos-1] == ']') &&
+ (p[1] == 'R' || p[1] == '-') &&
+ (p[2] == 'W' || p[2] == '-') &&
+ (p[3] == 'C' || p[3] == '-') &&
+ (p[4] == 'E' || p[4] == '-'))
+ {
+ /* rest is FMA[S] or AFM[S] */
+ lstyle = 'U'; /* very likely one of the NetWare servers */
+ if (toklen[0] == 10)
+ isOldHellsoft = true;
+ }
+ }
+ }
+ else if ((toklen[0] == 10 || toklen[0] == 11)
+ && strchr("-bcdlpsw?DFam", *tokens[0]))
+ {
+ p = &(tokens[0][1]);
+ if ((p[0] == 'r' || p[0] == '-') &&
+ (p[1] == 'w' || p[1] == '-') &&
+ (p[3] == 'r' || p[3] == '-') &&
+ (p[4] == 'w' || p[4] == '-') &&
+ (p[6] == 'r' || p[6] == '-') &&
+ (p[7] == 'w' || p[7] == '-'))
+ /* 'x'/p[9] can be S|s|x|-|T|t or implementation specific */
+ {
+ lstyle = 'U'; /* very likely /bin/ls */
+ }
+ }
+ }
+ if (lstyle == 'U') /* first token checks out */
+ {
+ lstyle = 0;
+ for (pos = (numtoks-5); !lstyle && pos > 1; pos--)
+ {
+ /* scan for: (\d+)\s+([A-Z][a-z][a-z])\s+
+ * (\d\d\d\d|\d\:\d\d|\d\d\:\d\d|\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d)
+ * \s+(.+)$
+ */
+ if (isASCIIDigit(*tokens[pos]) /* size */
+ /* (\w\w\w) */
+ && toklen[pos+1] == 3 && isASCIIAlpha(*tokens[pos+1]) &&
+ isASCIIAlpha(tokens[pos+1][1]) && isASCIIAlpha(tokens[pos+1][2])
+ /* (\d|\d\d) */
+ && isASCIIDigit(*tokens[pos+2]) &&
+ (toklen[pos+2] == 1 ||
+ (toklen[pos+2] == 2 && isASCIIDigit(tokens[pos+2][1])))
+ && toklen[pos+3] >= 4 && isASCIIDigit(*tokens[pos+3])
+ /* (\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d) */
+ && (toklen[pos+3] <= 5 || (
+ (toklen[pos+3] == 7 || toklen[pos+3] == 8) &&
+ (tokens[pos+3][toklen[pos+3]-3]) == ':'))
+ && isASCIIDigit(tokens[pos+3][toklen[pos+3]-2])
+ && isASCIIDigit(tokens[pos+3][toklen[pos+3]-1])
+ && (
+ /* (\d\d\d\d) */
+ ((toklen[pos+3] == 4 || toklen[pos+3] == 5) &&
+ isASCIIDigit(tokens[pos+3][1]) &&
+ isASCIIDigit(tokens[pos+3][2]) )
+ /* (\d\:\d\d|\d\:\d\d\:\d\d) */
+ || ((toklen[pos+3] == 4 || toklen[pos+3] == 7) &&
+ (tokens[pos+3][1]) == ':' &&
+ isASCIIDigit(tokens[pos+3][2]) && isASCIIDigit(tokens[pos+3][3]))
+ /* (\d\d\:\d\d|\d\d\:\d\d\:\d\d) */
+ || ((toklen[pos+3] == 5 || toklen[pos+3] == 8) &&
+ isASCIIDigit(tokens[pos+3][1]) && (tokens[pos+3][2]) == ':' &&
+ isASCIIDigit(tokens[pos+3][3]) && isASCIIDigit(tokens[pos+3][4]))
+ )
+ )
+ {
+ lstyle = 'U'; /* assume /bin/ls or variant format */
+ tokmarker = pos;
+
+ /* check that size is numeric */
+ p = tokens[tokmarker];
+ for (unsigned int i = 0; lstyle && i < toklen[tokmarker]; ++i)
+ {
+ if (!isASCIIDigit(*p++))
+ lstyle = 0;
+ }
+ if (lstyle)
+ {
+ month_num = 0;
+ p = tokens[tokmarker+1];
+ for (unsigned int i = 0; i < (12*3); i+=3)
+ {
+ if (p[0] == month_names[i+0] &&
+ p[1] == month_names[i+1] &&
+ p[2] == month_names[i+2])
+ break;
+ month_num++;
+ }
+ if (month_num >= 12)
+ lstyle = 0;
+ }
+ } /* relative position test */
+ } /* for (pos = (numtoks-5); !lstyle && pos > 1; pos--) */
+ } /* if (lstyle == 'U') */
+
+ if (lstyle == 'U')
+ {
+ state.parsedOne = true;
+ state.listStyle = lstyle;
+
+ result.caseSensitive = false;
+ result.type = FTPJunkEntry;
+ if (*tokens[0] == 'd' || *tokens[0] == 'D')
+ result.type = FTPDirectoryEntry;
+ else if (*tokens[0] == 'l')
+ result.type = FTPLinkEntry;
+ else if (*tokens[0] == '-' || *tokens[0] == 'F')
+ result.type = FTPFileEntry; /* (hopefully a regular file) */
+
+ if (result.type != FTPDirectoryEntry)
+ {
+ pos = toklen[tokmarker];
+ result.fileSize = String(tokens[tokmarker], pos);
+ }
+
+ result.modifiedTime.tm_mon = month_num;
+ result.modifiedTime.tm_mday = atoi(tokens[tokmarker+2]);
+ if (result.modifiedTime.tm_mday == 0)
+ result.modifiedTime.tm_mday++;
+
+ p = tokens[tokmarker+3];
+ pos = (unsigned int)atoi(p);
+ if (p[1] == ':') /* one digit hour */
+ p--;
+ if (p[2] != ':') /* year */
+ {
+ result.modifiedTime.tm_year = pos;
+ }
+ else
+ {
+ result.modifiedTime.tm_hour = pos;
+ result.modifiedTime.tm_min = atoi(p+3);
+ if (p[5] == ':')
+ result.modifiedTime.tm_sec = atoi(p+6);
+
+ if (!state.now)
+ {
+ time_t now = time(NULL);
+ state.now = now * 1000000.0;
+
+ // FIXME: This code has the year 2038 bug
+ gmtime_r(&now, &state.nowFTPTime);
+ state.nowFTPTime.tm_year += 1900;
+ }
+
+ result.modifiedTime.tm_year = state.nowFTPTime.tm_year;
+ if ( (( state.nowFTPTime.tm_mon << 5) + state.nowFTPTime.tm_mday) <
+ ((result.modifiedTime.tm_mon << 5) + result.modifiedTime.tm_mday) )
+ result.modifiedTime.tm_year--;
+
+ } /* time/year */
+
+ // there is exactly 1 space between filename and previous token in all
+ // outputs except old Hellsoft
+ if (!isOldHellsoft)
+ result.filename = tokens[tokmarker+3] + toklen[tokmarker+3] + 1;
+ else
+ result.filename = tokens[tokmarker+4];
+
+ result.filenameLength = (&(line[linelen]))
+ - (result.filename);
+
+ if (result.type == FTPLinkEntry && result.filenameLength > 4)
+ {
+ /* First try to use result.fe_size to find " -> " sequence.
+ This can give proper result for cases like "aaa -> bbb -> ccc". */
+ unsigned int fileSize = result.fileSize.toUInt();
+
+ if (result.filenameLength > (fileSize + 4) &&
+ strncmp(result.filename + result.filenameLength - fileSize - 4, " -> ", 4) == 0)
+ {
+ result.linkname = result.filename + (result.filenameLength - fileSize);
+ result.linknameLength = (&(line[linelen])) - (result.linkname);
+ result.filenameLength -= fileSize + 4;
+ }
+ else
+ {
+ /* Search for sequence " -> " from the end for case when there are
+ more occurrences. F.e. if ftpd returns "a -> b -> c" assume
+ "a -> b" as a name. Powerusers can remove unnecessary parts
+ manually but there is no way to follow the link when some
+ essential part is missing. */
+ p = result.filename + (result.filenameLength - 5);
+ for (pos = (result.filenameLength - 5); pos > 0; pos--)
+ {
+ if (strncmp(p, " -> ", 4) == 0)
+ {
+ result.linkname = p + 4;
+ result.linknameLength = (&(line[linelen]))
+ - (result.linkname);
+ result.filenameLength = pos;
+ break;
+ }
+ p--;
+ }
+ }
+ }
+
+#if defined(SUPPORT_LSLF) /* some (very rare) servers return ls -lF */
+ if (result.filenameLength > 1)
+ {
+ p = result.filename[result.filenameLength-1];
+ pos = result.type;
+ if (pos == 'd') {
+ if (*p == '/') result.filenameLength--; /* directory */
+ } else if (pos == 'l') {
+ if (*p == '@') result.filenameLength--; /* symlink */
+ } else if (pos == 'f') {
+ if (*p == '*') result.filenameLength--; /* executable */
+ } else if (*p == '=' || *p == '%' || *p == '|') {
+ result.filenameLength--; /* socket, whiteout, fifo */
+ }
+ }
+#endif
+
+ /* the caller should do this (if dropping "." and ".." is desired)
+ if (result.type == FTPDirectoryEntry && result.filename[0] == '.' &&
+ (result.filenameLength == 1 || (result.filenameLength == 2 &&
+ result.filename[1] == '.')))
+ return FTPJunkEntry;
+ */
+
+ return result.type;
+
+ } /* if (lstyle == 'U') */
+
+ } /* if (!lstyle && (!state.listStyle || state.listStyle == 'U')) */
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+
+#if defined(SUPPORT_W16) /* 16bit Windows */
+ if (!lstyle && (!state.listStyle || state.listStyle == 'w'))
+ { /* old SuperTCP suite FTP server for Win3.1 */
+ /* old NetManage Chameleon TCP/IP suite FTP server for Win3.1 */
+ /*
+ * SuperTCP dirlist from the mirror.pl project
+ * mon/day/year separator may be '/' or '-'.
+ * . <DIR> 11-16-94 17:16
+ * .. <DIR> 11-16-94 17:16
+ * INSTALL <DIR> 11-16-94 17:17
+ * CMT <DIR> 11-21-94 10:17
+ * DESIGN1.DOC 11264 05-11-95 14:20
+ * README.TXT 1045 05-10-95 11:01
+ * WPKIT1.EXE 960338 06-21-95 17:01
+ * CMT.CSV 0 07-06-95 14:56
+ *
+ * Chameleon dirlist guessed from lynx
+ * . <DIR> Nov 16 1994 17:16
+ * .. <DIR> Nov 16 1994 17:16
+ * INSTALL <DIR> Nov 16 1994 17:17
+ * CMT <DIR> Nov 21 1994 10:17
+ * DESIGN1.DOC 11264 May 11 1995 14:20 A
+ * README.TXT 1045 May 10 1995 11:01
+ * WPKIT1.EXE 960338 Jun 21 1995 17:01 R
+ * CMT.CSV 0 Jul 06 1995 14:56 RHA
+ */
+ if (numtoks >= 4 && toklen[0] < 13 &&
+ ((toklen[1] == 5 && *tokens[1] == '<') || isASCIIDigit(*tokens[1])) )
+ {
+ if (numtoks == 4
+ && (toklen[2] == 8 || toklen[2] == 9)
+ && (((tokens[2][2]) == '/' && (tokens[2][5]) == '/') ||
+ ((tokens[2][2]) == '-' && (tokens[2][5]) == '-'))
+ && (toklen[3] == 4 || toklen[3] == 5)
+ && (tokens[3][toklen[3]-3]) == ':'
+ && isASCIIDigit(tokens[2][0]) && isASCIIDigit(tokens[2][1])
+ && isASCIIDigit(tokens[2][3]) && isASCIIDigit(tokens[2][4])
+ && isASCIIDigit(tokens[2][6]) && isASCIIDigit(tokens[2][7])
+ && (toklen[2] < 9 || isASCIIDigit(tokens[2][8]))
+ && isASCIIDigit(tokens[3][toklen[3]-1]) && isASCIIDigit(tokens[3][toklen[3]-2])
+ && isASCIIDigit(tokens[3][toklen[3]-4]) && isASCIIDigit(*tokens[3])
+ )
+ {
+ lstyle = 'w';
+ }
+ else if ((numtoks == 6 || numtoks == 7)
+ && toklen[2] == 3 && toklen[3] == 2
+ && toklen[4] == 4 && toklen[5] == 5
+ && (tokens[5][2]) == ':'
+ && isASCIIAlpha(tokens[2][0]) && isASCIIAlpha(tokens[2][1])
+ && isASCIIAlpha(tokens[2][2])
+ && isASCIIDigit(tokens[3][0]) && isASCIIDigit(tokens[3][1])
+ && isASCIIDigit(tokens[4][0]) && isASCIIDigit(tokens[4][1])
+ && isASCIIDigit(tokens[4][2]) && isASCIIDigit(tokens[4][3])
+ && isASCIIDigit(tokens[5][0]) && isASCIIDigit(tokens[5][1])
+ && isASCIIDigit(tokens[5][3]) && isASCIIDigit(tokens[5][4])
+ /* could also check that (&(tokens[5][5]) - tokens[2]) == 17 */
+ )
+ {
+ lstyle = 'w';
+ }
+ if (lstyle && state.listStyle != lstyle) /* first time */
+ {
+ p = tokens[1];
+ if (toklen[1] != 5 || p[0] != '<' || p[1] != 'D' ||
+ p[2] != 'I' || p[3] != 'R' || p[4] != '>')
+ {
+ for (pos = 0; lstyle && pos < toklen[1]; pos++)
+ {
+ if (!isASCIIDigit(*p++))
+ lstyle = 0;
+ }
+ } /* not <DIR> */
+ } /* if (first time) */
+ } /* if (numtoks == ...) */
+
+ if (lstyle == 'w')
+ {
+ state.parsedOne = true;
+ state.listStyle = lstyle;
+
+ result.caseSensitive = true;
+ result.filename = tokens[0];
+ result.filenameLength = toklen[0];
+ result.type = FTPDirectoryEntry;
+
+ p = tokens[1];
+ if (isASCIIDigit(*p))
+ {
+ result.type = FTPFileEntry;
+ pos = toklen[1];
+ result.fileSize = String(p, pos);
+ }
+
+ p = tokens[2];
+ if (toklen[2] == 3) /* Chameleon */
+ {
+ tbuf[0] = toASCIIUpper(p[0]);
+ tbuf[1] = toASCIILower(p[1]);
+ tbuf[2] = toASCIILower(p[2]);
+ for (pos = 0; pos < (12*3); pos+=3)
+ {
+ if (tbuf[0] == month_names[pos+0] &&
+ tbuf[1] == month_names[pos+1] &&
+ tbuf[2] == month_names[pos+2])
+ {
+ result.modifiedTime.tm_mon = pos/3;
+ result.modifiedTime.tm_mday = atoi(tokens[3]);
+ result.modifiedTime.tm_year = atoi(tokens[4]) - 1900;
+ break;
+ }
+ }
+ pos = 5; /* Chameleon toknum of date field */
+ }
+ else
+ {
+ result.modifiedTime.tm_mon = atoi(p+0)-1;
+ result.modifiedTime.tm_mday = atoi(p+3);
+ result.modifiedTime.tm_year = atoi(p+6);
+ if (result.modifiedTime.tm_year < 80) /* SuperTCP */
+ result.modifiedTime.tm_year += 100;
+
+ pos = 3; /* SuperTCP toknum of date field */
+ }
+
+ result.modifiedTime.tm_hour = atoi(tokens[pos]);
+ result.modifiedTime.tm_min = atoi(&(tokens[pos][toklen[pos]-2]));
+
+ /* the caller should do this (if dropping "." and ".." is desired)
+ if (result.type == FTPDirectoryEntry && result.filename[0] == '.' &&
+ (result.filenameLength == 1 || (result.filenameLength == 2 &&
+ result.filename[1] == '.')))
+ return FTPJunkEntry;
+ */
+
+ return result.type;
+ } /* (lstyle == 'w') */
+
+ } /* if (!lstyle && (!state.listStyle || state.listStyle == 'w')) */
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+
+#if defined(SUPPORT_DLS) /* dls -dtR */
+ if (!lstyle &&
+ (state.listStyle == 'D' || (!state.listStyle && state.numLines == 1)))
+ /* /bin/dls lines have to be immediately recognizable (first line) */
+ {
+ /* I haven't seen an FTP server that delivers a /bin/dls listing,
+ * but can infer the format from the lynx and mirror.pl projects.
+ * Both formats are supported.
+ *
+ * Lynx says:
+ * README 763 Information about this server\0
+ * bin/ - \0
+ * etc/ = \0
+ * ls-lR 0 \0
+ * ls-lR.Z 3 \0
+ * pub/ = Public area\0
+ * usr/ - \0
+ * morgan 14 -> ../real/morgan\0
+ * TIMIT.mostlikely.Z\0
+ * 79215 \0
+ *
+ * mirror.pl says:
+ * filename: ^(\S*)\s+
+ * size: (\-|\=|\d+)\s+
+ * month/day: ((\w\w\w\s+\d+|\d+\s+\w\w\w)\s+
+ * time/year: (\d+:\d+|\d\d\d\d))\s+
+ * rest: (.+)
+ *
+ * README 763 Jul 11 21:05 Information about this server
+ * bin/ - Apr 28 1994
+ * etc/ = 11 Jul 21:04
+ * ls-lR 0 6 Aug 17:14
+ * ls-lR.Z 3 05 Sep 1994
+ * pub/ = Jul 11 21:04 Public area
+ * usr/ - Sep 7 09:39
+ * morgan 14 Apr 18 09:39 -> ../real/morgan
+ * TIMIT.mostlikely.Z
+ * 79215 Jul 11 21:04
+ */
+ if (!state.listStyle && line[linelen-1] == ':' &&
+ linelen >= 2 && toklen[numtoks-1] != 1)
+ {
+ /* code in mirror.pl suggests that a listing may be preceded
+ * by a PWD line in the form "/some/dir/names/here:"
+ * but does not necessarily begin with '/'. *sigh*
+ */
+ pos = 0;
+ p = line;
+ while (pos < (linelen-1))
+ {
+ /* illegal (or extremely unusual) chars in a dirspec */
+ if (*p == '<' || *p == '|' || *p == '>' ||
+ *p == '?' || *p == '*' || *p == '\\')
+ break;
+ if (*p == '/' && pos < (linelen-2) && p[1] == '/')
+ break;
+ pos++;
+ p++;
+ }
+ if (pos == (linelen-1))
+ {
+ state.listStyle = 'D';
+ return FTPJunkEntry;
+ }
+ }
+
+ if (!lstyle && numtoks >= 2)
+ {
+ pos = 22; /* pos of (\d+|-|=) if this is not part of a multiline */
+ if (state.listStyle && carry_buf_len) /* first is from previous line */
+ pos = toklen[1]-1; /* and is 'as-is' (may contain whitespace) */
+
+ if (linelen > pos)
+ {
+ p = &line[pos];
+ if ((*p == '-' || *p == '=' || isASCIIDigit(*p)) &&
+ ((linelen == (pos+1)) ||
+ (linelen >= (pos+3) && p[1] == ' ' && p[2] == ' ')) )
+ {
+ tokmarker = 1;
+ if (!carry_buf_len)
+ {
+ pos = 1;
+ while (pos < numtoks && (tokens[pos]+toklen[pos]) < (&line[23]))
+ pos++;
+ tokmarker = 0;
+ if ((tokens[pos]+toklen[pos]) == (&line[23]))
+ tokmarker = pos;
+ }
+ if (tokmarker)
+ {
+ lstyle = 'D';
+ if (*tokens[tokmarker] == '-' || *tokens[tokmarker] == '=')
+ {
+ if (toklen[tokmarker] != 1 ||
+ (tokens[tokmarker-1][toklen[tokmarker-1]-1]) != '/')
+ lstyle = 0;
+ }
+ else
+ {
+ for (pos = 0; lstyle && pos < toklen[tokmarker]; pos++)
+ {
+ if (!isASCIIDigit(tokens[tokmarker][pos]))
+ lstyle = 0;
+ }
+ }
+ if (lstyle && !state.listStyle) /* first time */
+ {
+ /* scan for illegal (or incredibly unusual) chars in fname */
+ for (p = tokens[0]; lstyle &&
+ p < &(tokens[tokmarker-1][toklen[tokmarker-1]]); p++)
+ {
+ if (*p == '<' || *p == '|' || *p == '>' ||
+ *p == '?' || *p == '*' || *p == '/' || *p == '\\')
+ lstyle = 0;
+ }
+ }
+
+ } /* size token found */
+ } /* expected chars behind expected size token */
+ } /* if (linelen > pos) */
+ } /* if (!lstyle && numtoks >= 2) */
+
+ if (!lstyle && state.listStyle == 'D' && !carry_buf_len)
+ {
+ /* the filename of a multi-line entry can be identified
+ * correctly only if dls format had been previously established.
+ * This should always be true because there should be entries
+ * for '.' and/or '..' and/or CWD that precede the rest of the
+ * listing.
+ */
+ pos = linelen;
+ if (pos > (sizeof(state.carryBuffer)-1))
+ pos = sizeof(state.carryBuffer)-1;
+ memcpy( state.carryBuffer, line, pos );
+ state.carryBufferLength = pos;
+ return FTPJunkEntry;
+ }
+
+ if (lstyle == 'D')
+ {
+ state.parsedOne = true;
+ state.listStyle = lstyle;
+
+ p = &(tokens[tokmarker-1][toklen[tokmarker-1]]);
+ result.filename = tokens[0];
+ result.filenameLength = p - tokens[0];
+ result.type = FTPFileEntry;
+
+ if (result.filename[result.filenameLength-1] == '/')
+ {
+ if (result.linknameLength == 1)
+ result.type = FTPJunkEntry;
+ else
+ {
+ result.filenameLength--;
+ result.type = FTPDirectoryEntry;
+ }
+ }
+ else if (isASCIIDigit(*tokens[tokmarker]))
+ {
+ pos = toklen[tokmarker];
+ result.fileSize = String(tokens[tokmarker], pos);
+ }
+
+ if ((tokmarker+3) < numtoks &&
+ (&(tokens[numtoks-1][toklen[numtoks-1]]) -
+ tokens[tokmarker+1]) >= (1+1+3+1+4) )
+ {
+ pos = (tokmarker+3);
+ p = tokens[pos];
+ pos = toklen[pos];
+
+ if ((pos == 4 || pos == 5)
+ && isASCIIDigit(*p) && isASCIIDigit(p[pos-1]) && isASCIIDigit(p[pos-2])
+ && ((pos == 5 && p[2] == ':') ||
+ (pos == 4 && (isASCIIDigit(p[1]) || p[1] == ':')))
+ )
+ {
+ month_num = tokmarker+1; /* assumed position of month field */
+ pos = tokmarker+2; /* assumed position of mday field */
+ if (isASCIIDigit(*tokens[month_num])) /* positions are reversed */
+ {
+ month_num++;
+ pos--;
+ }
+ p = tokens[month_num];
+ if (isASCIIDigit(*tokens[pos])
+ && (toklen[pos] == 1 ||
+ (toklen[pos] == 2 && isASCIIDigit(tokens[pos][1])))
+ && toklen[month_num] == 3
+ && isASCIIAlpha(*p) && isASCIIAlpha(p[1]) && isASCIIAlpha(p[2]) )
+ {
+ pos = atoi(tokens[pos]);
+ if (pos > 0 && pos <= 31)
+ {
+ result.modifiedTime.tm_mday = pos;
+ month_num = 1;
+ for (pos = 0; pos < (12*3); pos+=3)
+ {
+ if (p[0] == month_names[pos+0] &&
+ p[1] == month_names[pos+1] &&
+ p[2] == month_names[pos+2])
+ break;
+ month_num++;
+ }
+ if (month_num > 12)
+ result.modifiedTime.tm_mday = 0;
+ else
+ result.modifiedTime.tm_mon = month_num - 1;
+ }
+ }
+ if (result.modifiedTime.tm_mday)
+ {
+ tokmarker += 3; /* skip mday/mon/yrtime (to find " -> ") */
+ p = tokens[tokmarker];
+
+ pos = atoi(p);
+ if (pos > 24)
+ result.modifiedTime.tm_year = pos-1900;
+ else
+ {
+ if (p[1] == ':')
+ p--;
+ result.modifiedTime.tm_hour = pos;
+ result.modifiedTime.tm_min = atoi(p+3);
+ if (!state.now)
+ {
+ time_t now = time(NULL);
+ state.now = now * 1000000.0;
+
+ // FIXME: This code has the year 2038 bug
+ gmtime_r(&now, &state.nowFTPTime);
+ state.nowFTPTime.tm_year += 1900;
+ }
+ result.modifiedTime.tm_year = state.nowFTPTime.tm_year;
+ if ( (( state.nowFTPTime.tm_mon << 4) + state.nowFTPTime.tm_mday) <
+ ((result.modifiedTime.tm_mon << 4) + result.modifiedTime.tm_mday) )
+ result.modifiedTime.tm_year--;
+ } /* got year or time */
+ } /* got month/mday */
+ } /* may have year or time */
+ } /* enough remaining to possibly have date/time */
+
+ if (numtoks > (tokmarker+2))
+ {
+ pos = tokmarker+1;
+ p = tokens[pos];
+ if (toklen[pos] == 2 && *p == '-' && p[1] == '>')
+ {
+ p = &(tokens[numtoks-1][toklen[numtoks-1]]);
+ result.type = FTPLinkEntry;
+ result.linkname = tokens[pos+1];
+ result.linknameLength = p - result.linkname;
+ if (result.linknameLength > 1 &&
+ result.linkname[result.linknameLength-1] == '/')
+ result.linknameLength--;
+ }
+ } /* if (numtoks > (tokmarker+2)) */
+
+ /* the caller should do this (if dropping "." and ".." is desired)
+ if (result.type == FTPDirectoryEntry && result.filename[0] == '.' &&
+ (result.filenameLength == 1 || (result.filenameLength == 2 &&
+ result.filename[1] == '.')))
+ return FTPJunkEntry;
+ */
+
+ return result.type;
+
+ } /* if (lstyle == 'D') */
+ } /* if (!lstyle && (!state.listStyle || state.listStyle == 'D')) */
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+
+ } /* if (linelen > 0) */
+
+ return ParsingFailed(state);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(FTPDIR)
diff --git a/Source/WebCore/loader/FTPDirectoryParser.h b/Source/WebCore/loader/FTPDirectoryParser.h
new file mode 100644
index 0000000..023a895
--- /dev/null
+++ b/Source/WebCore/loader/FTPDirectoryParser.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2002 Cyrus Patel <cyp@fb14.uni-mainz.de>
+ * (C) 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License 2.1 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/* ParseFTPList() parses lines from an FTP LIST command.
+**
+** Written July 2002 by Cyrus Patel <cyp@fb14.uni-mainz.de>
+** with acknowledgements to squid, lynx, wget and ftpmirror.
+**
+** Arguments:
+** 'line': line of FTP data connection output. The line is assumed
+** to end at the first '\0' or '\n' or '\r\n'.
+** 'state': a structure used internally to track state between
+** lines. Needs to be bzero()'d at LIST begin.
+** 'result': where ParseFTPList will store the results of the parse
+** if 'line' is not a comment and is not junk.
+**
+** Returns one of the following:
+** 'd' - LIST line is a directory entry ('result' is valid)
+** 'f' - LIST line is a file's entry ('result' is valid)
+** 'l' - LIST line is a symlink's entry ('result' is valid)
+** '?' - LIST line is junk. (cwd, non-file/dir/link, etc)
+** '"' - its not a LIST line (its a "comment")
+**
+** It may be advisable to let the end-user see "comments" (particularly when
+** the listing results in ONLY such lines) because such a listing may be:
+** - an unknown LIST format (NLST or "custom" format for example)
+** - an error msg (EPERM,ENOENT,ENFILE,EMFILE,ENOTDIR,ENOTBLK,EEXDEV etc).
+** - an empty directory and the 'comment' is a "total 0" line or similar.
+** (warning: a "total 0" can also mean the total size is unknown).
+**
+** ParseFTPList() supports all known FTP LISTing formats:
+** - '/bin/ls -l' and all variants (including Hellsoft FTP for NetWare);
+** - EPLF (Easily Parsable List Format);
+** - Windows NT's default "DOS-dirstyle";
+** - OS/2 basic server format LIST format;
+** - VMS (MultiNet, UCX, and CMU) LIST format (including multi-line format);
+** - IBM VM/CMS, VM/ESA LIST format (two known variants);
+** - SuperTCP FTP Server for Win16 LIST format;
+** - NetManage Chameleon (NEWT) for Win16 LIST format;
+** - '/bin/dls' (two known variants, plus multi-line) LIST format;
+** If there are others, then I'd like to hear about them (send me a sample).
+**
+** NLSTings are not supported explicitely because they cannot be machine
+** parsed consistantly: NLSTings do not have unique characteristics - even
+** the assumption that there won't be whitespace on the line does not hold
+** because some nlistings have more than one filename per line and/or
+** may have filenames that have spaces in them. Moreover, distinguishing
+** between an error message and an NLST line would require ParseList() to
+** recognize all the possible strerror() messages in the world.
+*/
+
+// This was originally Mozilla code, titled ParseFTPList.h
+// Original version of this file can currently be found at: http://mxr.mozilla.org/mozilla1.8/source/netwerk/streamconv/converters/ParseFTPList.h
+
+#ifndef FTPDirectoryParser_h
+#define FTPDirectoryParser_h
+
+#include "PlatformString.h"
+
+#include <time.h>
+
+#define SUPPORT_LSL /* Support for /bin/ls -l and dozens of variations therof */
+#define SUPPORT_DLS /* Support for /bin/dls format (very, Very, VERY rare) */
+#define SUPPORT_EPLF /* Support for Extraordinarily Pathetic List Format */
+#define SUPPORT_DOS /* Support for WinNT server in 'site dirstyle' dos */
+#define SUPPORT_VMS /* Support for VMS (all: MultiNet, UCX, CMU-IP) */
+#define SUPPORT_CMS /* Support for IBM VM/CMS,VM/ESA (z/VM and LISTING forms) */
+#define SUPPORT_OS2 /* Support for IBM TCP/IP for OS/2 - FTP Server */
+#define SUPPORT_W16 /* Support for win16 hosts: SuperTCP or NetManage Chameleon */
+
+namespace WebCore {
+
+typedef struct tm FTPTime;
+
+struct ListState {
+ ListState()
+ : now(0)
+ , listStyle(0)
+ , parsedOne(false)
+ , carryBufferLength(0)
+ , numLines(0)
+ {
+ memset(&nowFTPTime, 0, sizeof(FTPTime));
+ }
+
+ double now; /* needed for year determination */
+ FTPTime nowFTPTime;
+ char listStyle; /* LISTing style */
+ bool parsedOne; /* returned anything yet? */
+ char carryBuffer[84]; /* for VMS multiline */
+ int carryBufferLength; /* length of name in carry_buf */
+ int64_t numLines; /* number of lines seen */
+};
+
+enum FTPEntryType {
+ FTPDirectoryEntry,
+ FTPFileEntry,
+ FTPLinkEntry,
+ FTPMiscEntry,
+ FTPJunkEntry
+};
+
+struct ListResult
+{
+ ListResult()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ valid = false;
+ type = FTPJunkEntry;
+ filename = 0;
+ filenameLength = 0;
+ linkname = 0;
+ linknameLength = 0;
+ fileSize.truncate(0);
+ caseSensitive = false;
+ memset(&modifiedTime, 0, sizeof(FTPTime));
+ }
+
+ bool valid;
+ FTPEntryType type;
+
+ const char* filename;
+ uint32_t filenameLength;
+
+ const char* linkname;
+ uint32_t linknameLength;
+
+ String fileSize;
+ FTPTime modifiedTime;
+ bool caseSensitive; // file system is definitely case insensitive
+};
+
+FTPEntryType parseOneFTPLine(const char* inputLine, ListState&, ListResult&);
+
+} // namespace WebCore
+
+#endif // FTPDirectoryParser_h
diff --git a/Source/WebCore/loader/FormState.cpp b/Source/WebCore/loader/FormState.cpp
new file mode 100644
index 0000000..552789a
--- /dev/null
+++ b/Source/WebCore/loader/FormState.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2006, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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 "FormState.h"
+
+#include "Frame.h"
+#include "HTMLFormElement.h"
+
+namespace WebCore {
+
+inline FormState::FormState(PassRefPtr<HTMLFormElement> form, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Frame> sourceFrame, FormSubmissionTrigger formSubmissionTrigger)
+ : m_form(form)
+ , m_sourceFrame(sourceFrame)
+ , m_formSubmissionTrigger(formSubmissionTrigger)
+{
+ m_textFieldValues.swap(textFieldValuesToAdopt);
+}
+
+PassRefPtr<FormState> FormState::create(PassRefPtr<HTMLFormElement> form, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Frame> sourceFrame, FormSubmissionTrigger formSubmissionTrigger)
+{
+ return adoptRef(new FormState(form, textFieldValuesToAdopt, sourceFrame, formSubmissionTrigger));
+}
+
+}
diff --git a/Source/WebCore/loader/FormState.h b/Source/WebCore/loader/FormState.h
new file mode 100644
index 0000000..8f7166e
--- /dev/null
+++ b/Source/WebCore/loader/FormState.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2006, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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 FormState_h
+#define FormState_h
+
+#include "PlatformString.h"
+
+namespace WebCore {
+
+ class Frame;
+ class HTMLFormElement;
+
+ enum FormSubmissionTrigger {
+ SubmittedByJavaScript,
+ NotSubmittedByJavaScript
+ };
+
+ typedef Vector<std::pair<String, String> > StringPairVector;
+
+ class FormState : public RefCounted<FormState> {
+ public:
+ static PassRefPtr<FormState> create(PassRefPtr<HTMLFormElement>, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Frame>, FormSubmissionTrigger);
+
+ HTMLFormElement* form() const { return m_form.get(); }
+ const StringPairVector& textFieldValues() const { return m_textFieldValues; }
+ Frame* sourceFrame() const { return m_sourceFrame.get(); }
+ FormSubmissionTrigger formSubmissionTrigger() const { return m_formSubmissionTrigger; }
+
+ private:
+ FormState(PassRefPtr<HTMLFormElement>, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Frame>, FormSubmissionTrigger);
+
+ RefPtr<HTMLFormElement> m_form;
+ StringPairVector m_textFieldValues;
+ RefPtr<Frame> m_sourceFrame;
+ FormSubmissionTrigger m_formSubmissionTrigger;
+ };
+
+}
+
+#endif // FormState_h
diff --git a/Source/WebCore/loader/FormSubmission.cpp b/Source/WebCore/loader/FormSubmission.cpp
new file mode 100644
index 0000000..44f9ff1
--- /dev/null
+++ b/Source/WebCore/loader/FormSubmission.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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;
+}
+
+void FormSubmission::Attributes::copyFrom(const Attributes& other)
+{
+ m_method = other.m_method;
+ m_isMultiPartForm = other.m_isMultiPartForm;
+
+ m_action = other.m_action;
+ m_target = other.m_target;
+ m_encodingType = other.m_encodingType;
+ m_acceptCharset = other.m_acceptCharset;
+}
+
+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);
+
+ HTMLFormControlElement* submitButton = 0;
+ if (event && event->target() && event->target()->toNode())
+ submitButton = static_cast<HTMLFormControlElement*>(event->target()->toNode());
+
+ FormSubmission::Attributes copiedAttributes;
+ copiedAttributes.copyFrom(attributes);
+ if (submitButton) {
+ String attributeValue;
+ if (!(attributeValue = submitButton->getAttribute(formactionAttr)).isNull())
+ copiedAttributes.parseAction(attributeValue);
+ if (!(attributeValue = submitButton->getAttribute(formenctypeAttr)).isNull())
+ copiedAttributes.parseEncodingType(attributeValue);
+ if (!(attributeValue = submitButton->getAttribute(formmethodAttr)).isNull())
+ copiedAttributes.parseMethodType(attributeValue);
+ if (!(attributeValue = submitButton->getAttribute(formtargetAttr)).isNull())
+ copiedAttributes.setTarget(attributeValue);
+ }
+
+ Document* document = form->document();
+ KURL actionURL = document->completeURL(copiedAttributes.action().isEmpty() ? document->url().string() : copiedAttributes.action());
+ bool isMailtoForm = actionURL.protocolIs("mailto");
+ bool isMultiPartForm = false;
+ String encodingType = copiedAttributes.encodingType();
+
+ if (copiedAttributes.method() == PostMethod) {
+ isMultiPartForm = copiedAttributes.isMultiPartForm();
+ if (isMultiPartForm && isMailtoForm) {
+ encodingType = "application/x-www-form-urlencoded";
+ isMultiPartForm = false;
+ }
+ }
+
+ TextEncoding dataEncoding = isMailtoForm ? UTF8Encoding() : FormDataBuilder::encodingFromAcceptCharset(copiedAttributes.acceptCharset(), document);
+ RefPtr<DOMFormData> domFormData = DOMFormData::create(dataEncoding.encodingForFormSubmission());
+ Vector<pair<String, String> > formValues;
+
+ for (unsigned i = 0; i < form->associatedElements().size(); ++i) {
+ FormAssociatedElement* control = form->associatedElements()[i];
+ HTMLElement* element = toHTMLElement(control);
+ if (!element->disabled())
+ control->appendFormData(*domFormData, isMultiPartForm);
+ if (element->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 (copiedAttributes.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 = copiedAttributes.target().isEmpty() ? document->baseTarget() : copiedAttributes.target();
+ RefPtr<FormState> formState = FormState::create(form, formValues, document->frame(), trigger);
+ return adoptRef(new FormSubmission(copiedAttributes.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/Source/WebCore/loader/FormSubmission.h b/Source/WebCore/loader/FormSubmission.h
new file mode 100644
index 0000000..d724835
--- /dev/null
+++ b/Source/WebCore/loader/FormSubmission.h
@@ -0,0 +1,128 @@
+/*
+ * 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; }
+
+ void copyFrom(const Attributes&);
+
+ 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/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp
new file mode 100644
index 0000000..d11399c
--- /dev/null
+++ b/Source/WebCore/loader/FrameLoader.cpp
@@ -0,0 +1,3566 @@
+/*
+ * 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 "FrameLoader.h"
+
+#include "ApplicationCacheHost.h"
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+#include "Archive.h"
+#include "ArchiveFactory.h"
+#endif
+#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 "Document.h"
+#include "DocumentLoadTiming.h"
+#include "DocumentLoader.h"
+#include "Editor.h"
+#include "EditorClient.h"
+#include "Element.h"
+#include "Event.h"
+#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 "HTMLFormElement.h"
+#include "HTMLNames.h"
+#include "HTMLObjectElement.h"
+#include "HTTPParsers.h"
+#include "HistoryItem.h"
+#include "IconDatabase.h"
+#include "IconLoader.h"
+#include "InspectorController.h"
+#include "Logging.h"
+#include "MIMETypeRegistry.h"
+#include "MainResourceLoader.h"
+#include "Page.h"
+#include "PageCache.h"
+#include "PageGroup.h"
+#include "PageTransitionEvent.h"
+#include "PluginData.h"
+#include "PluginDatabase.h"
+#include "PluginDocument.h"
+#include "ProgressTracker.h"
+#include "ResourceHandle.h"
+#include "ResourceRequest.h"
+#include "SchemeRegistry.h"
+#include "ScriptController.h"
+#include "ScriptSourceCode.h"
+#include "SecurityOrigin.h"
+#include "SegmentedString.h"
+#include "SerializedScriptValue.h"
+#include "Settings.h"
+#include "TextResourceDecoder.h"
+#include "WindowFeatures.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"
+#include "SVGLocatable.h"
+#include "SVGNames.h"
+#include "SVGPreserveAspectRatio.h"
+#include "SVGSVGElement.h"
+#include "SVGViewElement.h"
+#include "SVGViewSpec.h"
+#endif
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#include "RenderArena.h"
+#endif
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+#if ENABLE(SVG)
+using namespace SVGNames;
+#endif
+
+#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)
+{
+ switch (type) {
+ case FrameLoadTypeStandard:
+ case FrameLoadTypeReload:
+ case FrameLoadTypeReloadFromOrigin:
+ case FrameLoadTypeSame:
+ case FrameLoadTypeRedirectWithLockedBackForwardList:
+ case FrameLoadTypeReplace:
+ return false;
+ case FrameLoadTypeBack:
+ case FrameLoadTypeBackWMLDeckNotAccessible:
+ case FrameLoadTypeForward:
+ case FrameLoadTypeIndexedBackForward:
+ return true;
+ }
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+static int numRequests(Document* document)
+{
+ if (!document)
+ return 0;
+
+ return document->cachedResourceLoader()->requestCount();
+}
+
+// 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 frame->document() && frame->document()->securityOrigin()->isSandboxed(mask);
+}
+
+FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client)
+ : m_frame(frame)
+ , m_client(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_quickRedirectComing(false)
+ , m_sentRedirectNotification(false)
+ , m_inStopAllLoaders(false)
+ , m_isExecutingJavaScriptFormAction(false)
+ , m_didCallImplicitClose(false)
+ , m_wasUnloadEventEmitted(false)
+ , m_pageDismissalEventBeingDispatched(false)
+ , m_isComplete(false)
+ , m_isLoadingMainResource(false)
+ , m_needsClear(false)
+ , m_checkTimer(this, &FrameLoader::checkTimerFired)
+ , m_shouldCallCheckCompleted(false)
+ , m_shouldCallCheckLoadComplete(false)
+ , m_opener(0)
+ , m_didPerformFirstNavigation(false)
+ , m_loadingFromCachedPage(false)
+ , m_suppressOpenerInNewFrame(false)
+ , m_sandboxFlags(SandboxAll)
+ , m_forcedSandboxFlags(SandboxNone)
+{
+}
+
+FrameLoader::~FrameLoader()
+{
+ setOpener(0);
+
+ HashSet<Frame*>::iterator end = m_openedFrames.end();
+ for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it)
+ (*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_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();
+ writer()->begin(KURL(), false);
+ writer()->end();
+ m_frame->document()->cancelParsing();
+ m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument);
+ m_didCallImplicitClose = true;
+
+ m_networkingContext = m_client->createNetworkingContext();
+}
+
+void FrameLoader::setDefersLoading(bool defers)
+{
+ if (m_documentLoader)
+ m_documentLoader->setDefersLoading(defers);
+ if (m_provisionalDocumentLoader)
+ m_provisionalDocumentLoader->setDefersLoading(defers);
+ if (m_policyDocumentLoader)
+ m_policyDocumentLoader->setDefersLoading(defers);
+
+ if (!defers) {
+ m_frame->navigationScheduler()->startTimer();
+ startCheckCompleteTimer();
+ }
+}
+
+bool FrameLoader::canHandleRequest(const ResourceRequest& request)
+{
+ return m_client->canHandleRequest(request);
+}
+
+void FrameLoader::changeLocation(PassRefPtr<SecurityOrigin> securityOrigin, const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool refresh)
+{
+ RefPtr<Frame> protect(m_frame);
+ urlSelected(FrameLoadRequest(securityOrigin, ResourceRequest(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy), "_self"),
+ 0, lockHistory, lockBackForwardList, SendReferrer, ReplaceDocumentIfJavaScriptURL);
+}
+
+void FrameLoader::urlSelected(const KURL& url, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, ReferrerPolicy referrerPolicy)
+{
+ urlSelected(FrameLoadRequest(m_frame->document()->securityOrigin(), ResourceRequest(url), passedTarget),
+ triggeringEvent, lockHistory, lockBackForwardList, referrerPolicy, DoNotReplaceDocumentIfJavaScriptURL);
+}
+
+// The shouldReplaceDocumentIfJavaScriptURL parameter will go away when the FIXME to eliminate the
+// corresponding parameter from ScriptController::executeIfJavaScriptURL() is addressed.
+void FrameLoader::urlSelected(const FrameLoadRequest& passedRequest, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, ReferrerPolicy referrerPolicy, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL)
+{
+ ASSERT(!m_suppressOpenerInNewFrame);
+
+ FrameLoadRequest frameRequest(passedRequest);
+
+ if (m_frame->script()->executeIfJavaScriptURL(frameRequest.resourceRequest().url(), shouldReplaceDocumentIfJavaScriptURL))
+ return;
+
+ if (frameRequest.frameName().isEmpty())
+ frameRequest.setFrameName(m_frame->document()->baseTarget());
+
+ if (referrerPolicy == NoReferrer)
+ m_suppressOpenerInNewFrame = true;
+ if (frameRequest.resourceRequest().httpReferrer().isEmpty())
+ frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
+ addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin());
+
+ loadFrameRequest(frameRequest, lockHistory, lockBackForwardList, triggeringEvent, 0, referrerPolicy);
+
+ m_suppressOpenerInNewFrame = false;
+}
+
+void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission)
+{
+ ASSERT(submission->method() == FormSubmission::PostMethod || submission->method() == FormSubmission::GetMethod);
+
+ // FIXME: Find a good spot for these.
+ ASSERT(submission->data());
+ ASSERT(submission->state());
+ ASSERT(submission->state()->sourceFrame() == m_frame);
+
+ if (!m_frame->page())
+ return;
+
+ if (submission->action().isEmpty())
+ return;
+
+ if (isDocumentSandboxed(m_frame, SandboxForms))
+ return;
+
+ if (protocolIsJavaScript(submission->action())) {
+ m_isExecutingJavaScriptFormAction = true;
+ m_frame->script()->executeIfJavaScriptURL(submission->action(), DoNotReplaceDocumentIfJavaScriptURL);
+ m_isExecutingJavaScriptFormAction = false;
+ return;
+ }
+
+ Frame* targetFrame = m_frame->tree()->find(submission->target());
+ if (!shouldAllowNavigation(targetFrame))
+ return;
+ if (!targetFrame) {
+ if (!DOMWindow::allowPopUp(m_frame) && !isProcessingUserGesture())
+ return;
+
+ targetFrame = m_frame;
+ } else
+ submission->clearTarget();
+
+ if (!targetFrame->page())
+ return;
+
+ // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
+
+ // We do not want to submit more than one form from the same page, nor do we want to submit a single
+ // form more than once. This flag prevents these from happening; not sure how other browsers prevent this.
+ // The flag is reset in each time we start handle a new mouse or key down event, and
+ // also in setView since this part may get reused for a page from the back/forward cache.
+ // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
+
+ // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
+ // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
+ // needed any more now that we reset m_submittedFormURL on each mouse or key down event.
+
+ if (m_frame->tree()->isDescendantOf(targetFrame)) {
+ if (m_submittedFormURL == submission->action())
+ return;
+ m_submittedFormURL = submission->action();
+ }
+
+ submission->data()->generateFiles(m_frame->document());
+ submission->setReferrer(m_outgoingReferrer);
+ submission->setOrigin(outgoingOrigin());
+
+ targetFrame->navigationScheduler()->scheduleFormSubmission(submission);
+}
+
+void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolicy databasePolicy)
+{
+ if (m_frame->document() && m_frame->document()->parser())
+ m_frame->document()->parser()->stopParsing();
+
+ if (unloadEventPolicy != UnloadEventPolicyNone) {
+ if (m_frame->document()) {
+ if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) {
+ Node* currentFocusedNode = m_frame->document()->focusedNode();
+ if (currentFocusedNode)
+ currentFocusedNode->aboutToUnload();
+ 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()) {
+ RefPtr<Event> unloadEvent(Event::create(eventNames().unloadEvent, false, false));
+ // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed
+ // while dispatching the event, so protect it to prevent writing the end
+ // time into freed memory.
+ RefPtr<DocumentLoader> documentLoader = m_provisionalDocumentLoader;
+ if (documentLoader && !documentLoader->timing()->unloadEventStart && !documentLoader->timing()->unloadEventEnd) {
+ DocumentLoadTiming* timing = documentLoader->timing();
+ ASSERT(timing->navigationStart);
+ m_frame->domWindow()->dispatchTimedEvent(unloadEvent, m_frame->domWindow()->document(), &timing->unloadEventStart, &timing->unloadEventEnd);
+ ASSERT(timing->unloadEventStart >= timing->navigationStart);
+ } else
+ m_frame->domWindow()->dispatchEvent(unloadEvent, m_frame->domWindow()->document());
+ }
+ }
+ m_pageDismissalEventBeingDispatched = false;
+ if (m_frame->document())
+ m_frame->document()->updateStyleIfNeeded();
+ m_wasUnloadEventEmitted = true;
+ }
+ }
+
+ // 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_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader
+ && m_frame->document()->securityOrigin()->isSecureTransitionTo(m_provisionalDocumentLoader->url());
+
+ if (!keepEventListeners)
+ m_frame->document()->removeAllEventListeners();
+ }
+ }
+
+ m_isComplete = true; // to avoid calling completed() in finishedParsing()
+ m_isLoadingMainResource = false;
+ m_didCallImplicitClose = true; // don't want that one either
+
+ if (m_frame->document() && m_frame->document()->parsing()) {
+ finishedParsing();
+ m_frame->document()->setParsing(false);
+ }
+
+ m_workingURL = KURL();
+
+ if (Document* doc = m_frame->document()) {
+ // 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())
+ cachedResourceLoader->cancelRequests();
+
+#if ENABLE(DATABASE)
+ if (databasePolicy == DatabasePolicyStop)
+ doc->stopDatabases(0);
+#else
+ UNUSED_PARAM(databasePolicy);
+#endif
+ }
+
+ // 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()
+{
+ // http://bugs.webkit.org/show_bug.cgi?id=10854
+ // The frame's last ref may be removed and it will be deleted by checkCompleted().
+ RefPtr<Frame> protector(m_frame);
+
+ if (m_frame->document()->parser())
+ m_frame->document()->parser()->stopParsing();
+ m_frame->document()->finishParsing();
+
+ if (m_iconLoader)
+ m_iconLoader->stopLoading();
+}
+
+bool FrameLoader::closeURL()
+{
+ history()->saveDocumentState();
+
+ // Should only send the pagehide event here if the current document exists and has not been placed in the page cache.
+ Document* currentDocument = m_frame->document();
+ stopLoading(currentDocument && !currentDocument->inPageCache() ? UnloadEventPolicyUnloadAndPageHide : UnloadEventPolicyUnloadOnly);
+
+ m_frame->editor()->clearUndoRedoOperations();
+ return true;
+}
+
+KURL FrameLoader::iconURL()
+{
+ // If this isn't a top level frame, return nothing
+ if (m_frame->tree() && m_frame->tree()->parent())
+ return KURL();
+
+ // If we have an iconURL from a Link element, return that
+ if (!m_frame->document()->iconURL().isEmpty())
+ return KURL(ParsedURLString, m_frame->document()->iconURL());
+
+ // Don't return a favicon iconURL unless we're http or https
+ if (!m_URL.protocolInHTTPFamily())
+ return KURL();
+
+ KURL url;
+ bool couldSetProtocol = url.setProtocol(m_URL.protocol());
+ ASSERT_UNUSED(couldSetProtocol, couldSetProtocol);
+ url.setHost(m_URL.host());
+ if (m_URL.hasPort())
+ url.setPort(m_URL.port());
+ url.setPath("/favicon.ico");
+ return url;
+}
+
+bool FrameLoader::didOpenURL(const KURL& url)
+{
+ 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->navigationScheduler()->cancel();
+ m_frame->editor()->clearLastEditCommand();
+
+ m_isComplete = false;
+ m_isLoadingMainResource = true;
+ m_didCallImplicitClose = false;
+
+ // 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_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())
+ m_URL.setPath("/");
+ m_workingURL = m_URL;
+
+ started();
+
+ return true;
+}
+
+void FrameLoader::didExplicitOpen()
+{
+ m_isComplete = false;
+ m_didCallImplicitClose = false;
+
+ // Calling document.open counts as committing the first real document load.
+ 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->navigationScheduler()->cancel();
+ if (m_frame->document()->url() != blankURL())
+ m_URL = m_frame->document()->url();
+}
+
+
+void FrameLoader::cancelAndClear()
+{
+ m_frame->navigationScheduler()->cancel();
+
+ if (!m_isComplete)
+ closeURL();
+
+ clear(false);
+ m_frame->script()->updatePlatformScriptObjects();
+}
+
+void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView)
+{
+ m_frame->editor()->clear();
+
+ if (!m_needsClear)
+ return;
+ m_needsClear = false;
+
+ if (!m_frame->document()->inPageCache()) {
+ m_frame->document()->cancelParsing();
+ m_frame->document()->stopActiveDOMObjects();
+ if (m_frame->document()->attached()) {
+ m_frame->document()->willRemove();
+ m_frame->document()->detach();
+
+ m_frame->document()->removeFocusedNodeOfSubtree(m_frame->document());
+ }
+ }
+
+ // Do this after detaching the document so that the unload event works.
+ if (clearWindowProperties) {
+ m_frame->clearDOMWindow();
+ m_frame->script()->clearWindowShell(m_frame->document()->inPageCache());
+ }
+
+ m_frame->selection()->clear();
+ m_frame->eventHandler()->clear();
+ if (clearFrameView && m_frame->view())
+ m_frame->view()->clear();
+
+ // 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);
+ writer()->clear();
+
+ m_subframeLoader.clear();
+
+ if (clearScriptObjects)
+ m_frame->script()->clearScriptObjects();
+
+ m_frame->navigationScheduler()->clear();
+
+ m_checkTimer.stop();
+ m_shouldCallCheckCompleted = false;
+ m_shouldCallCheckLoadComplete = false;
+
+ if (m_stateMachine.isDisplayingInitialEmptyDocument() && m_stateMachine.committedFirstRealDocumentLoad())
+ m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);
+}
+
+void FrameLoader::receivedFirstData()
+{
+ writer()->begin(m_workingURL, false);
+ writer()->setDocumentWasLoadedAsPartOfNavigation();
+
+ dispatchDidCommitLoad();
+ dispatchDidClearWindowObjectsInAllWorlds();
+
+ if (m_documentLoader) {
+ String ptitle = m_documentLoader->title();
+ // If we have a title let the WebView know about it.
+ if (!ptitle.isNull())
+ m_client->dispatchDidReceiveTitle(ptitle);
+ }
+
+ m_workingURL = KURL();
+
+ double delay;
+ String url;
+ if (!m_documentLoader)
+ return;
+ if (m_frame->inViewSourceMode())
+ return;
+ if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField("Refresh"), false, delay, url))
+ return;
+
+ if (url.isEmpty())
+ url = m_URL.string();
+ else
+ url = m_frame->document()->completeURL(url).string();
+
+ m_frame->navigationScheduler()->scheduleRedirect(delay, url);
+}
+
+void FrameLoader::setURL(const KURL& url)
+{
+ KURL ref(url);
+ ref.setUser(String());
+ ref.setPass(String());
+ ref.removeFragmentIdentifier();
+ m_outgoingReferrer = ref.string();
+ m_URL = url;
+}
+
+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) {
+ m_frame->document()->statePopped(m_pendingStateObject.get());
+ m_pendingStateObject.clear();
+ }
+
+ if (dispatch)
+ dispatchDidClearWindowObjectsInAllWorlds();
+
+ updateFirstPartyForCookies();
+
+ Settings* settings = m_frame->document()->settings();
+ m_frame->document()->cachedResourceLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically());
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ 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())
+ m_frame->document()->parseDNSPrefetchControlHeader(dnsPrefetchControl);
+ }
+
+ history()->restoreDocumentState();
+}
+
+void FrameLoader::didEndDocument()
+{
+ m_isLoadingMainResource = false;
+}
+
+void FrameLoader::iconLoadDecisionAvailable()
+{
+ if (!m_mayLoadIconLater)
+ return;
+ LOG(IconDatabase, "FrameLoader %p was told a load decision is available for its icon", this);
+ startIconLoader();
+ m_mayLoadIconLater = false;
+}
+
+void FrameLoader::startIconLoader()
+{
+ // FIXME: We kick off the icon loader when the frame is done receiving its main resource.
+ // But we should instead do it when we're done parsing the head element.
+ if (!isLoadingMainFrame())
+ return;
+
+ if (!iconDatabase() || !iconDatabase()->isEnabled())
+ return;
+
+ KURL url(iconURL());
+ String urlString(url.string());
+ if (urlString.isEmpty())
+ return;
+
+ // If we're not reloading and the icon database doesn't say to load now then bail before we actually start the load
+ if (loadType() != FrameLoadTypeReload && loadType() != FrameLoadTypeReloadFromOrigin) {
+ IconLoadDecision decision = iconDatabase()->loadDecisionForIconURL(urlString, m_documentLoader.get());
+ if (decision == IconLoadNo) {
+ LOG(IconDatabase, "FrameLoader::startIconLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data());
+ commitIconURLToIconDatabase(url);
+
+ // We were told not to load this icon - that means this icon is already known by the database
+ // If the icon data hasn't been read in from disk yet, kick off the read of the icon from the database to make sure someone
+ // has done it. This is after registering for the notification so the WebView can call the appropriate delegate method.
+ // Otherwise if the icon data *is* available, notify the delegate
+ if (!iconDatabase()->iconDataKnownForIconURL(urlString)) {
+ LOG(IconDatabase, "Told not to load icon %s but icon data is not yet available - registering for notification and requesting load from disk", urlString.ascii().data());
+ m_client->registerForIconNotification();
+ iconDatabase()->iconForPageURL(m_URL.string(), IntSize(0, 0));
+ iconDatabase()->iconForPageURL(originalRequestURL().string(), IntSize(0, 0));
+ } else
+ m_client->dispatchDidReceiveIcon();
+
+ return;
+ }
+
+ if (decision == IconLoadUnknown) {
+ // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database
+ // just in case we don't end up loading later - if we commit the mapping a second time after the load, that's no big deal
+ // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the
+ // icon is later read in from disk
+ LOG(IconDatabase, "FrameLoader %p might load icon %s later", this, urlString.ascii().data());
+ m_mayLoadIconLater = true;
+ m_client->registerForIconNotification();
+ commitIconURLToIconDatabase(url);
+ return;
+ }
+ }
+
+ // People who want to avoid loading images generally want to avoid loading all images.
+ // Now that we've accounted for URL mapping, avoid starting the network load if images aren't set to display automatically.
+ Settings* settings = m_frame->settings();
+ if (settings && !settings->loadsImagesAutomatically())
+ return;
+
+ // This is either a reload or the icon database said "yes, load the icon", so kick off the load!
+ if (!m_iconLoader)
+ m_iconLoader = IconLoader::create(m_frame);
+
+ m_iconLoader->startLoading();
+}
+
+void FrameLoader::commitIconURLToIconDatabase(const KURL& icon)
+{
+ ASSERT(iconDatabase());
+ LOG(IconDatabase, "Committing iconURL %s to database for pageURLs %s and %s", icon.string().ascii().data(), m_URL.string().ascii().data(), originalRequestURL().string().ascii().data());
+ iconDatabase()->setIconURLForPageURL(icon.string(), m_URL.string());
+ iconDatabase()->setIconURLForPageURL(icon.string(), originalRequestURL().string());
+}
+
+void FrameLoader::finishedParsing()
+{
+ if (m_stateMachine.creatingInitialEmptyDocument())
+ return;
+
+ m_frame->injectUserScripts(InjectAtDocumentEnd);
+
+ // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves
+ // because doing so will cause us to re-enter the destructor when protector goes out of scope.
+ // Null-checking the FrameView indicates whether or not we're in the destructor.
+ RefPtr<Frame> protector = m_frame->view() ? m_frame : 0;
+
+ m_client->dispatchDidFinishDocumentLoad();
+
+ checkCompleted();
+
+ if (!m_frame->view())
+ return; // We are being destroyed by something checkCompleted called.
+
+ // Check if the scrollbars are really needed for the content.
+ // If not, remove them, relayout, and repaint.
+ m_frame->view()->restoreScrollbar();
+ m_frame->view()->scrollToFragment(m_URL);
+}
+
+void FrameLoader::loadDone()
+{
+ checkCompleted();
+}
+
+bool FrameLoader::allChildrenAreComplete() const
+{
+ for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
+ if (!child->loader()->m_isComplete)
+ return false;
+ }
+ return true;
+}
+
+bool FrameLoader::allAncestorsAreComplete() const
+{
+ for (Frame* ancestor = m_frame; ancestor; ancestor = ancestor->tree()->parent()) {
+ if (!ancestor->loader()->m_isComplete)
+ return false;
+ }
+ return true;
+}
+
+void FrameLoader::checkCompleted()
+{
+ m_shouldCallCheckCompleted = false;
+
+ if (m_frame->view())
+ m_frame->view()->checkStopDelayingDeferredRepaints();
+
+ // Any frame that hasn't completed yet?
+ if (!allChildrenAreComplete())
+ return;
+
+ // Have we completed before?
+ if (m_isComplete)
+ return;
+
+ // Are we still parsing?
+ if (m_frame->document()->parsing())
+ return;
+
+ // Still waiting for images/scripts?
+ 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->navigationScheduler()->startTimer();
+
+ completed();
+ if (m_frame->page())
+ checkLoadComplete();
+}
+
+void FrameLoader::checkTimerFired(Timer<FrameLoader>*)
+{
+ if (Page* page = m_frame->page()) {
+ if (page->defersLoading())
+ return;
+ }
+ if (m_shouldCallCheckCompleted)
+ checkCompleted();
+ if (m_shouldCallCheckLoadComplete)
+ checkLoadComplete();
+}
+
+void FrameLoader::startCheckCompleteTimer()
+{
+ if (!(m_shouldCallCheckCompleted || m_shouldCallCheckLoadComplete))
+ return;
+ if (m_checkTimer.isActive())
+ return;
+ m_checkTimer.startOneShot(0);
+}
+
+void FrameLoader::scheduleCheckCompleted()
+{
+ m_shouldCallCheckCompleted = true;
+ startCheckCompleteTimer();
+}
+
+void FrameLoader::scheduleCheckLoadComplete()
+{
+ m_shouldCallCheckLoadComplete = true;
+ startCheckCompleteTimer();
+}
+
+void FrameLoader::checkCallImplicitClose()
+{
+ if (m_didCallImplicitClose || m_frame->document()->parsing() || m_frame->document()->isDelayingLoadEvent())
+ return;
+
+ if (!allChildrenAreComplete())
+ return; // still got a frame running -> too early
+
+ m_didCallImplicitClose = true;
+ m_wasUnloadEventEmitted = false;
+ m_frame->document()->implicitClose();
+}
+
+KURL FrameLoader::baseURL() const
+{
+ ASSERT(m_frame->document());
+ return m_frame->document()->baseURL();
+}
+
+KURL FrameLoader::completeURL(const String& url)
+{
+ ASSERT(m_frame->document());
+ return m_frame->document()->completeURL(url);
+}
+
+void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, Frame* childFrame)
+{
+ ASSERT(childFrame);
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree()->uniqueName());
+ if (subframeArchive) {
+ childFrame->loader()->loadArchive(subframeArchive.release());
+ return;
+ }
+#endif
+
+ HistoryItem* parentItem = history()->currentItem();
+ // 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()->uniqueName());
+ if (childItem) {
+ childFrame->loader()->loadDifferentDocumentItem(childItem, loadType());
+ return;
+ }
+ }
+
+ childFrame->loader()->loadURL(url, referer, String(), false, FrameLoadTypeRedirectWithLockedBackForwardList, 0, 0);
+}
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive)
+{
+ RefPtr<Archive> archive = prpArchive;
+
+ ArchiveResource* mainResource = archive->mainResource();
+ ASSERT(mainResource);
+ if (!mainResource)
+ return;
+
+ SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), KURL());
+
+ ResourceRequest request(mainResource->url());
+#if PLATFORM(MAC)
+ request.applyWebArchiveHackForMail();
+#endif
+
+ RefPtr<DocumentLoader> documentLoader = m_client->createDocumentLoader(request, substituteData);
+ documentLoader->addAllArchiveResources(archive.get());
+ load(documentLoader.get());
+}
+#endif
+
+ObjectContentType FrameLoader::defaultObjectContentType(const KURL& url, const String& mimeTypeIn)
+{
+ String mimeType = mimeTypeIn;
+ // We don't use MIMETypeRegistry::getMIMETypeForPath() because it returns "application/octet-stream" upon failure
+ if (mimeType.isEmpty())
+ mimeType = MIMETypeRegistry::getMIMETypeForExtension(url.path().substring(url.path().reverseFind('.') + 1));
+
+ if (mimeType.isEmpty())
+ return ObjectContentFrame; // Go ahead and hope that we can display the content.
+
+ if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType))
+ return WebCore::ObjectContentImage;
+
+#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
+
+ if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType))
+ return WebCore::ObjectContentFrame;
+
+ return WebCore::ObjectContentNone;
+}
+
+String FrameLoader::outgoingReferrer() const
+{
+ return m_outgoingReferrer;
+}
+
+String FrameLoader::outgoingOrigin() const
+{
+ return m_frame->document()->securityOrigin()->toString();
+}
+
+bool FrameLoader::isMixedContent(SecurityOrigin* context, const KURL& url)
+{
+ if (context->protocol() != "https")
+ return false; // We only care about HTTPS security origins.
+
+ if (!url.isValid() || SchemeRegistry::shouldTreatURLSchemeAsSecure(url.protocol()))
+ return false; // Loading these protocols is secure.
+
+ return true;
+}
+
+void FrameLoader::checkIfDisplayInsecureContent(SecurityOrigin* context, const KURL& url)
+{
+ if (!isMixedContent(context, url))
+ return;
+
+ 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();
+}
+
+void FrameLoader::checkIfRunInsecureContent(SecurityOrigin* context, const KURL& url)
+{
+ if (!isMixedContent(context, url))
+ return;
+
+ 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);
+}
+
+Frame* FrameLoader::opener()
+{
+ return m_opener;
+}
+
+void FrameLoader::setOpener(Frame* opener)
+{
+ if (m_opener)
+ m_opener->loader()->m_openedFrames.remove(m_frame);
+ if (opener)
+ opener->loader()->m_openedFrames.add(m_frame);
+ m_opener = opener;
+
+ if (m_frame->document()) {
+ m_frame->document()->initSecurityContext();
+ m_frame->domWindow()->setSecurityOrigin(m_frame->document()->securityOrigin());
+ }
+}
+
+// FIXME: This does not belong in FrameLoader!
+void FrameLoader::handleFallbackContent()
+{
+ HTMLFrameOwnerElement* owner = m_frame->ownerElement();
+ if (!owner || !owner->hasTagName(objectTag))
+ return;
+ static_cast<HTMLObjectElement*>(owner)->renderFallbackContent();
+}
+
+void FrameLoader::provisionalLoadStarted()
+{
+#ifdef ANDROID_INSTRUMENT
+ if (!m_frame->tree()->parent())
+ android::TimeCounter::reset();
+#endif
+ 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(NotAboutToExecuteScript))
+ return true; // If JavaScript is disabled, a user gesture must have initiated the navigation.
+ return ScriptController::processingUserGesture(); // FIXME: Use pageIsProcessingUserGesture.
+}
+
+void FrameLoader::resetMultipleFormSubmissionProtection()
+{
+ m_submittedFormURL = KURL();
+}
+
+void FrameLoader::willSetEncoding()
+{
+ if (!m_workingURL.isEmpty())
+ receivedFirstData();
+}
+
+#if ENABLE(WML)
+static inline bool frameContainsWMLContent(Frame* frame)
+{
+ Document* document = frame ? frame->document() : 0;
+ if (!document)
+ return false;
+
+ return document->containsWMLContent() || document->isWMLDocument();
+}
+#endif
+
+void FrameLoader::updateFirstPartyForCookies()
+{
+ if (m_frame->tree()->parent())
+ setFirstPartyForCookies(m_frame->tree()->parent()->document()->firstPartyForCookies());
+ else
+ setFirstPartyForCookies(m_URL);
+}
+
+void FrameLoader::setFirstPartyForCookies(const KURL& url)
+{
+ m_frame->document()->setFirstPartyForCookies(url);
+ for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
+ child->loader()->setFirstPartyForCookies(url);
+}
+
+// This does the same kind of work that didOpenURL does, except it relies on the fact
+// that a higher level already checked that the URLs match and the scrolling is the right thing to do.
+void FrameLoader::loadInSameDocument(const KURL& url, SerializedScriptValue* stateObject, bool isNewNavigation)
+{
+ // If we have a state object, we cannot also be a new navigation.
+ ASSERT(!stateObject || (stateObject && !isNewNavigation));
+
+ // Update the data source's request with the new URL to fake the URL change
+ m_frame->document()->setURL(url);
+ documentLoader()->replaceRequestURLForSameDocumentNavigation(url);
+ if (isNewNavigation && !shouldTreatURLAsSameAsCurrent(url) && !stateObject) {
+ // NB: must happen after replaceRequestURLForSameDocumentNavigation(), since we add
+ // based on the current request. Must also happen before we openURL and displace the
+ // scroll position, since adding the BF item will save away scroll state.
+
+ // NB2: If we were loading a long, slow doc, and the user anchor nav'ed before
+ // it was done, currItem is now set the that slow doc, and prevItem is whatever was
+ // before it. Adding the b/f item will bump the slow doc down to prevItem, even
+ // though its load is not yet done. I think this all works out OK, for one because
+ // we have already saved away the scroll and doc state for the long slow load,
+ // but it's not an obvious case.
+
+ history()->updateBackForwardListForFragmentScroll();
+ }
+
+ String oldURL;
+ bool hashChange = equalIgnoringFragmentIdentifier(url, m_URL) && url.fragmentIdentifier() != m_URL.fragmentIdentifier();
+ oldURL = m_URL;
+
+ m_URL = url;
+ history()->updateForSameDocumentNavigation();
+
+ // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor
+ if (hashChange)
+ m_frame->eventHandler()->stopAutoscrollTimer();
+
+ // 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();
+
+ // 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();
+
+ if (isNewNavigation) {
+ // This will clear previousItem from the rest of the frame tree that didn't
+ // doing any loading. We need to make a pass on this now, since for anchor nav
+ // we'll not go through a real load and reach Completed state.
+ checkLoadComplete();
+ }
+
+ m_client->dispatchDidNavigateWithinPage();
+
+ m_frame->document()->statePopped(stateObject ? stateObject : SerializedScriptValue::nullValue());
+ m_client->dispatchDidPopStateWithinPage();
+
+ if (hashChange) {
+ m_frame->document()->enqueueHashchangeEvent(oldURL, url);
+ m_client->dispatchDidChangeLocationWithinPage();
+ }
+
+ // FrameLoaderClient::didFinishLoad() tells the internal load delegate the load finished with no error
+ m_client->didFinishLoad();
+}
+
+bool FrameLoader::isComplete() const
+{
+ return m_isComplete;
+}
+
+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->navigationScheduler()->startTimer();
+
+ if (Frame* parent = m_frame->tree()->parent())
+ parent->loader()->checkCompleted();
+
+ if (m_frame->view())
+ m_frame->view()->maintainScrollPositionAtAnchor(0);
+}
+
+void FrameLoader::started()
+{
+ for (Frame* frame = m_frame; frame; frame = frame->tree()->parent())
+ frame->loader()->m_isComplete = false;
+}
+
+void FrameLoader::prepareForLoadStart()
+{
+ if (Page* page = m_frame->page())
+ page->progress()->progressStarted(m_frame);
+ m_client->dispatchDidStartProvisionalLoad();
+}
+
+void FrameLoader::setupForReplace()
+{
+ setState(FrameStateProvisional);
+ m_provisionalDocumentLoader = m_documentLoader;
+ m_documentLoader = 0;
+ detachChildren();
+}
+
+void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType)
+{
+ activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType);
+}
+
+// This is a hack to allow keep navigation to http/https feeds working. To remove this
+// we need to introduce new API akin to registerURLSchemeAsLocal, that registers a
+// protocols navigation policy.
+static bool isFeedWithNestedProtocolInHTTPFamily(const KURL& url)
+{
+ const String& urlString = url.string();
+ if (!urlString.startsWith("feed", false))
+ return false;
+
+ return urlString.startsWith("feed://", false)
+ || urlString.startsWith("feed:http:", false) || urlString.startsWith("feed:https:", false)
+ || urlString.startsWith("feeds:http:", false) || urlString.startsWith("feeds:https:", false)
+ || urlString.startsWith("feedsearch:http:", false) || urlString.startsWith("feedsearch:https:", false);
+}
+
+void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHistory, bool lockBackForwardList,
+ PassRefPtr<Event> event, PassRefPtr<FormState> formState, ReferrerPolicy referrerPolicy)
+{
+ KURL url = request.resourceRequest().url();
+
+ ASSERT(m_frame->document());
+ // FIXME: Should we move the isFeedWithNestedProtocolInHTTPFamily logic inside SecurityOrigin::canDisplay?
+ if (!isFeedWithNestedProtocolInHTTPFamily(url) && !request.requester()->canDisplay(url)) {
+ reportLocalLoadFailed(m_frame, url.string());
+ return;
+ }
+
+ String referrer;
+ String argsReferrer = request.resourceRequest().httpReferrer();
+ if (!argsReferrer.isEmpty())
+ referrer = argsReferrer;
+ else
+ referrer = m_outgoingReferrer;
+
+ if (SecurityOrigin::shouldHideReferrer(url, referrer) || referrerPolicy == NoReferrer)
+ referrer = String();
+
+ FrameLoadType loadType;
+ if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData)
+ loadType = FrameLoadTypeReload;
+ else if (lockBackForwardList)
+ loadType = FrameLoadTypeRedirectWithLockedBackForwardList;
+ else
+ loadType = FrameLoadTypeStandard;
+
+ if (request.resourceRequest().httpMethod() == "POST")
+ loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get());
+ else
+ loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get());
+
+ // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual
+ // load if frame names have changed.
+ Frame* sourceFrame = formState ? formState->sourceFrame() : m_frame;
+ Frame* targetFrame = sourceFrame->loader()->findFrameForNavigation(request.frameName());
+ if (targetFrame && targetFrame != sourceFrame) {
+ if (Page* page = targetFrame->page())
+ page->chrome()->focus();
+ }
+}
+
+void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType,
+ PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState)
+{
+ RefPtr<FormState> formState = prpFormState;
+ bool isFormSubmission = formState;
+
+ ResourceRequest request(newURL);
+ if (!referrer.isEmpty()) {
+ request.setHTTPReferrer(referrer);
+ RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer);
+ addHTTPOriginIfNeeded(request, referrerOrigin->toString());
+ }
+ addExtraFieldsToRequest(request, newLoadType, true, event || isFormSubmission);
+ if (newLoadType == FrameLoadTypeReload || newLoadType == FrameLoadTypeReloadFromOrigin)
+ request.setCachePolicy(ReloadIgnoringCacheData);
+
+ ASSERT(newLoadType != FrameLoadTypeSame);
+
+ // The search for a target frame is done earlier in the case of form submission.
+ Frame* targetFrame = isFormSubmission ? 0 : findFrameForNavigation(frameName);
+ if (targetFrame && targetFrame != m_frame) {
+ targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release());
+ return;
+ }
+
+ if (m_pageDismissalEventBeingDispatched)
+ return;
+
+ NavigationAction action(newURL, newLoadType, isFormSubmission, event);
+
+ if (!targetFrame && !frameName.isEmpty()) {
+ policyChecker()->checkNewWindowPolicy(action, FrameLoader::callContinueLoadAfterNewWindowPolicy,
+ request, formState.release(), frameName, this);
+ return;
+ }
+
+ RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
+
+ bool sameURL = shouldTreatURLAsSameAsCurrent(newURL);
+ const String& httpMethod = request.httpMethod();
+
+ // Make sure to do scroll to anchor processing even if the URL is
+ // exactly the same so pages with '#' links and DHTML side effects
+ // work properly.
+ if (shouldScrollToAnchor(isFormSubmission, httpMethod, newLoadType, newURL)) {
+ oldDocumentLoader->setTriggeringAction(action);
+ policyChecker()->stopCheck();
+ policyChecker()->setLoadType(newLoadType);
+ policyChecker()->checkNavigationPolicy(request, oldDocumentLoader.get(), formState.release(),
+ callContinueFragmentScrollAfterNavigationPolicy, this);
+ } else {
+ // must grab this now, since this load may stop the previous load and clear this flag
+ bool isRedirect = m_quickRedirectComing;
+ loadWithNavigationAction(request, action, lockHistory, newLoadType, formState.release());
+ if (isRedirect) {
+ m_quickRedirectComing = false;
+ if (m_provisionalDocumentLoader)
+ m_provisionalDocumentLoader->setIsClientRedirect(true);
+ } else if (sameURL)
+ // Example of this case are sites that reload the same URL with a different cookie
+ // driving the generated content, or a master frame with links that drive a target
+ // frame, where the user has clicked on the same link repeatedly.
+ m_loadType = FrameLoadTypeSame;
+ }
+}
+
+void FrameLoader::load(const ResourceRequest& request, bool lockHistory)
+{
+ load(request, SubstituteData(), lockHistory);
+}
+
+void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory)
+{
+ if (m_inStopAllLoaders)
+ return;
+
+ // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.
+ m_loadType = FrameLoadTypeStandard;
+ RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData);
+ if (lockHistory && m_documentLoader)
+ loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory());
+ load(loader.get());
+}
+
+void FrameLoader::load(const ResourceRequest& request, const String& frameName, bool lockHistory)
+{
+ if (frameName.isEmpty()) {
+ load(request, lockHistory);
+ return;
+ }
+
+ Frame* frame = findFrameForNavigation(frameName);
+ if (frame) {
+ frame->loader()->load(request, lockHistory);
+ return;
+ }
+
+ policyChecker()->checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), FrameLoader::callContinueLoadAfterNewWindowPolicy, request, 0, frameName, this);
+}
+
+void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState)
+{
+ RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
+ if (lockHistory && m_documentLoader)
+ loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory());
+
+ loader->setTriggeringAction(action);
+ if (m_documentLoader)
+ loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
+
+ loadWithDocumentLoader(loader.get(), type, formState);
+}
+
+void FrameLoader::load(DocumentLoader* newDocumentLoader)
+{
+ ResourceRequest& r = newDocumentLoader->request();
+ addExtraFieldsToMainResourceRequest(r);
+ FrameLoadType type;
+
+ if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
+ r.setCachePolicy(ReloadIgnoringCacheData);
+ type = FrameLoadTypeSame;
+ } else
+ type = FrameLoadTypeStandard;
+
+ if (m_documentLoader)
+ newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
+
+ // When we loading alternate content for an unreachable URL that we're
+ // visiting in the history list, we treat it as a reload so the history list
+ // is appropriately maintained.
+ //
+ // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ...
+ // shouldn't a more explicit type of reload be defined, that means roughly
+ // "load without affecting history" ?
+ if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
+ ASSERT(type == FrameLoadTypeStandard);
+ type = FrameLoadTypeReload;
+ }
+
+ loadWithDocumentLoader(newDocumentLoader, type, 0);
+}
+
+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
+ // to parser requiring a FrameView. We should fix this dependency.
+
+ ASSERT(m_frame->view());
+
+ if (m_pageDismissalEventBeingDispatched)
+ return;
+
+ if (m_frame->document())
+ m_previousUrl = m_frame->document()->url();
+
+ policyChecker()->setLoadType(type);
+ RefPtr<FormState> formState = prpFormState;
+ bool isFormSubmission = formState;
+
+ const KURL& newURL = loader->request().url();
+ const String& httpMethod = loader->request().httpMethod();
+
+ if (shouldScrollToAnchor(isFormSubmission, httpMethod, policyChecker()->loadType(), newURL)) {
+ RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
+ NavigationAction action(newURL, policyChecker()->loadType(), isFormSubmission);
+
+ oldDocumentLoader->setTriggeringAction(action);
+ policyChecker()->stopCheck();
+ policyChecker()->checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState,
+ callContinueFragmentScrollAfterNavigationPolicy, this);
+ } else {
+ if (Frame* parent = m_frame->tree()->parent())
+ loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());
+
+ policyChecker()->stopCheck();
+ setPolicyDocumentLoader(loader);
+ if (loader->triggeringAction().isEmpty())
+ loader->setTriggeringAction(NavigationAction(newURL, policyChecker()->loadType(), isFormSubmission));
+
+ if (Element* ownerElement = m_frame->document()->ownerElement()) {
+ if (!ownerElement->dispatchBeforeLoadEvent(loader->request().url().string())) {
+ continueLoadAfterNavigationPolicy(loader->request(), formState, false);
+ return;
+ }
+ }
+
+ policyChecker()->checkNavigationPolicy(loader->request(), loader, formState,
+ callContinueLoadAfterNavigationPolicy, this);
+ }
+}
+
+void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url)
+{
+ ASSERT(!url.isEmpty());
+ if (!frame)
+ return;
+
+ frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String());
+}
+
+const ResourceRequest& FrameLoader::initialRequest() const
+{
+ return activeDocumentLoader()->originalRequest();
+}
+
+bool FrameLoader::willLoadMediaElementURL(KURL& url)
+{
+ ResourceRequest request(url);
+
+ unsigned long identifier;
+ ResourceError error;
+ requestFromDelegate(request, identifier, error);
+ notifier()->sendRemainingDelegateMessages(m_documentLoader.get(), identifier, ResourceResponse(url, String(), -1, String(), String()), -1, error);
+
+ url = request.url();
+
+ return error.isNull();
+}
+
+bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader)
+{
+ KURL unreachableURL = docLoader->unreachableURL();
+
+ if (unreachableURL.isEmpty())
+ return false;
+
+ if (!isBackForwardLoadType(policyChecker()->loadType()))
+ return false;
+
+ // We only treat unreachableURLs specially during the delegate callbacks
+ // for provisional load errors and navigation policy decisions. The former
+ // case handles well-formed URLs that can't be loaded, and the latter
+ // case handles malformed URLs and unknown schemes. Loading alternate content
+ // at other times behaves like a standard load.
+ DocumentLoader* compareDocumentLoader = 0;
+ if (policyChecker()->delegateIsDecidingNavigationPolicy() || policyChecker()->delegateIsHandlingUnimplementablePolicy())
+ compareDocumentLoader = m_policyDocumentLoader.get();
+ else if (m_delegateIsHandlingProvisionalLoadError)
+ compareDocumentLoader = m_provisionalDocumentLoader.get();
+
+ return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url();
+}
+
+void FrameLoader::reloadWithOverrideEncoding(const String& encoding)
+{
+ if (!m_documentLoader)
+ return;
+
+ ResourceRequest request = m_documentLoader->request();
+ KURL unreachableURL = m_documentLoader->unreachableURL();
+ if (!unreachableURL.isEmpty())
+ request.setURL(unreachableURL);
+
+ request.setCachePolicy(ReturnCacheDataElseLoad);
+
+ RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
+ setPolicyDocumentLoader(loader.get());
+
+ loader->setOverrideEncoding(encoding);
+
+ loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0);
+}
+
+void FrameLoader::reload(bool endToEndReload)
+{
+ if (!m_documentLoader)
+ return;
+
+ // If a window is created by javascript, its main frame can have an empty but non-nil URL.
+ // Reloading in this case will lose the current contents (see 4151001).
+ if (m_documentLoader->request().url().isEmpty())
+ return;
+
+ ResourceRequest initialRequest = m_documentLoader->request();
+
+ // Replace error-page URL with the URL we were trying to reach.
+ KURL unreachableURL = m_documentLoader->unreachableURL();
+ if (!unreachableURL.isEmpty())
+ initialRequest.setURL(unreachableURL);
+
+ // Create a new document loader for the reload, this will become m_documentLoader eventually,
+ // but first it has to be the "policy" document loader, and then the "provisional" document loader.
+ RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData());
+
+ ResourceRequest& request = loader->request();
+
+ // FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment.
+ request.setCachePolicy(ReloadIgnoringCacheData);
+
+ // If we're about to re-post, set up action so the application can warn the user.
+ if (request.httpMethod() == "POST")
+ loader->setTriggeringAction(NavigationAction(request.url(), NavigationTypeFormResubmitted));
+
+ loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
+
+ loadWithDocumentLoader(loader.get(), endToEndReload ? FrameLoadTypeReloadFromOrigin : FrameLoadTypeReload, 0);
+}
+
+static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame)
+{
+ // targetFrame can be NULL when we're trying to navigate a top-level frame
+ // that has a NULL opener.
+ if (!targetFrame)
+ return false;
+
+ const bool isLocalActiveOrigin = activeSecurityOrigin->isLocal();
+ for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) {
+ Document* ancestorDocument = ancestorFrame->document();
+ if (!ancestorDocument)
+ return true;
+
+ const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin();
+ if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin))
+ return true;
+
+ // Allow file URL descendant navigation even when allowFileAccessFromFileURLs is false.
+ if (isLocalActiveOrigin && ancestorSecurityOrigin->isLocal())
+ return true;
+ }
+
+ return false;
+}
+
+bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const
+{
+ // The navigation change is safe if the active frame is:
+ // - in the same security origin as the target or one of the target's
+ // ancestors.
+ //
+ // Or the target frame is:
+ // - a top-level frame in the frame hierarchy and the active frame can
+ // navigate the target frame's opener per above or it is the opener of
+ // the target frame.
+
+ if (!targetFrame)
+ return true;
+
+ // Performance optimization.
+ if (m_frame == targetFrame)
+ return true;
+
+ // 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 (!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;
+
+ Document* activeDocument = m_frame->document();
+ ASSERT(activeDocument);
+ const SecurityOrigin* activeSecurityOrigin = activeDocument->securityOrigin();
+
+ // For top-level windows, check the opener.
+ if (!targetFrame->tree()->parent() && canAccessAncestor(activeSecurityOrigin, targetFrame->loader()->opener()))
+ return true;
+
+ // In general, check the frame's ancestors.
+ if (canAccessAncestor(activeSecurityOrigin, targetFrame))
+ return true;
+
+ Settings* settings = targetFrame->settings();
+ if (settings && !settings->privateBrowsingEnabled()) {
+ Document* targetDocument = targetFrame->document();
+ // FIXME: this error message should contain more specifics of why the navigation change is not allowed.
+ String message = 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());
+ }
+
+ return false;
+}
+
+void FrameLoader::stopLoadingSubframes()
+{
+ for (RefPtr<Frame> child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
+ child->loader()->stopAllLoaders();
+}
+
+void FrameLoader::stopAllLoaders(DatabasePolicy databasePolicy)
+{
+ ASSERT(!m_frame->document() || !m_frame->document()->inPageCache());
+ if (m_pageDismissalEventBeingDispatched)
+ return;
+
+ // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this.
+ if (m_inStopAllLoaders)
+ return;
+
+ m_inStopAllLoaders = true;
+
+ policyChecker()->stopCheck();
+
+ stopLoadingSubframes();
+ if (m_provisionalDocumentLoader)
+ m_provisionalDocumentLoader->stopLoading(databasePolicy);
+ if (m_documentLoader)
+ m_documentLoader->stopLoading(databasePolicy);
+
+ setProvisionalDocumentLoader(0);
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ if (m_documentLoader)
+ m_documentLoader->clearArchiveResources();
+#endif
+
+ m_checkTimer.stop();
+
+ m_inStopAllLoaders = false;
+}
+
+void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete)
+{
+ stopAllLoaders();
+
+ if (deferCheckLoadComplete)
+ scheduleCheckLoadComplete();
+ else if (m_frame->page())
+ checkLoadComplete();
+}
+
+DocumentLoader* FrameLoader::activeDocumentLoader() const
+{
+ if (m_state == FrameStateProvisional)
+ return m_provisionalDocumentLoader.get();
+ return m_documentLoader.get();
+}
+
+bool FrameLoader::isLoading() const
+{
+ DocumentLoader* docLoader = activeDocumentLoader();
+ if (!docLoader)
+ return false;
+ return docLoader->isLoadingMainResource() || docLoader->isLoadingSubresources() || docLoader->isLoadingPlugIns();
+}
+
+bool FrameLoader::frameHasLoaded() const
+{
+ return m_stateMachine.committedFirstRealDocumentLoad() || (m_provisionalDocumentLoader && !m_stateMachine.creatingInitialEmptyDocument());
+}
+
+void FrameLoader::transferLoadingResourcesFromPage(Page* oldPage)
+{
+ ASSERT(oldPage != m_frame->page());
+ if (isLoading()) {
+ activeDocumentLoader()->transferLoadingResourcesFromPage(oldPage);
+ oldPage->progress()->progressCompleted(m_frame);
+ if (m_frame->page())
+ m_frame->page()->progress()->progressStarted(m_frame);
+ }
+}
+
+void FrameLoader::dispatchTransferLoadingResourceFromPage(unsigned long identifier, DocumentLoader* docLoader, const ResourceRequest& request, Page* oldPage)
+{
+ notifier()->dispatchTransferLoadingResourceFromPage(identifier, docLoader, request, oldPage);
+}
+
+void FrameLoader::setDocumentLoader(DocumentLoader* loader)
+{
+ if (!loader && !m_documentLoader)
+ return;
+
+ ASSERT(loader != m_documentLoader);
+ ASSERT(!loader || loader->frameLoader() == this);
+
+ m_client->prepareForDataSourceReplacement();
+ detachChildren();
+ if (m_documentLoader)
+ m_documentLoader->detachFromFrame();
+
+ m_documentLoader = loader;
+}
+
+void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader)
+{
+ if (m_policyDocumentLoader == loader)
+ return;
+
+ ASSERT(m_frame);
+ if (loader)
+ loader->setFrame(m_frame);
+ if (m_policyDocumentLoader
+ && m_policyDocumentLoader != m_provisionalDocumentLoader
+ && m_policyDocumentLoader != m_documentLoader)
+ m_policyDocumentLoader->detachFromFrame();
+
+ m_policyDocumentLoader = loader;
+}
+
+void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader)
+{
+ ASSERT(!loader || !m_provisionalDocumentLoader);
+ ASSERT(!loader || loader->frameLoader() == this);
+
+ if (m_provisionalDocumentLoader && m_provisionalDocumentLoader != m_documentLoader)
+ m_provisionalDocumentLoader->detachFromFrame();
+
+ m_provisionalDocumentLoader = loader;
+}
+
+double FrameLoader::timeOfLastCompletedLoad()
+{
+ return storedTimeOfLastCompletedLoad;
+}
+
+void FrameLoader::setState(FrameState newState)
+{
+ m_state = newState;
+
+ if (newState == FrameStateProvisional)
+ provisionalLoadStarted();
+ else if (newState == FrameStateComplete) {
+ frameLoadCompleted();
+ storedTimeOfLastCompletedLoad = currentTime();
+ if (m_documentLoader)
+ m_documentLoader->stopRecordingResponses();
+ }
+}
+
+void FrameLoader::clearProvisionalLoad()
+{
+ setProvisionalDocumentLoader(0);
+ if (Page* page = m_frame->page())
+ page->progress()->progressCompleted(m_frame);
+ setState(FrameStateComplete);
+}
+
+void FrameLoader::markLoadComplete()
+{
+ setState(FrameStateComplete);
+}
+
+void FrameLoader::commitProvisionalLoad()
+{
+ 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()->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.
+ 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_stateMachine.creatingInitialEmptyDocument())
+ m_client->makeRepresentation(pdl.get());
+
+ transitionToCommitted(cachedPage);
+
+ if (pdl) {
+ // Check if the destination page is allowed to access the previous page's timing information.
+ RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(pdl->request().url());
+ m_documentLoader->timing()->hasSameOriginAsPreviousDocument = securityOrigin->canRequest(m_previousUrl);
+ }
+
+ // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's
+ // status has changed, if there was a redirect. The frame load delegate may have saved some state about
+ // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are
+ // just about to commit a new page, there cannot possibly be a pending redirect at this point.
+ if (m_sentRedirectNotification)
+ clientRedirectCancelledOrFinished(false);
+
+ 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();
+ if (url.isEmpty())
+ url = pdl->responseURL();
+ if (url.isEmpty())
+ url = blankURL();
+
+ didOpenURL(url);
+ }
+
+ 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();
+
+ if (m_loadingFromCachedPage) {
+ m_frame->document()->documentDidBecomeActive();
+
+ // Force a layout to update view size and thereby update scrollbars.
+ m_frame->view()->forceLayout();
+
+ const ResponseVector& responses = m_documentLoader->responses();
+ size_t count = responses.size();
+ for (size_t i = 0; i < count; i++) {
+ const ResourceResponse& response = responses[i];
+ // FIXME: If the WebKit client changes or cancels the request, this is not respected.
+ ResourceError error;
+ unsigned long identifier;
+ ResourceRequest request(response.url());
+ requestFromDelegate(request, identifier, error);
+ // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
+ // However, with today's computers and networking speeds, this won't happen in practice.
+ // Could be an issue with a giant local file.
+ notifier()->sendRemainingDelegateMessages(m_documentLoader.get(), identifier, response, static_cast<int>(response.expectedContentLength()), error);
+ }
+
+ pageCache()->remove(history()->currentItem());
+
+ m_documentLoader->setPrimaryLoadComplete(true);
+
+ // FIXME: Why only this frame and not parent frames?
+ checkLoadCompleteForThisFrame();
+ }
+}
+
+void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)
+{
+ ASSERT(m_client->hasWebView());
+ ASSERT(m_state == FrameStateProvisional);
+
+ if (m_state != FrameStateProvisional)
+ return;
+
+ m_client->setCopiesOnScroll();
+ history()->updateForCommit();
+
+ // The call to closeURL() invokes the unload event handler, which can execute arbitrary
+ // JavaScript. If the script initiates a new load, we need to abandon the current load,
+ // or the two will stomp each other.
+ DocumentLoader* pdl = m_provisionalDocumentLoader.get();
+ if (m_documentLoader)
+ closeURL();
+ if (pdl != m_provisionalDocumentLoader)
+ return;
+
+ // Nothing else can interupt this commit - set the Provisional->Committed transition in stone
+ if (m_documentLoader)
+ m_documentLoader->stopLoadingSubresources();
+ if (m_documentLoader)
+ m_documentLoader->stopLoadingPlugIns();
+
+ setDocumentLoader(m_provisionalDocumentLoader.get());
+ setProvisionalDocumentLoader(0);
+ setState(FrameStateCommittedPage);
+
+ // Handle adding the URL to the back/forward list.
+ DocumentLoader* dl = m_documentLoader.get();
+
+ switch (m_loadType) {
+ case FrameLoadTypeForward:
+ case FrameLoadTypeBack:
+ case FrameLoadTypeBackWMLDeckNotAccessible:
+ case FrameLoadTypeIndexedBackForward:
+ 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;
+
+ case FrameLoadTypeReload:
+ case FrameLoadTypeReloadFromOrigin:
+ case FrameLoadTypeSame:
+ case FrameLoadTypeReplace:
+ history()->updateForReload();
+ m_client->transitionToCommittedForNewPage();
+ break;
+
+ case FrameLoadTypeStandard:
+ history()->updateForStandardLoad();
+#ifndef BUILDING_ON_TIGER
+ // This code was originally added for a Leopard performance imporvement. We decided to
+ // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
+ if (m_frame->view())
+ m_frame->view()->setScrollbarsSuppressed(true);
+#endif
+ m_client->transitionToCommittedForNewPage();
+ break;
+
+ case FrameLoadTypeRedirectWithLockedBackForwardList:
+ history()->updateForRedirectWithLockedBackForwardList();
+ m_client->transitionToCommittedForNewPage();
+ break;
+
+ // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is).
+ // An exception should be thrown if we're in the FrameLoadTypeUninitialized state.
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ writer()->setMIMEType(dl->responseMIMEType());
+
+ // Tell the client we've committed this URL.
+ ASSERT(m_frame->view());
+
+ if (m_stateMachine.creatingInitialEmptyDocument())
+ return;
+
+ if (!m_stateMachine.committedFirstRealDocumentLoad())
+ m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit);
+
+ if (!m_client->hasHTMLView())
+ receivedFirstData();
+}
+
+void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress)
+{
+ // Note that -webView:didCancelClientRedirectForFrame: is called on the frame load delegate even if
+ // the redirect succeeded. We should either rename this API, or add a new method, like
+ // -webView:didFinishClientRedirectForFrame:
+ m_client->dispatchDidCancelClientRedirect();
+
+ if (!cancelWithLoadInProgress)
+ m_quickRedirectComing = false;
+
+ m_sentRedirectNotification = false;
+}
+
+void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireDate, bool lockBackForwardList)
+{
+ m_client->dispatchWillPerformClientRedirect(url, seconds, fireDate);
+
+ // Remember that we sent a redirect notification to the frame load delegate so that when we commit
+ // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame:
+ m_sentRedirectNotification = true;
+
+ // If a "quick" redirect comes in, we set a special mode so we treat the next
+ // load as part of the original navigation. If we don't have a document loader, we have
+ // no "original" load on which to base a redirect, so we treat the redirect as a normal load.
+ // Loads triggered by JavaScript form submissions never count as quick redirects.
+ m_quickRedirectComing = (lockBackForwardList || history()->currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction;
+}
+
+bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL)
+{
+#if ENABLE(WML)
+ // All WML decks are supposed to be reloaded, even within the same URL fragment
+ if (frameContainsWMLContent(m_frame))
+ return true;
+#endif
+
+ // This function implements the rule: "Don't reload if navigating by fragment within
+ // the same URL, but do reload if going to a new URL or to the same URL with no
+ // fragment identifier at all."
+ if (!destinationURL.hasFragmentIdentifier())
+ return true;
+ return !equalIgnoringFragmentIdentifier(currentURL, destinationURL);
+}
+
+void FrameLoader::closeOldDataSources()
+{
+ // FIXME: Is it important for this traversal to be postorder instead of preorder?
+ // If so, add helpers for postorder traversal, and use them. If not, then lets not
+ // use a recursive algorithm here.
+ for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
+ child->loader()->closeOldDataSources();
+
+ if (m_documentLoader)
+ m_client->dispatchWillClose();
+
+ m_client->setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers
+}
+
+void FrameLoader::prepareForCachedPageRestore()
+{
+ ASSERT(!m_frame->tree()->parent());
+ ASSERT(m_frame->page());
+ ASSERT(m_frame->page()->mainFrame() == m_frame);
+
+ 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(NotAboutToExecuteScript)) {
+ if (DOMWindow* window = m_frame->existingDOMWindow()) {
+ window->setStatus(String());
+ window->setDefaultStatus(String());
+ }
+ }
+}
+
+void FrameLoader::open(CachedFrameBase& cachedFrame)
+{
+ m_isComplete = false;
+
+ // Don't re-emit the load event.
+ m_didCallImplicitClose = true;
+
+ KURL url = cachedFrame.url();
+
+ if (url.protocolInHTTPFamily() && !url.host().isEmpty() && url.path().isEmpty())
+ url.setPath("/");
+
+ m_URL = url;
+ m_workingURL = url;
+
+ started();
+ clear(true, true, cachedFrame.isMainFrame());
+
+ Document* document = cachedFrame.document();
+ ASSERT(document);
+ document->setInPageCache(false);
+
+ m_needsClear = true;
+ m_isComplete = false;
+ m_didCallImplicitClose = false;
+ m_outgoingReferrer = url.string();
+
+ FrameView* view = cachedFrame.view();
+
+ // When navigating to a CachedFrame its FrameView should never be null. If it is we'll crash in creative ways downstream.
+ ASSERT(view);
+ view->setWasScrolledByUser(false);
+
+ // Use the current ScrollView's frame rect.
+ if (m_frame->view())
+ view->setFrameRect(m_frame->view()->frameRect());
+ m_frame->setView(view);
+
+ m_frame->setDocument(document);
+ m_frame->setDOMWindow(cachedFrame.domWindow());
+ m_frame->domWindow()->setURL(document->url());
+ m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());
+
+ writer()->setDecoder(document->decoder());
+
+ updateFirstPartyForCookies();
+
+ cachedFrame.restore();
+}
+
+bool FrameLoader::isStopping() const
+{
+ return activeDocumentLoader()->isStopping();
+}
+
+void FrameLoader::finishedLoading()
+{
+ // Retain because the stop may release the last reference to it.
+ RefPtr<Frame> protect(m_frame);
+
+ RefPtr<DocumentLoader> dl = activeDocumentLoader();
+ dl->finishedLoading();
+ if (!dl->mainDocumentError().isNull() || !dl->frameLoader())
+ return;
+ dl->setPrimaryLoadComplete(true);
+ m_client->dispatchDidLoadMainResource(dl.get());
+ checkLoadComplete();
+}
+
+bool FrameLoader::isHostedByObjectElement() const
+{
+ HTMLFrameOwnerElement* owner = m_frame->ownerElement();
+ return owner && owner->hasTagName(objectTag);
+}
+
+bool FrameLoader::isLoadingMainFrame() const
+{
+ Page* page = m_frame->page();
+ return page && m_frame == page->mainFrame();
+}
+
+bool FrameLoader::canShowMIMEType(const String& MIMEType) const
+{
+ return m_client->canShowMIMEType(MIMEType);
+}
+
+bool FrameLoader::representationExistsForURLScheme(const String& URLScheme)
+{
+ return m_client->representationExistsForURLScheme(URLScheme);
+}
+
+String FrameLoader::generatedMIMETypeForURLScheme(const String& URLScheme)
+{
+ return m_client->generatedMIMETypeForURLScheme(URLScheme);
+}
+
+void FrameLoader::didReceiveServerRedirectForProvisionalLoadForFrame()
+{
+ m_client->dispatchDidReceiveServerRedirectForProvisionalLoad();
+}
+
+void FrameLoader::finishedLoadingDocument(DocumentLoader* loader)
+{
+ // FIXME: Platforms shouldn't differ here!
+#if PLATFORM(WIN) || PLATFORM(CHROMIUM) || defined(ANDROID)
+ if (m_stateMachine.creatingInitialEmptyDocument())
+ return;
+#endif
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+
+ // Give archive machinery a crack at this document. If the MIME type is not an archive type, it will return 0.
+ RefPtr<Archive> archive = ArchiveFactory::create(loader->mainResourceData().get(), loader->responseMIMEType());
+ if (!archive) {
+ m_client->finishedLoading(loader);
+ return;
+ }
+
+ // FIXME: The remainder of this function should be moved to DocumentLoader.
+
+ loader->addAllArchiveResources(archive.get());
+
+ ArchiveResource* mainResource = archive->mainResource();
+ loader->setParsedArchiveData(mainResource->data());
+
+ writer()->setMIMEType(mainResource->mimeType());
+
+ closeURL();
+ didOpenURL(mainResource->url());
+
+ ASSERT(m_frame->document());
+ String userChosenEncoding = documentLoader()->overrideEncoding();
+ bool encodingIsUserChosen = !userChosenEncoding.isNull();
+ writer()->setEncoding(encodingIsUserChosen ? userChosenEncoding : mainResource->textEncoding(), encodingIsUserChosen);
+ writer()->addData(mainResource->data()->data(), mainResource->data()->size());
+#else
+ m_client->finishedLoading(loader);
+#endif // ARCHIVE
+}
+
+bool FrameLoader::isReplacing() const
+{
+ return m_loadType == FrameLoadTypeReplace;
+}
+
+void FrameLoader::setReplacing()
+{
+ m_loadType = FrameLoadTypeReplace;
+}
+
+void FrameLoader::revertToProvisional(DocumentLoader* loader)
+{
+ m_client->revertToProvisionalState(loader);
+}
+
+bool FrameLoader::subframeIsLoading() const
+{
+ // It's most likely that the last added frame is the last to load so we walk backwards.
+ for (Frame* child = m_frame->tree()->lastChild(); child; child = child->tree()->previousSibling()) {
+ FrameLoader* childLoader = child->loader();
+ DocumentLoader* documentLoader = childLoader->documentLoader();
+ if (documentLoader && documentLoader->isLoadingInAPISense())
+ return true;
+ documentLoader = childLoader->provisionalDocumentLoader();
+ if (documentLoader && documentLoader->isLoadingInAPISense())
+ return true;
+ documentLoader = childLoader->policyDocumentLoader();
+ if (documentLoader)
+ return true;
+ }
+ return false;
+}
+
+void FrameLoader::willChangeTitle(DocumentLoader* loader)
+{
+ m_client->willChangeTitle(loader);
+}
+
+FrameLoadType FrameLoader::loadType() const
+{
+ return m_loadType;
+}
+
+CachePolicy FrameLoader::subresourceCachePolicy() const
+{
+ if (m_isComplete)
+ return CachePolicyVerify;
+
+ if (m_loadType == FrameLoadTypeReloadFromOrigin)
+ return CachePolicyReload;
+
+ if (Frame* parentFrame = m_frame->tree()->parent()) {
+ CachePolicy parentCachePolicy = parentFrame->loader()->subresourceCachePolicy();
+ if (parentCachePolicy != CachePolicyVerify)
+ return parentCachePolicy;
+ }
+
+ // FIXME: POST documents are always Reloads, but their subresources should still be Revalidate.
+ // If we bring the CachePolicy.h and ResourceRequest cache policy enums in sync with each other and
+ // remember "Revalidate" in ResourceRequests, we can remove this "POST" check and return either "Reload"
+ // or "Revalidate" if the DocumentLoader was requested with either.
+ const ResourceRequest& request(documentLoader()->request());
+ if (request.cachePolicy() == ReloadIgnoringCacheData && !equalIgnoringCase(request.httpMethod(), "post"))
+ return CachePolicyRevalidate;
+
+ if (m_loadType == FrameLoadTypeReload)
+ return CachePolicyRevalidate;
+
+ if (request.cachePolicy() == ReturnCacheDataElseLoad)
+ return CachePolicyHistoryBuffer;
+
+ return CachePolicyVerify;
+}
+
+void FrameLoader::checkLoadCompleteForThisFrame()
+{
+ ASSERT(m_client->hasWebView());
+
+ switch (m_state) {
+ case FrameStateProvisional: {
+ if (m_delegateIsHandlingProvisionalLoadError)
+ return;
+
+ RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
+ if (!pdl)
+ return;
+
+ // If we've received any errors we may be stuck in the provisional state and actually complete.
+ const ResourceError& error = pdl->mainDocumentError();
+ if (error.isNull())
+ return;
+
+ // Check all children first.
+ RefPtr<HistoryItem> item;
+ if (Page* page = m_frame->page())
+ if (isBackForwardLoadType(loadType()) && m_frame == page->mainFrame())
+ item = history()->currentItem();
+
+ bool shouldReset = true;
+ if (!(pdl->isLoadingInAPISense() && !pdl->isStopping())) {
+ m_delegateIsHandlingProvisionalLoadError = true;
+ m_client->dispatchDidFailProvisionalLoad(error);
+ m_delegateIsHandlingProvisionalLoadError = false;
+
+ // FIXME: can stopping loading here possibly have any effect, if isLoading is false,
+ // which it must be to be in this branch of the if? And is it OK to just do a full-on
+ // stopAllLoaders instead of stopLoadingSubframes?
+ stopLoadingSubframes();
+ pdl->stopLoading();
+
+ // If we're in the middle of loading multipart data, we need to restore the document loader.
+ if (isReplacing() && !m_documentLoader.get())
+ setDocumentLoader(m_provisionalDocumentLoader.get());
+
+ // Finish resetting the load state, but only if another load hasn't been started by the
+ // delegate callback.
+ if (pdl == m_provisionalDocumentLoader)
+ clearProvisionalLoad();
+ else if (activeDocumentLoader()) {
+ KURL unreachableURL = activeDocumentLoader()->unreachableURL();
+ if (!unreachableURL.isEmpty() && unreachableURL == pdl->request().url())
+ shouldReset = false;
+ }
+ }
+ if (shouldReset && item)
+ if (Page* page = m_frame->page()) {
+ page->backForward()->setCurrentItem(item.get());
+ Settings* settings = m_frame->settings();
+ page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : item.get());
+ }
+ return;
+ }
+
+ case FrameStateCommittedPage: {
+ DocumentLoader* dl = m_documentLoader.get();
+ if (!dl || (dl->isLoadingInAPISense() && !dl->isStopping()))
+ return;
+
+ markLoadComplete();
+
+ // FIXME: Is this subsequent work important if we already navigated away?
+ // Maybe there are bugs because of that, or extra work we can skip because
+ // the new page is ready.
+
+ m_client->forceLayoutForNonHTML();
+
+ // If the user had a scroll point, scroll to it, overriding the anchor point if any.
+ if (m_frame->page()) {
+ if (isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin)
+ history()->restoreScrollPositionAndViewState();
+ }
+
+ if (m_stateMachine.creatingInitialEmptyDocument() || !m_stateMachine.committedFirstRealDocumentLoad())
+ return;
+
+ const ResourceError& error = dl->mainDocumentError();
+ if (!error.isNull())
+ m_client->dispatchDidFailLoad(error);
+ else
+ m_client->dispatchDidFinishLoad();
+
+ if (Page* page = m_frame->page())
+ page->progress()->progressCompleted(m_frame);
+
+#ifdef ANDROID_INSTRUMENT
+ if (!m_frame->tree()->parent() && m_frame->document()->renderArena())
+ android::TimeCounter::report(m_URL, cache()->getLiveSize(), cache()->getDeadSize(),
+ m_frame->document()->renderArena()->reportPoolSize());
+#endif
+ return;
+ }
+
+ case FrameStateComplete:
+ frameLoadCompleted();
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+void FrameLoader::continueLoadAfterWillSubmitForm()
+{
+ if (!m_provisionalDocumentLoader)
+ return;
+
+ // DocumentLoader calls back to our prepareForLoadStart
+ m_provisionalDocumentLoader->prepareForLoadStart();
+
+ // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader,
+ // so we need to null check it again.
+ if (!m_provisionalDocumentLoader)
+ return;
+
+ DocumentLoader* activeDocLoader = activeDocumentLoader();
+ if (activeDocLoader && activeDocLoader->isLoadingMainResource())
+ return;
+
+ m_loadingFromCachedPage = false;
+
+ unsigned long identifier = 0;
+
+ if (Page* page = m_frame->page()) {
+ identifier = page->progress()->createUniqueIdentifier();
+ 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 (m_frame->page() && isBackForwardLoadType(m_loadType))
+ history()->restoreScrollPositionAndViewState();
+
+ if (m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone())
+ m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone);
+ m_client->dispatchDidFirstLayout();
+}
+
+void FrameLoader::didFirstVisuallyNonEmptyLayout()
+{
+ m_client->dispatchDidFirstVisuallyNonEmptyLayout();
+}
+
+void FrameLoader::frameLoadCompleted()
+{
+ // Note: Can be called multiple times.
+
+ m_client->frameLoadCompleted();
+
+ history()->updateForFrameLoadCompleted();
+
+ // After a canceled provisional load, firstLayoutDone is false.
+ // Reset it to true if we're displaying a page.
+ if (m_documentLoader && m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone())
+ m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone);
+}
+
+void FrameLoader::detachChildren()
+{
+ // FIXME: Is it really necessary to do this in reverse order?
+ Frame* previous;
+ for (Frame* child = m_frame->tree()->lastChild(); child; child = previous) {
+ previous = child->tree()->previousSibling();
+ child->loader()->detachFromParent();
+ }
+}
+
+void FrameLoader::closeAndRemoveChild(Frame* child)
+{
+ child->tree()->detachFromParent();
+
+ 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);
+}
+
+void FrameLoader::recursiveCheckLoadComplete()
+{
+ Vector<RefPtr<Frame>, 10> frames;
+
+ for (RefPtr<Frame> frame = m_frame->tree()->firstChild(); frame; frame = frame->tree()->nextSibling())
+ frames.append(frame);
+
+ unsigned size = frames.size();
+ for (unsigned i = 0; i < size; i++)
+ frames[i]->loader()->recursiveCheckLoadComplete();
+
+ checkLoadCompleteForThisFrame();
+}
+
+// Called every time a resource is completely loaded, or an error is received.
+void FrameLoader::checkLoadComplete()
+{
+ ASSERT(m_client->hasWebView());
+
+ m_shouldCallCheckLoadComplete = false;
+
+ // FIXME: Always traversing the entire frame tree is a bit inefficient, but
+ // is currently needed in order to null out the previous history item for all frames.
+ if (Page* page = m_frame->page())
+ page->mainFrame()->loader()->recursiveCheckLoadComplete();
+}
+
+int FrameLoader::numPendingOrLoadingRequests(bool recurse) const
+{
+ if (!recurse)
+ return numRequests(m_frame->document());
+
+ int count = 0;
+ for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame))
+ count += numRequests(frame->document());
+ return count;
+}
+
+String FrameLoader::userAgent(const KURL& url) const
+{
+ return m_client->userAgent(url);
+}
+
+void FrameLoader::handledOnloadEvents()
+{
+ m_client->dispatchDidHandleOnloadEvents();
+
+ if (documentLoader()) {
+ documentLoader()->handledOnloadEvents();
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ documentLoader()->applicationCacheHost()->stopDeferringEvents();
+#endif
+ }
+}
+
+void FrameLoader::frameDetached()
+{
+ stopAllLoaders();
+ m_frame->document()->stopActiveDOMObjects();
+ detachFromParent();
+}
+
+void FrameLoader::detachFromParent()
+{
+ RefPtr<Frame> protect(m_frame);
+
+ closeURL();
+ 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())
+ page->inspectorController()->frameDetachedFromParent(m_frame);
+#endif
+
+ detachViewsAndDocumentLoader();
+
+ if (Frame* parent = m_frame->tree()->parent()) {
+ parent->loader()->closeAndRemoveChild(m_frame);
+ 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();
+ }
+}
+
+void FrameLoader::detachViewsAndDocumentLoader()
+{
+ m_client->detachedFromParent2();
+ setDocumentLoader(0);
+ m_client->detachedFromParent3();
+}
+
+void FrameLoader::addExtraFieldsToSubresourceRequest(ResourceRequest& request)
+{
+ addExtraFieldsToRequest(request, m_loadType, false, false);
+}
+
+void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request)
+{
+ addExtraFieldsToRequest(request, m_loadType, true, false);
+}
+
+void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource, bool cookiePolicyURLFromRequest)
+{
+ // Don't set the cookie policy URL if it's already been set.
+ // But make sure to set it on all requests, as it has significance beyond the cookie policy for all protocols (<rdar://problem/6616664>).
+ if (request.firstPartyForCookies().isEmpty()) {
+ if (mainResource && (isLoadingMainFrame() || cookiePolicyURLFromRequest))
+ request.setFirstPartyForCookies(request.url());
+ else if (Document* document = m_frame->document())
+ request.setFirstPartyForCookies(document->firstPartyForCookies());
+ }
+
+ // The remaining modifications are only necessary for HTTP and HTTPS.
+ if (!request.url().isEmpty() && !request.url().protocolInHTTPFamily())
+ return;
+
+ applyUserAgent(request);
+
+ // If 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);
+ 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);
+
+ // Make sure we send the Origin header.
+ addHTTPOriginIfNeeded(request, String());
+
+ // Always try UTF-8. If that fails, try frame encoding (if any) and then the default.
+ // For a newly opened frame with an empty URL, encoding() should not be used, because this methods asks decoder, which uses ISO-8859-1.
+ Settings* settings = m_frame->settings();
+ request.setResponseContentDispositionEncodingFallbackArray("UTF-8", writer()->deprecatedFrameEncoding(), settings ? settings->defaultTextEncodingName() : String());
+}
+
+void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin)
+{
+ if (!request.httpOrigin().isEmpty())
+ return; // Request already has an Origin header.
+
+ // Don't send an Origin header for GET or HEAD to avoid privacy issues.
+ // For example, if an intranet page has a hyperlink to an external web
+ // site, we don't want to include the Origin of the request because it
+ // will leak the internal host name. Similar privacy concerns have lead
+ // to the widespread suppression of the Referer header at the network
+ // layer.
+ if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD")
+ return;
+
+ // For non-GET and non-HEAD methods, always send an Origin header so the
+ // server knows we support this feature.
+
+ if (origin.isEmpty()) {
+ // If we don't know what origin header to attach, we attach the value
+ // for an empty origin.
+ origin = SecurityOrigin::createEmpty()->toString();
+ }
+
+ request.setHTTPOrigin(origin);
+}
+
+void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState)
+{
+ RefPtr<FormState> formState = prpFormState;
+
+ // When posting, use the NSURLRequestReloadIgnoringCacheData load flag.
+ // This prevents a potential bug which may cause a page with a form that uses itself
+ // as an action to be returned from the cache without submitting.
+
+ // FIXME: Where's the code that implements what the comment above says?
+
+ // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a
+ // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case
+ // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest
+ // from scratch as it did all along.
+ const KURL& url = inRequest.url();
+ RefPtr<FormData> formData = inRequest.httpBody();
+ const String& contentType = inRequest.httpContentType();
+ String origin = inRequest.httpOrigin();
+
+ ResourceRequest workingResourceRequest(url);
+
+ if (!referrer.isEmpty())
+ workingResourceRequest.setHTTPReferrer(referrer);
+ workingResourceRequest.setHTTPOrigin(origin);
+ workingResourceRequest.setHTTPMethod("POST");
+ workingResourceRequest.setHTTPBody(formData);
+ workingResourceRequest.setHTTPContentType(contentType);
+ addExtraFieldsToRequest(workingResourceRequest, loadType, true, true);
+
+ NavigationAction action(url, loadType, true, event);
+
+ if (!frameName.isEmpty()) {
+ // The search for a target frame is done earlier in the case of form submission.
+ if (Frame* targetFrame = formState ? 0 : findFrameForNavigation(frameName))
+ targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());
+ else
+ policyChecker()->checkNewWindowPolicy(action, FrameLoader::callContinueLoadAfterNewWindowPolicy, workingResourceRequest, formState.release(), frameName, this);
+ } else
+ loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());
+}
+
+unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
+{
+ String referrer = m_outgoingReferrer;
+ if (SecurityOrigin::shouldHideReferrer(request.url(), referrer))
+ referrer = String();
+
+ ResourceRequest initialRequest = request;
+ initialRequest.setTimeoutInterval(10);
+
+ if (!referrer.isEmpty())
+ initialRequest.setHTTPReferrer(referrer);
+ addHTTPOriginIfNeeded(initialRequest, outgoingOrigin());
+
+ 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);
+ requestFromDelegate(newRequest, identifier, error);
+
+ if (error.isNull()) {
+ ASSERT(!newRequest.isNull());
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (!documentLoader()->applicationCacheHost()->maybeLoadSynchronously(newRequest, error, response, data)) {
+#endif
+ ResourceHandle::loadResourceSynchronously(networkingContext(), newRequest, storedCredentials, error, response, data);
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ documentLoader()->applicationCacheHost()->maybeLoadFallbackSynchronously(newRequest, error, response, data);
+ }
+#endif
+ }
+
+ notifier()->sendRemainingDelegateMessages(m_documentLoader.get(), identifier, response, data.size(), error);
+ return identifier;
+}
+
+const ResourceRequest& FrameLoader::originalRequest() const
+{
+ return activeDocumentLoader()->originalRequestCopy();
+}
+
+void FrameLoader::receivedMainResourceError(const ResourceError& error, bool isComplete)
+{
+ // Retain because the stop may release the last reference to it.
+ RefPtr<Frame> protect(m_frame);
+
+ RefPtr<DocumentLoader> loader = activeDocumentLoader();
+
+ if (isComplete) {
+ // FIXME: Don't want to do this if an entirely new load is going, so should check
+ // that both data sources on the frame are either this or nil.
+ stop();
+ if (m_client->shouldFallBack(error))
+ handleFallbackContent();
+ }
+
+ if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) {
+ if (m_submittedFormURL == m_provisionalDocumentLoader->originalRequestCopy().url())
+ m_submittedFormURL = KURL();
+
+ // We might have made a page cache item, but now we're bailing out due to an error before we ever
+ // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state
+ // so that the existing view (that wenever got far enough to replace) can continue being used.
+ history()->invalidateCurrentItemCachedPage();
+
+ // Call clientRedirectCancelledOrFinished here so that the frame load delegate is notified that the redirect's
+ // status has changed, if there was a redirect. The frame load delegate may have saved some state about
+ // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are definitely
+ // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect
+ // has ended.
+ if (m_sentRedirectNotification)
+ clientRedirectCancelledOrFinished(false);
+ }
+
+ loader->mainReceivedError(error, isComplete);
+}
+
+void FrameLoader::callContinueFragmentScrollAfterNavigationPolicy(void* argument,
+ const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
+{
+ FrameLoader* loader = static_cast<FrameLoader*>(argument);
+ loader->continueFragmentScrollAfterNavigationPolicy(request, shouldContinue);
+}
+
+void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
+{
+ m_quickRedirectComing = false;
+
+ if (!shouldContinue)
+ return;
+
+ bool isRedirect = m_quickRedirectComing || policyChecker()->loadType() == FrameLoadTypeRedirectWithLockedBackForwardList;
+ loadInSameDocument(request.url(), 0, !isRedirect);
+}
+
+bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, const String& httpMethod, FrameLoadType loadType, const KURL& url)
+{
+ // Should we do anchor navigation within the existing content?
+
+ // We don't do this if we are submitting a form with method other than "GET", explicitly reloading,
+ // currently displaying a frameset, or if the URL does not have a fragment.
+ // These rules were originally based on what KHTML was doing in KHTMLPart::openURL.
+
+ // FIXME: What about load types other than Standard and Reload?
+
+ return (!isFormSubmission || equalIgnoringCase(httpMethod, "GET"))
+ && loadType != FrameLoadTypeReload
+ && loadType != FrameLoadTypeReloadFromOrigin
+ && loadType != FrameLoadTypeSame
+ && !shouldReload(this->url(), url)
+ // We don't want to just scroll if a link from within a
+ // frameset is trying to reload the frameset into _top.
+ && !m_frame->document()->isFrameSet();
+}
+
+void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,
+ const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
+{
+ FrameLoader* loader = static_cast<FrameLoader*>(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;
+
+ // Store all references to each subframe in advance since beforeunload's event handler may modify frame
+ Vector<RefPtr<Frame> > targetFrames;
+ targetFrames.append(m_frame);
+ for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->traverseNext(m_frame))
+ targetFrames.append(child);
+
+ bool shouldClose = false;
+ {
+ NavigationDisablerForBeforeUnload navigationDisabler;
+ size_t i;
+
+ for (i = 0; i < targetFrames.size(); i++) {
+ if (!targetFrames[i]->tree()->isDescendantOf(m_frame))
+ continue;
+ if (!targetFrames[i]->loader()->fireBeforeUnloadEvent(chrome))
+ break;
+ }
+
+ if (i == targetFrames.size())
+ shouldClose = true;
+ }
+
+ return shouldClose;
+}
+
+bool FrameLoader::fireBeforeUnloadEvent(Chrome* chrome)
+{
+ DOMWindow* domWindow = m_frame->existingDOMWindow();
+ if (!domWindow)
+ return true;
+
+ RefPtr<Document> document = m_frame->document();
+ if (!document->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
+ // nil policyDataSource because loading the alternate page will have passed
+ // through this method already, nested; otherwise, policyDataSource should still be set.
+ ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty());
+
+ bool isTargetItem = history()->provisionalItem() ? history()->provisionalItem()->isTargetItem() : false;
+
+ // Two reasons we can't continue:
+ // 1) Navigation policy delegate said we can't so request is nil. A primary case of this
+ // 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.
+ bool canContinue = shouldContinue && shouldClose();
+
+ if (!canContinue) {
+ // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we
+ // need to report that the client redirect was cancelled.
+ if (m_quickRedirectComing)
+ clientRedirectCancelledOrFinished(false);
+
+ setPolicyDocumentLoader(0);
+
+ // If the navigation request came from the back/forward menu, and we punt on it, we have the
+ // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
+ // we only do this when punting a navigation for the target frame or top-level frame.
+ if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(policyChecker()->loadType()))
+ if (Page* page = m_frame->page()) {
+ Frame* mainFrame = page->mainFrame();
+ if (HistoryItem* resetItem = mainFrame->loader()->history()->currentItem()) {
+ page->backForward()->setCurrentItem(resetItem);
+ Settings* settings = m_frame->settings();
+ page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : resetItem);
+ }
+ }
+ return;
+ }
+
+ FrameLoadType type = policyChecker()->loadType();
+ stopAllLoaders();
+
+ // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders()
+ // might detach the current FrameLoader, in which case we should bail on this newly defunct load.
+ if (!m_frame->page())
+ return;
+
+#if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR) && USE(JSC)
+ if (Page* page = m_frame->page()) {
+ if (page->mainFrame() == m_frame)
+ page->inspectorController()->resume();
+ }
+#endif
+
+ setProvisionalDocumentLoader(m_policyDocumentLoader.get());
+ m_loadType = type;
+ setState(FrameStateProvisional);
+
+ setPolicyDocumentLoader(0);
+
+ if (isBackForwardLoadType(type) && history()->provisionalItem()->isInPageCache()) {
+ loadProvisionalItemFromCachedPage();
+ return;
+ }
+
+ if (formState)
+ m_client->dispatchWillSubmitForm(&PolicyChecker::continueLoadAfterWillSubmitForm, formState);
+ else
+ continueLoadAfterWillSubmitForm();
+}
+
+void FrameLoader::callContinueLoadAfterNewWindowPolicy(void* argument,
+ 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, action, shouldContinue);
+}
+
+void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request,
+ 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(action);
+ if (!mainFrame)
+ return;
+
+ if (frameName != "_blank")
+ mainFrame->tree()->setName(frameName);
+
+ mainFrame->page()->setOpenedByDOM();
+ mainFrame->loader()->m_client->dispatchShow();
+ if (!m_suppressOpenerInNewFrame)
+ mainFrame->loader()->setOpener(frame.get());
+ mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), false, FrameLoadTypeStandard, formState);
+}
+
+void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error)
+{
+ ASSERT(!request.isNull());
+
+ identifier = 0;
+ if (Page* page = m_frame->page()) {
+ identifier = page->progress()->createUniqueIdentifier();
+ notifier()->assignIdentifierToInitialRequest(identifier, m_documentLoader.get(), request);
+ }
+
+ ResourceRequest newRequest(request);
+ notifier()->dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse());
+
+ if (newRequest.isNull())
+ error = cancelledError(request);
+ else
+ error = ResourceError();
+
+ request = newRequest;
+}
+
+void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource)
+{
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+}
+
+void FrameLoader::applyUserAgent(ResourceRequest& request)
+{
+ String userAgent = client()->userAgent(request.url());
+ ASSERT(!userAgent.isNull());
+ request.setHTTPUserAgent(userAgent);
+}
+
+bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const KURL& url)
+{
+ Frame* topFrame = m_frame->tree()->top();
+ if (m_frame == topFrame)
+ return false;
+
+ if (equalIgnoringCase(content, "deny"))
+ return true;
+
+ if (equalIgnoringCase(content, "sameorigin")) {
+ RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
+ if (!origin->isSameSchemeHostPort(topFrame->document()->securityOrigin()))
+ return true;
+ }
+
+ return false;
+}
+
+void FrameLoader::loadProvisionalItemFromCachedPage()
+{
+ DocumentLoader* provisionalLoader = provisionalDocumentLoader();
+ LOG(PageCache, "WebCorePageCache: Loading provisional DocumentLoader %p with URL '%s' from CachedPage", provisionalDocumentLoader(), provisionalDocumentLoader()->url().string().utf8().data());
+
+ provisionalLoader->prepareForLoadStart();
+
+ m_loadingFromCachedPage = true;
+
+ // Should have timing data from previous time(s) the page was shown.
+ ASSERT(provisionalLoader->timing()->navigationStart);
+ provisionalLoader->resetTiming();
+ provisionalLoader->timing()->navigationStart = currentTime();
+
+ provisionalLoader->setCommitted(true);
+ commitProvisionalLoad();
+}
+
+bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& url) const
+{
+ if (!history()->currentItem())
+ return false;
+ return url == history()->currentItem()->url() || url == history()->currentItem()->originalURL();
+}
+
+void FrameLoader::checkDidPerformFirstNavigation()
+{
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ if (!m_didPerformFirstNavigation && page->backForward()->currentItem() && !page->backForward()->backItem() && !page->backForward()->forwardItem()) {
+ m_didPerformFirstNavigation = true;
+ m_client->didPerformFirstNavigation();
+ }
+}
+
+Frame* FrameLoader::findFrameForNavigation(const AtomicString& name)
+{
+ Frame* frame = m_frame->tree()->find(name);
+ if (!shouldAllowNavigation(frame))
+ return 0;
+ return frame;
+}
+
+void FrameLoader::loadSameDocumentItem(HistoryItem* item)
+{
+ ASSERT(item->documentSequenceNumber() == history()->currentItem()->documentSequenceNumber());
+
+ // Save user view state to the current history item here since we don't do a normal load.
+ // FIXME: Does form state need to be saved here too?
+ history()->saveScrollPositionAndViewStateToItem(history()->currentItem());
+ if (FrameView* view = m_frame->view())
+ view->setWasScrolledByUser(false);
+
+ history()->setCurrentItem(item);
+
+ // loadInSameDocument() actually changes the URL and notifies load delegates of a "fake" load
+ loadInSameDocument(item->url(), item->stateObject(), false);
+
+ // Restore user view state from the current history item here since we don't do a normal load.
+ history()->restoreScrollPositionAndViewState();
+}
+
+// FIXME: This function should really be split into a couple pieces, some of
+// which should be methods of HistoryController and some of which should be
+// methods of FrameLoader.
+void FrameLoader::loadDifferentDocumentItem(HistoryItem* item, FrameLoadType loadType)
+{
+ // Remember this item so we can traverse any child items as child frames load
+ history()->setProvisionalItem(item);
+
+ if (CachedPage* cachedPage = pageCache()->get(item)) {
+ loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0);
+ return;
+ }
+
+ KURL itemURL = item->url();
+ KURL itemOriginalURL = item->originalURL();
+ KURL currentURL;
+ if (documentLoader())
+ currentURL = documentLoader()->url();
+ RefPtr<FormData> formData = item->formData();
+
+ bool addedExtraFields = false;
+ ResourceRequest request(itemURL);
+
+ if (!item->referrer().isNull())
+ request.setHTTPReferrer(item->referrer());
+
+ // If this was a repost that failed the page cache, we might try to repost the form.
+ NavigationAction action;
+ if (formData) {
+ formData->generateFiles(m_frame->document());
+
+ request.setHTTPMethod("POST");
+ request.setHTTPBody(formData);
+ request.setHTTPContentType(item->formContentType());
+ RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer());
+ addHTTPOriginIfNeeded(request, securityOrigin->toString());
+
+ // Make sure to add extra fields to the request after the Origin header is added for the FormData case.
+ // See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion.
+ addExtraFieldsToRequest(request, m_loadType, true, formData);
+ addedExtraFields = true;
+
+ // FIXME: Slight hack to test if the NSURL cache contains the page we're going to.
+ // We want to know this before talking to the policy delegate, since it affects whether
+ // we show the DoYouReallyWantToRepost nag.
+ //
+ // This trick has a small bug (3123893) where we might find a cache hit, but then
+ // have the item vanish when we try to use it in the ensuing nav. This should be
+ // extremely rare, but in that case the user will get an error on the navigation.
+
+ if (ResourceHandle::willLoadFromCache(request, m_frame))
+ action = NavigationAction(itemURL, loadType, false);
+ else {
+ request.setCachePolicy(ReloadIgnoringCacheData);
+ action = NavigationAction(itemURL, NavigationTypeFormResubmitted);
+ }
+ } else {
+ switch (loadType) {
+ case FrameLoadTypeReload:
+ case FrameLoadTypeReloadFromOrigin:
+ request.setCachePolicy(ReloadIgnoringCacheData);
+ break;
+ case FrameLoadTypeBack:
+ case FrameLoadTypeBackWMLDeckNotAccessible:
+ case FrameLoadTypeForward:
+ case FrameLoadTypeIndexedBackForward:
+ // 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:
+ case FrameLoadTypeRedirectWithLockedBackForwardList:
+ break;
+ case FrameLoadTypeSame:
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ action = NavigationAction(itemOriginalURL, loadType, false);
+ }
+
+ if (!addedExtraFields)
+ addExtraFieldsToRequest(request, m_loadType, true, formData);
+
+ loadWithNavigationAction(request, action, false, loadType, 0);
+}
+
+// Loads content into this frame, as specified by history item
+void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
+{
+ HistoryItem* currentItem = history()->currentItem();
+ 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
+ // FIXME: Why do WML decks have this different behavior?
+ // Are WML decks incompatible with HTML5 pushState/replaceState which require inter-document history navigations?
+ // Should this new API be disabled for WML pages, or does WML need to update their mechanism to act like normal loads?
+ // If scroll-to-anchor navigations were broken for WML and required them to have different loading behavior, then
+ // state object loads are certainly also broken for them.
+ if (frameContainsWMLContent(m_frame))
+ sameDocumentNavigation = false;
+#endif
+
+ if (sameDocumentNavigation)
+ loadSameDocumentItem(item);
+ else
+ loadDifferentDocumentItem(item, loadType);
+}
+
+void FrameLoader::setMainDocumentError(DocumentLoader* loader, const ResourceError& error)
+{
+ m_client->setMainDocumentError(loader, error);
+}
+
+void FrameLoader::mainReceivedCompleteError(DocumentLoader* loader, const ResourceError&)
+{
+ loader->setPrimaryLoadComplete(true);
+ m_client->dispatchDidLoadMainResource(activeDocumentLoader());
+ checkCompleted();
+ if (m_frame->page())
+ checkLoadComplete();
+}
+
+void FrameLoader::mainReceivedError(const ResourceError& error, bool isComplete)
+{
+ activeDocumentLoader()->mainReceivedError(error, isComplete);
+}
+
+ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const
+{
+ ResourceError error = m_client->cancelledError(request);
+ error.setIsCancellation(true);
+ return error;
+}
+
+ResourceError FrameLoader::blockedError(const ResourceRequest& request) const
+{
+ return m_client->blockedError(request);
+}
+
+ResourceError FrameLoader::cannotShowURLError(const ResourceRequest& request) const
+{
+ return m_client->cannotShowURLError(request);
+}
+
+ResourceError FrameLoader::interruptionForPolicyChangeError(const ResourceRequest& request) const
+{
+ return m_client->interruptForPolicyChangeError(request);
+}
+
+ResourceError FrameLoader::fileDoesNotExistError(const ResourceResponse& response) const
+{
+ return m_client->fileDoesNotExistError(response);
+}
+
+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();
+}
+
+String FrameLoader::referrer() const
+{
+ return m_documentLoader ? m_documentLoader->request().httpReferrer() : "";
+}
+
+void FrameLoader::dispatchDocumentElementAvailable()
+{
+ m_frame->injectUserScripts(InjectAtDocumentStart);
+ m_client->documentElementAvailable();
+}
+
+void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds()
+{
+ if (!m_frame->script()->canExecuteScripts(NotAboutToExecuteScript))
+ return;
+
+ Vector<DOMWrapperWorld*> worlds;
+ ScriptController::getAllWorlds(worlds);
+ for (size_t i = 0; i < worlds.size(); ++i)
+ dispatchDidClearWindowObjectInWorld(worlds[i]);
+}
+
+void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world)
+{
+ if (!m_frame->script()->canExecuteScripts(NotAboutToExecuteScript) || !m_frame->script()->existingWindowShell(world))
+ return;
+
+ m_client->dispatchDidClearWindowObjectInWorld(world);
+
+#if ENABLE(INSPECTOR)
+ if (world != mainThreadNormalWorld())
+ return;
+
+ if (Page* page = m_frame->page()) {
+ if (InspectorController* inspector = page->inspectorController())
+ inspector->inspectedWindowScriptObjectCleared(m_frame);
+ }
+#endif
+}
+
+void FrameLoader::updateSandboxFlags()
+{
+ SandboxFlags flags = m_forcedSandboxFlags;
+ if (Frame* parentFrame = m_frame->tree()->parent())
+ flags |= parentFrame->loader()->sandboxFlags();
+ if (HTMLFrameOwnerElement* ownerElement = m_frame->ownerElement())
+ flags |= ownerElement->sandboxFlags();
+
+ if (m_sandboxFlags == flags)
+ return;
+
+ m_sandboxFlags = flags;
+
+ for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
+ child->loader()->updateSandboxFlags();
+}
+
+void FrameLoader::didChangeTitle(DocumentLoader* loader)
+{
+ m_client->didChangeTitle(loader);
+
+ if (loader == m_documentLoader) {
+ // Must update the entries in the back-forward list too.
+ history()->setCurrentItemTitle(loader->title());
+ // This must go through the WebFrame because it has the right notion of the current b/f item.
+ m_client->setTitle(loader->title(), loader->urlForHistory());
+ m_client->setMainFrameDocumentReady(true); // update observers with new DOMDocument
+ m_client->dispatchDidReceiveTitle(loader->title());
+ }
+}
+
+void FrameLoader::didChangeIcons(DocumentLoader* loader)
+{
+ if (loader == m_documentLoader)
+ m_client->dispatchDidChangeIcons();
+}
+
+void FrameLoader::dispatchDidCommitLoad()
+{
+ if (m_stateMachine.creatingInitialEmptyDocument())
+ return;
+
+ m_client->dispatchDidCommitLoad();
+
+#if ENABLE(INSPECTOR)
+ if (Page* page = m_frame->page())
+ page->inspectorController()->didCommitLoad(m_documentLoader.get());
+#endif
+}
+
+void FrameLoader::tellClientAboutPastMemoryCacheLoads()
+{
+ ASSERT(m_frame->page());
+ ASSERT(m_frame->page()->areMemoryCacheClientCallsEnabled());
+
+ if (!m_documentLoader)
+ return;
+
+ Vector<String> pastLoads;
+ m_documentLoader->takeMemoryCacheLoadsForClientNotification(pastLoads);
+
+ size_t size = pastLoads.size();
+ for (size_t i = 0; i < size; ++i) {
+ CachedResource* resource = cache()->resourceForURL(KURL(ParsedURLString, pastLoads[i]));
+
+ // FIXME: These loads, loaded from cache, but now gone from the cache by the time
+ // Page::setMemoryCacheClientCallsEnabled(true) is called, will not be seen by the client.
+ // Consider if there's some efficient way of remembering enough to deliver this client call.
+ // We have the URL, but not the rest of the response or the length.
+ if (!resource)
+ continue;
+
+ ResourceRequest request(resource->url());
+ m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize());
+ }
+}
+
+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/Source/WebCore/loader/FrameLoader.h b/Source/WebCore/loader/FrameLoader.h
new file mode 100644
index 0000000..95755d6
--- /dev/null
+++ b/Source/WebCore/loader/FrameLoader.h
@@ -0,0 +1,510 @@
+/*
+ * 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 FrameLoader_h
+#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 "ResourceLoadNotifier.h"
+#include "ResourceRequest.h"
+#include "SubframeLoader.h"
+#include "ThreadableLoader.h"
+#include "Timer.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+class Archive;
+#endif
+class AuthenticationChallenge;
+class CachedFrameBase;
+class CachedPage;
+class CachedResource;
+class Chrome;
+class DOMWrapperWorld;
+class Document;
+class DocumentLoader;
+class Event;
+class FormData;
+class FormState;
+class FormSubmission;
+class Frame;
+class FrameLoaderClient;
+class FrameNetworkingContext;
+class HistoryItem;
+class HTMLFormElement;
+class IconLoader;
+class NavigationAction;
+class NetworkingContext;
+class Page;
+class ProtectionSpace;
+class ResourceError;
+class ResourceLoader;
+class ResourceResponse;
+class ScriptSourceCode;
+class ScriptValue;
+class SecurityOrigin;
+class SerializedScriptValue;
+class SharedBuffer;
+class SubstituteData;
+class TextResourceDecoder;
+
+struct FrameLoadRequest;
+struct WindowFeatures;
+
+bool isBackForwardLoadType(FrameLoadType);
+
+class FrameLoader : public Noncopyable {
+public:
+ FrameLoader(Frame*, FrameLoaderClient*);
+ ~FrameLoader();
+
+ void init();
+
+ Frame* frame() const { return m_frame; }
+
+ 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
+ // the logic by extracting common code paths.
+
+ void prepareForLoadStart();
+ void setupForReplace();
+ void setupForReplaceByMIMEType(const String& newMIMEType);
+
+ void loadURLIntoChildFrame(const KURL&, const String& referer, Frame*);
+
+ void loadFrameRequest(const FrameLoadRequest&, bool lockHistory, bool lockBackForwardList, // Called by submitForm, calls loadPostRequest and loadURL.
+ PassRefPtr<Event>, PassRefPtr<FormState>, ReferrerPolicy);
+
+ void load(const ResourceRequest&, bool lockHistory); // Called by WebFrame, calls load(ResourceRequest, SubstituteData).
+ void load(const ResourceRequest&, const SubstituteData&, bool lockHistory); // Called both by WebFrame and internally, calls load(DocumentLoader*).
+ void load(const ResourceRequest&, const String& frameName, bool lockHistory); // Called by WebPluginController.
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ void loadArchive(PassRefPtr<Archive>);
+#endif
+
+ static void reportLocalLoadFailed(Frame*, const String& url);
+
+ unsigned long loadResourceSynchronously(const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data);
+
+ bool canHandleRequest(const ResourceRequest&);
+
+ // Also not cool.
+ void stopAllLoaders(DatabasePolicy = DatabasePolicyStop);
+ void stopForUserCancel(bool deferCheckLoadComplete = false);
+
+ bool isLoadingMainResource() const { return m_isLoadingMainResource; }
+ bool isLoading() const;
+ bool frameHasLoaded() const;
+ void transferLoadingResourcesFromPage(Page*);
+ void dispatchTransferLoadingResourceFromPage(unsigned long, DocumentLoader*, const ResourceRequest&, Page*);
+
+ int numPendingOrLoadingRequests(bool recurse) const;
+ String referrer() const;
+ String outgoingReferrer() const;
+ String outgoingOrigin() const;
+
+ DocumentLoader* activeDocumentLoader() const;
+ DocumentLoader* documentLoader() const { return m_documentLoader.get(); }
+ DocumentLoader* policyDocumentLoader() const { return m_policyDocumentLoader.get(); }
+ DocumentLoader* provisionalDocumentLoader() const { return m_provisionalDocumentLoader.get(); }
+ FrameState state() const { return m_state; }
+ static double timeOfLastCompletedLoad();
+
+ bool shouldUseCredentialStorage(ResourceLoader*);
+#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);
+
+ bool willLoadMediaElementURL(KURL&);
+
+ void handleFallbackContent();
+ bool isStopping() const;
+
+ void finishedLoading();
+
+ ResourceError cancelledError(const ResourceRequest&) const;
+ ResourceError fileDoesNotExistError(const ResourceResponse&) const;
+ ResourceError blockedError(const ResourceRequest&) const;
+ ResourceError cannotShowURLError(const ResourceRequest&) const;
+ ResourceError interruptionForPolicyChangeError(const ResourceRequest&) const;
+
+ bool isHostedByObjectElement() const;
+ bool isLoadingMainFrame() const;
+ bool canShowMIMEType(const String& MIMEType) const;
+ bool representationExistsForURLScheme(const String& URLScheme);
+ String generatedMIMETypeForURLScheme(const String& URLScheme);
+
+ void reload(bool endToEndReload = false);
+ void reloadWithOverrideEncoding(const String& overrideEncoding);
+
+ void didReceiveServerRedirectForProvisionalLoadForFrame();
+ void finishedLoadingDocument(DocumentLoader*);
+ bool isReplacing() const;
+ void setReplacing();
+ void revertToProvisional(DocumentLoader*);
+ void setMainDocumentError(DocumentLoader*, const ResourceError&);
+ void mainReceivedCompleteError(DocumentLoader*, const ResourceError&);
+ bool subframeIsLoading() const;
+ void willChangeTitle(DocumentLoader*);
+ void didChangeTitle(DocumentLoader*);
+ void didChangeIcons(DocumentLoader*);
+
+ FrameLoadType loadType() const;
+
+ CachePolicy subresourceCachePolicy() const;
+
+ void didFirstLayout();
+
+ void didFirstVisuallyNonEmptyLayout();
+
+ void loadedResourceFromMemoryCache(const CachedResource*);
+ void tellClientAboutPastMemoryCacheLoads();
+
+ void checkLoadComplete();
+ void detachFromParent();
+ void detachViewsAndDocumentLoader();
+
+ void addExtraFieldsToSubresourceRequest(ResourceRequest&);
+ void addExtraFieldsToMainResourceRequest(ResourceRequest&);
+
+ static void addHTTPOriginIfNeeded(ResourceRequest&, String origin);
+
+ FrameLoaderClient* client() const { return m_client; }
+
+ void setDefersLoading(bool);
+
+ void changeLocation(PassRefPtr<SecurityOrigin>, 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(PassRefPtr<FormSubmission>);
+
+ void stop();
+ void stopLoading(UnloadEventPolicy, DatabasePolicy = DatabasePolicyStop);
+ bool closeURL();
+
+ void didExplicitOpen();
+
+ // Callbacks from DocumentWriter
+ void didBeginDocument(bool dispatchWindowObjectAvailable);
+ void didEndDocument();
+ void willSetEncoding();
+
+ KURL iconURL();
+ void commitIconURLToIconDatabase(const KURL&);
+
+ KURL baseURL() const;
+
+ void handledOnloadEvents();
+ String userAgent(const KURL&) const;
+
+ void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*);
+ void dispatchDidClearWindowObjectsInAllWorlds();
+ void dispatchDocumentElementAvailable();
+
+ void ownerElementSandboxFlagsChanged() { updateSandboxFlags(); }
+
+ 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&);
+ void checkIfDisplayInsecureContent(SecurityOrigin* context, const KURL&);
+ void checkIfRunInsecureContent(SecurityOrigin* context, const KURL&);
+
+ Frame* opener();
+ void setOpener(Frame*);
+
+ bool isProcessingUserGesture();
+
+ void resetMultipleFormSubmissionProtection();
+
+ void checkCallImplicitClose();
+
+ void frameDetached();
+
+ const KURL& url() const { return m_URL; }
+
+ // setURL is a low-level setter and does not trigger loading.
+ void setURL(const KURL&);
+
+ void loadDone();
+ void finishedParsing();
+ void checkCompleted();
+
+ void checkDidPerformFirstNavigation();
+
+ bool isComplete() const;
+
+ KURL completeURL(const String& url);
+
+ void cancelAndClear();
+
+ void setTitle(const String&);
+ void setIconURL(const String&);
+
+ void commitProvisionalLoad();
+ bool isLoadingFromCachedPage() const { return m_loadingFromCachedPage; }
+
+ FrameLoaderStateMachine* stateMachine() const { return &m_stateMachine; }
+
+ void iconLoadDecisionAvailable();
+
+ bool shouldAllowNavigation(Frame* targetFrame) const;
+ Frame* findFrameForNavigation(const AtomicString& name);
+
+ void startIconLoader();
+
+ void applyUserAgent(ResourceRequest& request);
+
+ bool shouldInterruptLoadForXFrameOptions(const String&, const KURL&);
+
+ void open(CachedFrameBase&);
+
+ // FIXME: Should these really be public?
+ void completed();
+ bool allAncestorsAreComplete() const; // including this
+ bool allChildrenAreComplete() const; // immediate children, not all descendants
+ void clientRedirected(const KURL&, double delay, double fireDate, bool lockBackForwardList);
+ void clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress);
+ void loadItem(HistoryItem*, FrameLoadType);
+
+ // FIXME: This is public because this asynchronous callback from the FrameLoaderClient
+ // uses the policy machinery (and therefore is called via the PolicyChecker). Once we
+ // introduce a proper callback type for this function, we should make it private again.
+ void continueLoadAfterWillSubmitForm();
+
+ bool suppressOpenerInNewFrame() const { return m_suppressOpenerInNewFrame; }
+
+ 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:
+ void checkTimerFired(Timer<FrameLoader>*);
+
+ void loadSameDocumentItem(HistoryItem*);
+ void loadDifferentDocumentItem(HistoryItem*, FrameLoadType);
+
+ void loadProvisionalItemFromCachedPage();
+
+ void receivedFirstData();
+
+ void updateFirstPartyForCookies();
+ void setFirstPartyForCookies(const KURL&);
+
+ void addExtraFieldsToRequest(ResourceRequest&, FrameLoadType loadType, bool isMainResource, bool cookiePolicyURLFromRequest);
+
+ // Also not cool.
+ void stopLoadingSubframes();
+
+ void clearProvisionalLoad();
+ void markLoadComplete();
+ void transitionToCommitted(PassRefPtr<CachedPage>);
+ void frameLoadCompleted();
+
+ void mainReceivedError(const ResourceError&, bool isComplete);
+
+ static void callContinueLoadAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, 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);
+
+ bool fireBeforeUnloadEvent(Chrome*);
+
+ void continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState>, 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, const String& httpMethod, FrameLoadType, const KURL&);
+
+ void checkLoadCompleteForThisFrame();
+
+ void setDocumentLoader(DocumentLoader*);
+ void setPolicyDocumentLoader(DocumentLoader*);
+ void setProvisionalDocumentLoader(DocumentLoader*);
+
+ void setState(FrameState);
+
+ void closeOldDataSources();
+ void prepareForCachedPageRestore();
+
+ bool shouldReloadToHandleUnreachableURL(DocumentLoader*);
+
+ void dispatchDidCommitLoad();
+
+ void urlSelected(const FrameLoadRequest&, 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>);
+
+ 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>);
+
+ bool shouldReload(const KURL& currentURL, const KURL& destinationURL);
+
+ void requestFromDelegate(ResourceRequest&, unsigned long& identifier, ResourceError&);
+
+ void recursiveCheckLoadComplete();
+
+ void detachChildren();
+ void closeAndRemoveChild(Frame*);
+
+ void loadInSameDocument(const KURL&, SerializedScriptValue* stateObject, bool isNewNavigation);
+
+ void provisionalLoadStarted();
+
+ bool didOpenURL(const KURL&);
+
+ void scheduleCheckCompleted();
+ void scheduleCheckLoadComplete();
+ void startCheckCompleteTimer();
+
+ KURL originalRequestURL() const;
+
+ bool shouldTreatURLAsSameAsCurrent(const KURL&) const;
+
+ void updateSandboxFlags();
+
+ Frame* m_frame;
+ FrameLoaderClient* m_client;
+
+ 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;
+
+ // Document loaders for the three phases of frame loading. Note that while
+ // a new request is being loaded, the old document loader may still be referenced.
+ // E.g. while a new request is in the "policy" state, the old document loader may
+ // be consulted in particular as it makes sense to imply certain settings on the new loader.
+ RefPtr<DocumentLoader> m_documentLoader;
+ RefPtr<DocumentLoader> m_provisionalDocumentLoader;
+ RefPtr<DocumentLoader> m_policyDocumentLoader;
+
+ bool m_delegateIsHandlingProvisionalLoadError;
+
+ bool m_quickRedirectComing;
+ bool m_sentRedirectNotification;
+ bool m_inStopAllLoaders;
+
+ String m_outgoingReferrer;
+
+ bool m_isExecutingJavaScriptFormAction;
+
+ bool m_didCallImplicitClose;
+ bool m_wasUnloadEventEmitted;
+ bool m_pageDismissalEventBeingDispatched;
+ bool m_isComplete;
+ bool m_isLoadingMainResource;
+
+ RefPtr<SerializedScriptValue> m_pendingStateObject;
+
+ KURL m_URL;
+ KURL m_workingURL;
+
+ OwnPtr<IconLoader> m_iconLoader;
+ bool m_mayLoadIconLater;
+
+ bool m_needsClear;
+
+ KURL m_submittedFormURL;
+
+ Timer<FrameLoader> m_checkTimer;
+ bool m_shouldCallCheckCompleted;
+ bool m_shouldCallCheckLoadComplete;
+
+ Frame* m_opener;
+ HashSet<Frame*> m_openedFrames;
+
+ bool m_didPerformFirstNavigation;
+ bool m_loadingFromCachedPage;
+ bool m_suppressOpenerInNewFrame;
+
+ SandboxFlags m_sandboxFlags;
+ SandboxFlags m_forcedSandboxFlags;
+
+ RefPtr<FrameNetworkingContext> m_networkingContext;
+
+ KURL m_previousUrl;
+};
+
+// 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/Source/WebCore/loader/FrameLoaderClient.h b/Source/WebCore/loader/FrameLoaderClient.h
new file mode 100644
index 0000000..73860a5
--- /dev/null
+++ b/Source/WebCore/loader/FrameLoaderClient.h
@@ -0,0 +1,314 @@
+/*
+ * 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
+ * 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 FrameLoaderClient_h
+#define FrameLoaderClient_h
+
+#include "FrameLoaderTypes.h"
+#include "ScrollTypes.h"
+#include <wtf/Forward.h>
+#include <wtf/Vector.h>
+
+#if PLATFORM(MAC)
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+typedef id RemoteAXObjectRef;
+#else
+typedef void* RemoteAXObjectRef;
+#endif
+#endif
+
+typedef class _jobject* jobject;
+
+#if PLATFORM(MAC) && !defined(__OBJC__)
+class NSCachedURLResponse;
+class NSView;
+#endif
+
+namespace WebCore {
+
+ class AuthenticationChallenge;
+ class CachedFrame;
+ class Color;
+ class DOMWrapperWorld;
+ class DocumentLoader;
+ class Element;
+ 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;
+ class ResourceHandle;
+ class ResourceLoader;
+ class ResourceRequest;
+ class ResourceResponse;
+ class SecurityOrigin;
+ class SharedBuffer;
+ class SubstituteData;
+ class Widget;
+
+ typedef void (PolicyChecker::*FramePolicyFunction)(PolicyAction);
+
+ class FrameLoaderClient {
+ public:
+ // An inline function cannot be the first non-abstract virtual function declared
+ // in the class as it results in the vtable being generated as a weak symbol.
+ // This hurts performance (in Mac OS X at least, when loadig frameworks), so we
+ // don't want to do it in WebKit.
+ virtual bool hasHTMLView() const;
+
+ virtual ~FrameLoaderClient() { }
+
+ virtual void frameLoaderDestroyed() = 0;
+
+ virtual bool hasWebView() const = 0; // mainly for assertions
+
+ virtual void makeRepresentation(DocumentLoader*) = 0;
+ virtual void forceLayout() = 0;
+ virtual void forceLayoutForNonHTML() = 0;
+
+ virtual void setCopiesOnScroll() = 0;
+
+ virtual void detachedFromParent2() = 0;
+ virtual void detachedFromParent3() = 0;
+
+ virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&) = 0;
+
+ virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse) = 0;
+ virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier) = 0;
+ virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0;
+ virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0;
+#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 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;
+ virtual void dispatchDidPopStateWithinPage() = 0;
+ virtual void dispatchWillClose() = 0;
+ 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;
+ virtual void dispatchDidFinishDocumentLoad() = 0;
+ virtual void dispatchDidFinishLoad() = 0;
+ virtual void dispatchDidFirstLayout() = 0;
+ virtual void dispatchDidFirstVisuallyNonEmptyLayout() = 0;
+
+ virtual Frame* dispatchCreatePage(const NavigationAction&) = 0;
+ virtual void dispatchShow() = 0;
+
+ virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&) = 0;
+ virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName) = 0;
+ virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>) = 0;
+ virtual void cancelPolicyCheck() = 0;
+
+ virtual void dispatchUnableToImplementPolicy(const ResourceError&) = 0;
+
+ virtual void dispatchWillSendSubmitEvent(HTMLFormElement*) = 0;
+ virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>) = 0;
+
+ virtual void dispatchDidLoadMainResource(DocumentLoader*) = 0;
+ virtual void revertToProvisionalState(DocumentLoader*) = 0;
+ virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) = 0;
+
+ // Maybe these should go into a ProgressTrackerClient some day
+ virtual void willChangeEstimatedProgress() { }
+ virtual void didChangeEstimatedProgress() { }
+ virtual void postProgressStartedNotification() = 0;
+ virtual void postProgressEstimateChangedNotification() = 0;
+ virtual void postProgressFinishedNotification() = 0;
+
+ virtual void setMainFrameDocumentReady(bool) = 0;
+
+ virtual void startDownload(const ResourceRequest&) = 0;
+
+ virtual void willChangeTitle(DocumentLoader*) = 0;
+ virtual void didChangeTitle(DocumentLoader*) = 0;
+
+ virtual void committedLoad(DocumentLoader*, const char*, int) = 0;
+ virtual void finishedLoading(DocumentLoader*) = 0;
+
+ virtual void updateGlobalHistory() = 0;
+ virtual void updateGlobalHistoryRedirectLinks() = 0;
+
+ virtual bool shouldGoToHistoryItem(HistoryItem*) const = 0;
+ virtual void dispatchDidAddBackForwardItem(HistoryItem*) const = 0;
+ virtual void dispatchDidRemoveBackForwardItem(HistoryItem*) const = 0;
+ virtual void dispatchDidChangeBackForwardIndex() const = 0;
+
+ // This frame has displayed inactive content (such as an image) from an
+ // insecure source. Inactive content cannot spread to other frames.
+ virtual void didDisplayInsecureContent() = 0;
+
+ // The indicated security origin has run active content (such as a
+ // script) from an insecure source. Note that the insecure content can
+ // spread to other frames in the same origin.
+ virtual void didRunInsecureContent(SecurityOrigin*) = 0;
+
+ virtual ResourceError cancelledError(const ResourceRequest&) = 0;
+ virtual ResourceError blockedError(const ResourceRequest&) = 0;
+ virtual ResourceError cannotShowURLError(const ResourceRequest&) = 0;
+ virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&) = 0;
+
+ virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) = 0;
+ virtual ResourceError fileDoesNotExistError(const ResourceResponse&) = 0;
+ virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) = 0;
+
+ virtual bool shouldFallBack(const ResourceError&) = 0;
+
+ virtual 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;
+
+ virtual void frameLoadCompleted() = 0;
+ virtual void saveViewStateToItem(HistoryItem*) = 0;
+ virtual void restoreViewState() = 0;
+ virtual void provisionalLoadStarted() = 0;
+ virtual void didFinishLoad() = 0;
+ virtual void prepareForDataSourceReplacement() = 0;
+
+ virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest&, const SubstituteData&) = 0;
+ virtual void setTitle(const String& title, const KURL&) = 0;
+
+ virtual String userAgent(const KURL&) = 0;
+
+ virtual void savePlatformDataToCachedFrame(CachedFrame*) = 0;
+ virtual void transitionToCommittedFromCachedFrame(CachedFrame*) = 0;
+ virtual void transitionToCommittedForNewPage() = 0;
+
+ virtual void didSaveToPageCache() = 0;
+ virtual void didRestoreFromPageCache() = 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;
+
+ virtual void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*) = 0;
+ virtual void documentElementAvailable() = 0;
+ virtual void didPerformFirstNavigation() const = 0; // "Navigation" here means a transition from one page to another that ends up in the back/forward list.
+
+#if USE(V8)
+ 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;
+#ifdef ANDROID_APPLE_TOUCH_ICON
+ virtual void dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) = 0;
+#endif
+
+#if PLATFORM(MAC)
+ // Allow an accessibility object to retrieve a Frame parent if there's no PlatformWidget.
+ virtual RemoteAXObjectRef accessibilityRemoteObject() = 0;
+#if ENABLE(JAVA_BRIDGE)
+ virtual jobject javaApplet(NSView*) { return 0; }
+#endif
+ virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0;
+#endif
+#if USE(CFNETWORK)
+ virtual bool shouldCacheResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&, const unsigned char* data, unsigned long long length) = 0;
+#endif
+
+ virtual bool shouldUsePluginDocument(const String& /*mimeType*/) const { return false; }
+ virtual bool shouldLoadMediaElementURL(const KURL&) const { return true; }
+
+ virtual void didChangeScrollOffset() { }
+
+ 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
+
+#endif // FrameLoaderClient_h
diff --git a/Source/WebCore/loader/FrameLoaderStateMachine.cpp b/Source/WebCore/loader/FrameLoaderStateMachine.cpp
new file mode 100644
index 0000000..790b144
--- /dev/null
+++ b/Source/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/Source/WebCore/loader/FrameLoaderStateMachine.h b/Source/WebCore/loader/FrameLoaderStateMachine.h
new file mode 100644
index 0000000..c3408c2
--- /dev/null
+++ b/Source/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/Source/WebCore/loader/FrameLoaderTypes.h b/Source/WebCore/loader/FrameLoaderTypes.h
new file mode 100644
index 0000000..016de19
--- /dev/null
+++ b/Source/WebCore/loader/FrameLoaderTypes.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FrameLoaderTypes_h
+#define FrameLoaderTypes_h
+
+namespace WebCore {
+
+ enum FrameState {
+ FrameStateProvisional,
+ // This state indicates we are ready to commit to a page,
+ // which means the view will transition to use the new data source.
+ FrameStateCommittedPage,
+ FrameStateComplete
+ };
+
+ enum PolicyAction {
+ PolicyUse,
+ PolicyDownload,
+ PolicyIgnore,
+ };
+
+ // NOTE: Keep in sync with WebKit/mac/WebView/WebFramePrivate.h and WebKit/win/Interfaces/IWebFramePrivate.idl
+ enum FrameLoadType {
+ FrameLoadTypeStandard,
+ FrameLoadTypeBack,
+ FrameLoadTypeForward,
+ FrameLoadTypeIndexedBackForward, // a multi-item hop in the backforward list
+ FrameLoadTypeReload,
+ // Skipped value: 'FrameLoadTypeReloadAllowingStaleData', still present in mac/win public API. Ready to be reused
+ FrameLoadTypeSame = FrameLoadTypeReload + 2, // user loads same URL again (but not reload button)
+ FrameLoadTypeRedirectWithLockedBackForwardList, // FIXME: Merge "lockBackForwardList", "lockHistory", "quickRedirect" and "clientRedirect" into a single concept of redirect.
+ FrameLoadTypeReplace,
+ FrameLoadTypeReloadFromOrigin,
+ FrameLoadTypeBackWMLDeckNotAccessible
+ };
+
+ enum NavigationType {
+ NavigationTypeLinkClicked,
+ NavigationTypeFormSubmitted,
+ NavigationTypeBackForward,
+ NavigationTypeReload,
+ NavigationTypeFormResubmitted,
+ NavigationTypeOther
+ };
+
+ enum DatabasePolicy {
+ DatabasePolicyStop, // The database thread should be stopped and database connections closed.
+ DatabasePolicyContinue
+ };
+
+ enum ObjectContentType {
+ ObjectContentNone,
+ ObjectContentImage,
+ ObjectContentFrame,
+ ObjectContentNetscapePlugin,
+ ObjectContentOtherPlugin
+ };
+
+ enum UnloadEventPolicy {
+ UnloadEventPolicyNone,
+ UnloadEventPolicyUnloadOnly,
+ UnloadEventPolicyUnloadAndPageHide
+ };
+
+ enum ReferrerPolicy {
+ SendReferrer,
+ NoReferrer
+ };
+
+ enum SandboxFlag {
+ SandboxNone = 0,
+ SandboxNavigation = 1,
+ SandboxPlugins = 1 << 1,
+ 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;
+}
+
+#endif
diff --git a/Source/WebCore/loader/FrameNetworkingContext.h b/Source/WebCore/loader/FrameNetworkingContext.h
new file mode 100644
index 0000000..dff1144
--- /dev/null
+++ b/Source/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/Source/WebCore/loader/HistoryController.cpp b/Source/WebCore/loader/HistoryController.cpp
new file mode 100644
index 0000000..ff733a9
--- /dev/null
+++ b/Source/WebCore/loader/HistoryController.cpp
@@ -0,0 +1,681 @@
+/*
+ * 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/)
+ *
+ * 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 "HistoryController.h"
+
+#include "BackForwardController.h"
+#include "CachedPage.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"
+#include "Logging.h"
+#include "Page.h"
+#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)
+ , m_frameLoadComplete(true)
+{
+}
+
+HistoryController::~HistoryController()
+{
+}
+
+void HistoryController::saveScrollPositionAndViewStateToItem(HistoryItem* item)
+{
+ if (!item || !m_frame->view())
+ return;
+
+ item->setScrollPoint(m_frame->view()->scrollPosition());
+ // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client.
+ m_frame->loader()->client()->saveViewStateToItem(item);
+}
+
+/*
+ There is a race condition between the layout and load completion that affects restoring the scroll position.
+ We try to restore the scroll position at both the first layout and upon load completion.
+
+ 1) If first layout happens before the load completes, we want to restore the scroll position then so that the
+ first time we draw the page is already scrolled to the right place, instead of starting at the top and later
+ jumping down. It is possible that the old scroll position is past the part of the doc laid out so far, in
+ which case the restore silent fails and we will fix it in when we try to restore on doc completion.
+ 2) If the layout happens after the load completes, the attempt to restore at load completion time silently
+ fails. We then successfully restore it when the layout happens.
+*/
+void HistoryController::restoreScrollPositionAndViewState()
+{
+ if (!m_frame->loader()->stateMachine()->committedFirstRealDocumentLoad())
+ return;
+
+ ASSERT(m_currentItem);
+
+ // FIXME: As the ASSERT attests, it seems we should always have a currentItem here.
+ // One counterexample is <rdar://problem/4917290>
+ // For now, to cover this issue in release builds, there is no technical harm to returning
+ // early and from a user standpoint - as in the above radar - the previous page load failed
+ // so there *is* no scroll or view state to restore!
+ if (!m_currentItem)
+ return;
+
+ // FIXME: It would be great to work out a way to put this code in WebCore instead of calling
+ // through to the client. It's currently used only for the PDF view on Mac.
+ m_frame->loader()->client()->restoreViewState();
+
+ if (FrameView* view = m_frame->view())
+ if (!view->wasScrolledByUser())
+ view->setScrollPosition(m_currentItem->scrollPoint());
+}
+
+void HistoryController::updateBackForwardListForFragmentScroll()
+{
+ updateBackForwardListClippedAtTarget(false);
+}
+
+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()->stateMachine()->creatingInitialEmptyDocument())
+ return;
+
+ // For a standard page load, we will have a previous item set, which will be used to
+ // store the form state. However, in some cases we will have no previous item, and
+ // the current item is the right place to save the state. One example is when we
+ // detach a bunch of frames because we are navigating from a site with frames to
+ // another site. Another is when saving the frame state of a frame that is not the
+ // target of the current navigation (if we even decide to save with that granularity).
+
+ // Because of previousItem's "masking" of currentItem for this purpose, it's important
+ // that we keep track of the end of a page transition with m_frameLoadComplete. We
+ // leverage the checkLoadComplete recursion to achieve this goal.
+
+ HistoryItem* item = m_frameLoadComplete ? m_currentItem.get() : m_previousItem.get();
+ if (!item)
+ return;
+
+ Document* document = m_frame->document();
+ ASSERT(document);
+
+ if (item->isCurrentDocument(document)) {
+ LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->uniqueName().string().utf8().data(), item);
+ item->setDocumentState(document->formElementsState());
+ }
+}
+
+// Walk the frame tree, telling all frames to save their form state into their current
+// history item.
+void HistoryController::saveDocumentAndScrollState()
+{
+ for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame)) {
+ frame->loader()->history()->saveDocumentState();
+ frame->loader()->history()->saveScrollPositionAndViewStateToItem(frame->loader()->history()->currentItem());
+ }
+}
+
+void HistoryController::restoreDocumentState()
+{
+ Document* doc = m_frame->document();
+
+ HistoryItem* itemToRestore = 0;
+
+ switch (m_frame->loader()->loadType()) {
+ case FrameLoadTypeReload:
+ case FrameLoadTypeReloadFromOrigin:
+ case FrameLoadTypeSame:
+ case FrameLoadTypeReplace:
+ break;
+ case FrameLoadTypeBack:
+ case FrameLoadTypeBackWMLDeckNotAccessible:
+ case FrameLoadTypeForward:
+ case FrameLoadTypeIndexedBackForward:
+ case FrameLoadTypeRedirectWithLockedBackForwardList:
+ case FrameLoadTypeStandard:
+ itemToRestore = m_currentItem.get();
+ }
+
+ if (!itemToRestore)
+ return;
+
+ LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->uniqueName().string().utf8().data(), itemToRestore);
+ doc->setStateForNewFormElements(itemToRestore->documentState());
+}
+
+void HistoryController::invalidateCurrentItemCachedPage()
+{
+ // When we are pre-commit, the currentItem is where the pageCache data resides
+ CachedPage* cachedPage = pageCache()->get(currentItem());
+
+ // FIXME: This is a grotesque hack to fix <rdar://problem/4059059> Crash in RenderFlow::detach
+ // Somehow the PageState object is not properly updated, and is holding onto a stale document.
+ // Both Xcode and FileMaker see this crash, Safari does not.
+
+ ASSERT(!cachedPage || cachedPage->document() == m_frame->document());
+ if (cachedPage && cachedPage->document() == m_frame->document()) {
+ cachedPage->document()->setInPageCache(false);
+ cachedPage->clear();
+ }
+
+ if (cachedPage)
+ pageCache()->remove(currentItem());
+}
+
+// Main funnel for navigating to a previous location (back/forward, non-search snap-back)
+// This includes recursion to handle loading into framesets properly
+void HistoryController::goToItem(HistoryItem* targetItem, FrameLoadType type)
+{
+ ASSERT(!m_frame->tree()->parent());
+
+ // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
+ // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
+ // Ultimately, history item navigations should go through the policy delegate. That's covered in:
+ // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+ if (!m_frame->loader()->client()->shouldGoToHistoryItem(targetItem))
+ return;
+
+ // Set the BF cursor before commit, which lets the user quickly click back/forward again.
+ // - plus, it only makes sense for the top level of the operation through the frametree,
+ // as opposed to happening for some/one of the page commits that might happen soon
+ 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);
+}
+
+void HistoryController::updateForBackForwardNavigation()
+{
+#if !LOG_DISABLED
+ if (m_frame->loader()->documentLoader())
+ LOG(History, "WebCoreHistory: Updating History for back/forward navigation in frame %s", m_frame->loader()->documentLoader()->title().utf8().data());
+#endif
+
+ // Must grab the current scroll position before disturbing it
+ if (!m_frameLoadComplete)
+ saveScrollPositionAndViewStateToItem(m_previousItem.get());
+}
+
+void HistoryController::updateForReload()
+{
+#if !LOG_DISABLED
+ if (m_frame->loader()->documentLoader())
+ LOG(History, "WebCoreHistory: Updating History for reload in frame %s", m_frame->loader()->documentLoader()->title().utf8().data());
+#endif
+
+ if (m_currentItem) {
+ pageCache()->remove(m_currentItem.get());
+
+ if (m_frame->loader()->loadType() == FrameLoadTypeReload || m_frame->loader()->loadType() == FrameLoadTypeReloadFromOrigin)
+ saveScrollPositionAndViewStateToItem(m_currentItem.get());
+
+ // Sometimes loading a page again leads to a different result because of cookies. Bugzilla 4072
+ if (m_frame->loader()->documentLoader()->unreachableURL().isEmpty())
+ m_currentItem->setURL(m_frame->loader()->documentLoader()->requestURL());
+ }
+}
+
+// There are 3 things you might think of as "history", all of which are handled by these functions.
+//
+// 1) Back/forward: The m_currentItem is part of this mechanism.
+// 2) Global history: Handled by the client.
+// 3) Visited links: Handled by the PageGroup.
+
+void HistoryController::updateForStandardLoad(HistoryUpdateType updateType)
+{
+ LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", m_frame->loader()->documentLoader()->url().string().ascii().data());
+
+ FrameLoader* frameLoader = m_frame->loader();
+
+ Settings* settings = m_frame->settings();
+ bool needPrivacy = !settings || settings->privateBrowsingEnabled();
+ const KURL& historyURL = frameLoader->documentLoader()->urlForHistory();
+
+ if (!frameLoader->documentLoader()->isClientRedirect()) {
+ if (!historyURL.isEmpty()) {
+ if (updateType != UpdateAllExceptBackForwardList)
+ updateBackForwardListClippedAtTarget(true);
+ if (!needPrivacy) {
+ frameLoader->client()->updateGlobalHistory();
+ frameLoader->documentLoader()->setDidCreateGlobalHistoryEntry(true);
+ if (frameLoader->documentLoader()->unreachableURL().isEmpty())
+ frameLoader->client()->updateGlobalHistoryRedirectLinks();
+ }
+ if (Page* page = m_frame->page())
+ page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForward()->currentItem());
+ }
+ } else if (frameLoader->documentLoader()->unreachableURL().isEmpty() && m_currentItem) {
+ m_currentItem->setURL(frameLoader->documentLoader()->url());
+ m_currentItem->setFormInfoFromRequest(frameLoader->documentLoader()->request());
+ }
+
+ if (!historyURL.isEmpty() && !needPrivacy) {
+ if (Page* page = m_frame->page())
+ addVisitedLink(page, historyURL);
+
+ if (!frameLoader->documentLoader()->didCreateGlobalHistoryEntry() && frameLoader->documentLoader()->unreachableURL().isEmpty() && !frameLoader->url().isEmpty())
+ frameLoader->client()->updateGlobalHistoryRedirectLinks();
+ }
+}
+
+void HistoryController::updateForRedirectWithLockedBackForwardList()
+{
+#if !LOG_DISABLED
+ if (m_frame->loader()->documentLoader())
+ LOG(History, "WebCoreHistory: Updating History for redirect load in frame %s", m_frame->loader()->documentLoader()->title().utf8().data());
+#endif
+
+ Settings* settings = m_frame->settings();
+ bool needPrivacy = !settings || settings->privateBrowsingEnabled();
+ const KURL& historyURL = m_frame->loader()->documentLoader()->urlForHistory();
+
+ if (m_frame->loader()->documentLoader()->isClientRedirect()) {
+ if (!m_currentItem && !m_frame->tree()->parent()) {
+ if (!historyURL.isEmpty()) {
+ updateBackForwardListClippedAtTarget(true);
+ if (!needPrivacy) {
+ m_frame->loader()->client()->updateGlobalHistory();
+ m_frame->loader()->documentLoader()->setDidCreateGlobalHistoryEntry(true);
+ if (m_frame->loader()->documentLoader()->unreachableURL().isEmpty())
+ m_frame->loader()->client()->updateGlobalHistoryRedirectLinks();
+ }
+ if (Page* page = m_frame->page())
+ page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForward()->currentItem());
+ }
+ }
+ if (m_currentItem) {
+ m_currentItem->setURL(m_frame->loader()->documentLoader()->url());
+ m_currentItem->setFormInfoFromRequest(m_frame->loader()->documentLoader()->request());
+ }
+ } else {
+ Frame* parentFrame = m_frame->tree()->parent();
+ if (parentFrame && parentFrame->loader()->history()->m_currentItem)
+ parentFrame->loader()->history()->m_currentItem->setChildItem(createItem(true));
+ }
+
+ if (!historyURL.isEmpty() && !needPrivacy) {
+ if (Page* page = m_frame->page())
+ 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();
+ }
+}
+
+void HistoryController::updateForClientRedirect()
+{
+#if !LOG_DISABLED
+ if (m_frame->loader()->documentLoader())
+ LOG(History, "WebCoreHistory: Updating History for client redirect in frame %s", m_frame->loader()->documentLoader()->title().utf8().data());
+#endif
+
+ // Clear out form data so we don't try to restore it into the incoming page. Must happen after
+ // webcore has closed the URL and saved away the form state.
+ if (m_currentItem) {
+ m_currentItem->clearDocumentState();
+ m_currentItem->clearScrollPoint();
+ }
+
+ Settings* settings = m_frame->settings();
+ bool needPrivacy = !settings || settings->privateBrowsingEnabled();
+ const KURL& historyURL = m_frame->loader()->documentLoader()->urlForHistory();
+
+ if (!historyURL.isEmpty() && !needPrivacy) {
+ if (Page* page = m_frame->page())
+ addVisitedLink(page, historyURL);
+ }
+}
+
+void HistoryController::updateForCommit()
+{
+ FrameLoader* frameLoader = m_frame->loader();
+#if !LOG_DISABLED
+ if (frameLoader->documentLoader())
+ LOG(History, "WebCoreHistory: Updating History for commit in frame %s", frameLoader->documentLoader()->title().utf8().data());
+#endif
+ FrameLoadType type = frameLoader->loadType();
+ if (isBackForwardLoadType(type) ||
+ ((type == FrameLoadTypeReload || type == FrameLoadTypeReloadFromOrigin) && !frameLoader->provisionalDocumentLoader()->unreachableURL().isEmpty())) {
+ // Once committed, we want to use current item for saving DocState, and
+ // the provisional item for restoring state.
+ // Note previousItem must be set before we close the URL, which will
+ // happen when the data source is made non-provisional below
+ m_frameLoadComplete = false;
+ m_previousItem = m_currentItem;
+ ASSERT(m_provisionalItem);
+ m_currentItem = m_provisionalItem;
+ m_provisionalItem = 0;
+ }
+}
+
+void HistoryController::updateForSameDocumentNavigation()
+{
+ if (m_frame->loader()->url().isEmpty())
+ return;
+
+ Settings* settings = m_frame->settings();
+ if (!settings || settings->privateBrowsingEnabled())
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ addVisitedLink(page, m_frame->loader()->url());
+}
+
+void HistoryController::updateForFrameLoadCompleted()
+{
+ // Even if already complete, we might have set a previous item on a frame that
+ // didn't do any data loading on the past transaction. Make sure to track that
+ // the load is complete so that we use the current item instead.
+ m_frameLoadComplete = true;
+}
+
+void HistoryController::setCurrentItem(HistoryItem* item)
+{
+ m_frameLoadComplete = false;
+ m_previousItem = m_currentItem;
+ m_currentItem = item;
+}
+
+void HistoryController::setCurrentItemTitle(const String& title)
+{
+ if (m_currentItem)
+ 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;
+}
+
+PassRefPtr<HistoryItem> HistoryController::createItem(bool useOriginal)
+{
+ DocumentLoader* documentLoader = m_frame->loader()->documentLoader();
+
+ KURL unreachableURL = documentLoader ? documentLoader->unreachableURL() : KURL();
+
+ KURL url;
+ KURL originalURL;
+
+ if (!unreachableURL.isEmpty()) {
+ url = unreachableURL;
+ originalURL = unreachableURL;
+ } else {
+ originalURL = documentLoader ? documentLoader->originalURL() : KURL();
+ if (useOriginal)
+ url = originalURL;
+ else if (documentLoader)
+ url = documentLoader->requestURL();
+ }
+
+ LOG(History, "WebCoreHistory: Creating item for %s", url.string().ascii().data());
+
+ // Frames that have never successfully loaded any content
+ // may have no URL at all. Currently our history code can't
+ // deal with such things, so we nip that in the bud here.
+ // Later we may want to learn to live with nil for URL.
+ // See bug 3368236 and related bugs for more information.
+ if (url.isEmpty())
+ url = blankURL();
+ if (originalURL.isEmpty())
+ originalURL = blankURL();
+
+ Frame* parentFrame = m_frame->tree()->parent();
+ String parent = parentFrame ? parentFrame->tree()->uniqueName() : "";
+ String title = documentLoader ? documentLoader->title() : "";
+
+ RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->uniqueName(), parent, title);
+ item->setOriginalURLString(originalURL.string());
+
+ if (!unreachableURL.isEmpty() || !documentLoader || documentLoader->response().httpStatusCode() >= 400)
+ item->setLastVisitWasFailure(true);
+
+ // Save form state if this is a POST
+ if (documentLoader) {
+ if (useOriginal)
+ item->setFormInfoFromRequest(documentLoader->originalRequest());
+ else
+ item->setFormInfoFromRequest(documentLoader->request());
+ }
+
+ // Set the item for which we will save document state
+ m_frameLoadComplete = false;
+ m_previousItem = m_currentItem;
+ m_currentItem = item;
+
+ return item.release();
+}
+
+PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame* targetFrame, bool clipAtTarget)
+{
+ RefPtr<HistoryItem> bfItem = createItem(m_frame->tree()->parent() ? true : false);
+ if (!m_frameLoadComplete)
+ saveScrollPositionAndViewStateToItem(m_previousItem.get());
+
+ 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();
+
+ // If the child is a frame corresponding to an <object> element that never loaded,
+ // we don't want to create a history item, because that causes fallback content
+ // to be ignored on reload.
+
+ if (!(!hasChildLoaded && childLoader->isHostedByObjectElement()))
+ bfItem->addChildItem(childLoader->history()->createItemTree(targetFrame, clipAtTarget));
+ }
+ }
+ // FIXME: Eliminate the isTargetItem flag in favor of itemSequenceNumber.
+ if (m_frame == targetFrame)
+ bfItem->setIsTargetItem(true);
+ return bfItem;
+}
+
+// The general idea here is to traverse the frame tree and the item tree in parallel,
+// tracking whether each frame already has the content the item requests. If there is
+// a match (by URL), we just restore scroll position and recurse. Otherwise we must
+// reload that frame, and all its kids.
+void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type)
+{
+ ASSERT(item);
+ ASSERT(fromItem);
+
+ // 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 m_frameLoadComplete is true)
+ ASSERT(m_frameLoadComplete);
+ saveDocumentState();
+ saveScrollPositionAndViewStateToItem(m_currentItem.get());
+
+ if (FrameView* view = m_frame->view())
+ view->setWasScrolledByUser(false);
+
+ m_previousItem = m_currentItem;
+ m_currentItem = item;
+
+ // Restore form state (works from currentItem)
+ restoreDocumentState();
+
+ // Restore the scroll position (we choose to do this rather than going back to the anchor point)
+ restoreScrollPositionAndViewState();
+
+ const HistoryItemVector& childItems = item->children();
+
+ int size = childItems.size();
+ for (int i = 0; i < size; ++i) {
+ String childFrameName = childItems[i]->target();
+ HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName);
+ ASSERT(fromChildItem);
+ Frame* childFrame = m_frame->tree()->child(childFrameName);
+ ASSERT(childFrame);
+ childFrame->loader()->history()->recursiveGoToItem(childItems[i].get(), fromChildItem, type);
+ }
+ } else {
+ m_frame->loader()->loadItem(item, type);
+ }
+}
+
+// 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;
+
+ unsigned size = childItems.size();
+ for (unsigned i = 0; i < size; ++i) {
+ if (!m_frame->tree()->child(childItems[i]->target()))
+ return false;
+ }
+
+ return true;
+}
+
+void HistoryController::updateBackForwardListClippedAtTarget(bool doClip)
+{
+ // In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.
+ // The item that was the target of the user's navigation is designated as the "targetItem".
+ // When this function is called with doClip=true we're able to create the whole tree except for the target's children,
+ // which will be loaded in the future. That part of the tree will be filled out as the child loads are committed.
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ if (m_frame->loader()->documentLoader()->urlForHistory().isEmpty())
+ return;
+
+ Frame* mainFrame = page->mainFrame();
+ ASSERT(mainFrame);
+ FrameLoader* frameLoader = mainFrame->loader();
+
+ frameLoader->checkDidPerformFirstNavigation();
+
+ 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> topItem = page->mainFrame()->loader()->history()->createItemTree(m_frame, false);
+
+ // 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)
+{
+ if (!m_currentItem)
+ return;
+
+ if (!urlString.isEmpty())
+ m_currentItem->setURLString(urlString);
+ m_currentItem->setTitle(title);
+ m_currentItem->setStateObject(stateObject);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/HistoryController.h b/Source/WebCore/loader/HistoryController.h
new file mode 100644
index 0000000..1bf5072
--- /dev/null
+++ b/Source/WebCore/loader/HistoryController.h
@@ -0,0 +1,105 @@
+/*
+ * 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/)
+ *
+ * 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 HistoryController_h
+#define HistoryController_h
+
+#include "FrameLoaderTypes.h"
+#include "PlatformString.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class Frame;
+class HistoryItem;
+class SerializedScriptValue;
+
+class HistoryController : public Noncopyable {
+public:
+ enum HistoryUpdateType { UpdateAll, UpdateAllExceptBackForwardList };
+
+ HistoryController(Frame*);
+ ~HistoryController();
+
+ void saveScrollPositionAndViewStateToItem(HistoryItem*);
+ void restoreScrollPositionAndViewState();
+
+ void updateBackForwardListForFragmentScroll();
+
+ void saveDocumentState();
+ void saveDocumentAndScrollState();
+ void restoreDocumentState();
+
+ void invalidateCurrentItemCachedPage();
+
+ void goToItem(HistoryItem*, FrameLoadType);
+
+ void updateForBackForwardNavigation();
+ void updateForReload();
+ void updateForStandardLoad(HistoryUpdateType updateType = UpdateAll);
+ void updateForRedirectWithLockedBackForwardList();
+ void updateForClientRedirect();
+ void updateForCommit();
+ void updateForSameDocumentNavigation();
+ void updateForFrameLoadCompleted();
+
+ 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*);
+
+ void pushState(PassRefPtr<SerializedScriptValue>, const String& title, const String& url);
+ void replaceState(PassRefPtr<SerializedScriptValue>, const String& title, const String& url);
+
+private:
+ PassRefPtr<HistoryItem> createItem(bool useOriginal);
+ PassRefPtr<HistoryItem> createItemTree(Frame* targetFrame, bool clipAtTarget);
+
+ void recursiveGoToItem(HistoryItem*, HistoryItem*, FrameLoadType);
+ bool currentFramesMatchItem(HistoryItem*) const;
+ void updateBackForwardListClippedAtTarget(bool doClip);
+
+ Frame* m_frame;
+
+ RefPtr<HistoryItem> m_currentItem;
+ RefPtr<HistoryItem> m_previousItem;
+ RefPtr<HistoryItem> m_provisionalItem;
+
+ bool m_frameLoadComplete;
+};
+
+} // namespace WebCore
+
+#endif // HistoryController_h
diff --git a/Source/WebCore/loader/ImageLoader.cpp b/Source/WebCore/loader/ImageLoader.cpp
new file mode 100644
index 0000000..a77e8c0
--- /dev/null
+++ b/Source/WebCore/loader/ImageLoader.cpp
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "ImageLoader.h"
+
+#include "CachedImage.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 {
+
+template<> struct ValueCheck<WebCore::ImageLoader*> {
+ typedef WebCore::ImageLoader* TraitType;
+ static void checkConsistency(const WebCore::ImageLoader* p)
+ {
+ if (!p)
+ return;
+ ASSERT(p->element());
+ ValueCheck<WebCore::Element*>::checkConsistency(p->element());
+ }
+};
+
+}
+#endif
+
+namespace WebCore {
+
+class ImageEventSender : public Noncopyable {
+public:
+ ImageEventSender(const AtomicString& eventType);
+
+ void dispatchEventSoon(ImageLoader*);
+ void cancelEvent(ImageLoader*);
+
+ void dispatchPendingEvents();
+
+#if !ASSERT_DISABLED
+ bool hasPendingEvents(ImageLoader* loader) { return m_dispatchSoonList.find(loader) != notFound; }
+#endif
+
+private:
+ void timerFired(Timer<ImageEventSender>*);
+
+ AtomicString m_eventType;
+ Timer<ImageEventSender> m_timer;
+ Vector<ImageLoader*> m_dispatchSoonList;
+ Vector<ImageLoader*> m_dispatchingList;
+};
+
+static ImageEventSender& beforeLoadEventSender()
+{
+ DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent));
+ return sender;
+}
+
+static ImageEventSender& loadEventSender()
+{
+ DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent));
+ return sender;
+}
+
+ImageLoader::ImageLoader(Element* element)
+ : m_element(element)
+ , m_image(0)
+ , m_firedBeforeLoad(true)
+ , m_firedLoad(true)
+ , m_imageComplete(true)
+ , m_loadManually(false)
+{
+}
+
+ImageLoader::~ImageLoader()
+{
+ if (m_image)
+ m_image->removeClient(this);
+
+ ASSERT(!m_firedBeforeLoad || !beforeLoadEventSender().hasPendingEvents(this));
+ if (!m_firedBeforeLoad)
+ beforeLoadEventSender().cancelEvent(this);
+
+ ASSERT(!m_firedLoad || !loadEventSender().hasPendingEvents(this));
+ if (!m_firedLoad)
+ loadEventSender().cancelEvent(this);
+}
+
+void ImageLoader::setImage(CachedImage* newImage)
+{
+ ASSERT(m_failedLoadURL.isEmpty());
+ CachedImage* oldImage = m_image.get();
+ if (newImage != oldImage) {
+ m_image = newImage;
+ if (!m_firedBeforeLoad) {
+ beforeLoadEventSender().cancelEvent(this);
+ m_firedBeforeLoad = true;
+ }
+ if (!m_firedLoad) {
+ loadEventSender().cancelEvent(this);
+ m_firedLoad = true;
+ }
+ m_imageComplete = true;
+ if (newImage)
+ newImage->addClient(this);
+ if (oldImage)
+ oldImage->removeClient(this);
+ }
+
+ if (RenderImageResource* imageResource = renderImageResource())
+ imageResource->resetAnimation();
+}
+
+void ImageLoader::updateFromElement()
+{
+ // If we're not making renderers for the page, then don't load images. We don't want to slow
+ // down the raw HTML parsing case by loading images we don't intend to display.
+ Document* document = m_element->document();
+ if (!document->renderer())
+ return;
+
+ AtomicString attr = m_element->getAttribute(m_element->imageSourceAttributeName());
+
+ if (attr == m_failedLoadURL)
+ return;
+
+ // Do not load any image if the 'src' attribute is missing or if it is
+ // an empty string referring to a local file. The latter condition is
+ // a quirk that preserves old behavior that Dashboard widgets
+ // need (<rdar://problem/5994621>).
+ CachedImage* newImage = 0;
+ if (!(attr.isNull() || (attr.isEmpty() && document->baseURI().isLocalFile()))) {
+ if (m_loadManually) {
+ bool autoLoadOtherImages = document->cachedResourceLoader()->autoLoadImages();
+ document->cachedResourceLoader()->setAutoLoadImages(false);
+ newImage = new CachedImage(sourceURI(attr));
+ newImage->setLoading(true);
+ newImage->setOwningCachedResourceLoader(document->cachedResourceLoader());
+ document->cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage);
+ document->cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages);
+ } else
+ newImage = document->cachedResourceLoader()->requestImage(sourceURI(attr));
+
+ // If we do not have an image here, it means that a cross-site
+ // violation occurred.
+ m_failedLoadURL = !newImage ? attr : AtomicString();
+ }
+
+ CachedImage* oldImage = m_image.get();
+ if (newImage != oldImage) {
+ if (!m_firedBeforeLoad)
+ beforeLoadEventSender().cancelEvent(this);
+ if (!m_firedLoad)
+ loadEventSender().cancelEvent(this);
+
+ m_image = newImage;
+ m_firedBeforeLoad = !newImage;
+ m_firedLoad = !newImage;
+ m_imageComplete = !newImage;
+
+ if (newImage) {
+ newImage->addClient(this);
+ if (!m_element->document()->hasListenerType(Document::BEFORELOAD_LISTENER))
+ dispatchPendingBeforeLoadEvent();
+ else
+ beforeLoadEventSender().dispatchEventSoon(this);
+ }
+ if (oldImage)
+ oldImage->removeClient(this);
+ }
+
+ if (RenderImageResource* imageResource = renderImageResource())
+ imageResource->resetAnimation();
+}
+
+void ImageLoader::updateFromElementIgnoringPreviousError()
+{
+ // Clear previous error.
+ m_failedLoadURL = AtomicString();
+ updateFromElement();
+}
+
+void ImageLoader::notifyFinished(CachedResource*)
+{
+ ASSERT(m_failedLoadURL.isEmpty());
+
+ m_imageComplete = true;
+ if (haveFiredBeforeLoadEvent())
+ updateRenderer();
+
+ if (m_firedLoad)
+ return;
+
+ 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()
+{
+ 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()
+{
+ if (m_firedBeforeLoad)
+ return;
+ if (!m_image)
+ return;
+ if (!m_element->document()->attached())
+ return;
+ m_firedBeforeLoad = true;
+ if (m_element->dispatchBeforeLoadEvent(m_image->url())) {
+ updateRenderer();
+ return;
+ }
+ if (m_image) {
+ m_image->removeClient(this);
+ m_image = 0;
+ }
+ loadEventSender().cancelEvent(this);
+
+ if (m_element->hasTagName(HTMLNames::objectTag))
+ static_cast<HTMLObjectElement*>(m_element)->renderFallbackContent();
+}
+
+void ImageLoader::dispatchPendingLoadEvent()
+{
+ if (m_firedLoad)
+ return;
+ if (!m_image)
+ return;
+ if (!m_element->document()->attached())
+ return;
+ m_firedLoad = true;
+ dispatchLoadEvent();
+}
+
+void ImageLoader::dispatchPendingBeforeLoadEvents()
+{
+ beforeLoadEventSender().dispatchPendingEvents();
+}
+
+void ImageLoader::dispatchPendingLoadEvents()
+{
+ loadEventSender().dispatchPendingEvents();
+}
+
+void ImageLoader::elementWillMoveToNewOwnerDocument()
+{
+ setImage(0);
+}
+
+ImageEventSender::ImageEventSender(const AtomicString& eventType)
+ : m_eventType(eventType)
+ , m_timer(this, &ImageEventSender::timerFired)
+{
+}
+
+void ImageEventSender::dispatchEventSoon(ImageLoader* loader)
+{
+ m_dispatchSoonList.append(loader);
+ if (!m_timer.isActive())
+ m_timer.startOneShot(0);
+}
+
+void ImageEventSender::cancelEvent(ImageLoader* loader)
+{
+ // Remove instances of this loader from both lists.
+ // Use loops because we allow multiple instances to get into the lists.
+ size_t size = m_dispatchSoonList.size();
+ for (size_t i = 0; i < size; ++i) {
+ if (m_dispatchSoonList[i] == loader)
+ m_dispatchSoonList[i] = 0;
+ }
+ size = m_dispatchingList.size();
+ for (size_t i = 0; i < size; ++i) {
+ if (m_dispatchingList[i] == loader)
+ m_dispatchingList[i] = 0;
+ }
+ if (m_dispatchSoonList.isEmpty())
+ m_timer.stop();
+}
+
+void ImageEventSender::dispatchPendingEvents()
+{
+ // Need to avoid re-entering this function; if new dispatches are
+ // scheduled before the parent finishes processing the list, they
+ // will set a timer and eventually be processed.
+ if (!m_dispatchingList.isEmpty())
+ return;
+
+ m_timer.stop();
+
+ m_dispatchSoonList.checkConsistency();
+
+ m_dispatchingList.swap(m_dispatchSoonList);
+ size_t size = m_dispatchingList.size();
+ for (size_t i = 0; i < size; ++i) {
+ if (ImageLoader* loader = m_dispatchingList[i]) {
+ if (m_eventType == eventNames().beforeloadEvent)
+ loader->dispatchPendingBeforeLoadEvent();
+ else
+ loader->dispatchPendingLoadEvent();
+ }
+ }
+ m_dispatchingList.clear();
+}
+
+void ImageEventSender::timerFired(Timer<ImageEventSender>*)
+{
+ dispatchPendingEvents();
+}
+
+}
diff --git a/Source/WebCore/loader/ImageLoader.h b/Source/WebCore/loader/ImageLoader.h
new file mode 100644
index 0000000..9bf7624
--- /dev/null
+++ b/Source/WebCore/loader/ImageLoader.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2009 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ImageLoader_h
+#define ImageLoader_h
+
+#include "CachedResourceClient.h"
+#include "CachedResourceHandle.h"
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class Element;
+class ImageLoadEventSender;
+class RenderImageResource;
+
+class ImageLoader : public CachedResourceClient {
+public:
+ ImageLoader(Element*);
+ virtual ~ImageLoader();
+
+ // This function should be called when the element is attached to a document; starts
+ // loading if a load hasn't already been started.
+ void updateFromElement();
+
+ // This function should be called whenever the 'src' attribute is set, even if its value
+ // doesn't change; starts new load unconditionally (matches Firefox and Opera behavior).
+ void updateFromElementIgnoringPreviousError();
+
+ void elementWillMoveToNewOwnerDocument();
+
+ Element* element() const { return m_element; }
+ bool imageComplete() const { return m_imageComplete; }
+
+ CachedImage* image() const { return m_image.get(); }
+ void setImage(CachedImage*); // Cancels pending beforeload and load events, and doesn't dispatch new ones.
+
+ void setLoadManually(bool loadManually) { m_loadManually = loadManually; }
+
+ bool haveFiredBeforeLoadEvent() const { return m_firedBeforeLoad; }
+ bool haveFiredLoadEvent() const { return m_firedLoad; }
+
+ static void dispatchPendingBeforeLoadEvents();
+ static void dispatchPendingLoadEvents();
+
+protected:
+ virtual void notifyFinished(CachedResource*);
+
+private:
+ virtual void dispatchLoadEvent() = 0;
+ virtual String sourceURI(const AtomicString&) const = 0;
+
+ friend class ImageEventSender;
+ void dispatchPendingBeforeLoadEvent();
+ void dispatchPendingLoadEvent();
+
+ RenderImageResource* renderImageResource();
+ void updateRenderer();
+
+ Element* m_element;
+ CachedResourceHandle<CachedImage> m_image;
+ AtomicString m_failedLoadURL;
+ bool m_firedBeforeLoad : 1;
+ bool m_firedLoad : 1;
+ bool m_imageComplete : 1;
+ bool m_loadManually : 1;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/MainResourceLoader.cpp b/Source/WebCore/loader/MainResourceLoader.cpp
new file mode 100644
index 0000000..e6abefd
--- /dev/null
+++ b/Source/WebCore/loader/MainResourceLoader.cpp
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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 "MainResourceLoader.h"
+
+#include "ApplicationCacheHost.h"
+#include "DocumentLoadTiming.h"
+#include "DocumentLoader.h"
+#include "FormState.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "HTMLFormElement.h"
+#include "Page.h"
+#if PLATFORM(QT)
+#include "PluginDatabase.h"
+#endif
+#include "ResourceError.h"
+#include "ResourceHandle.h"
+#include "ResourceLoadScheduler.h"
+#include "SchemeRegistry.h"
+#include "Settings.h"
+#include <wtf/CurrentTime.h>
+
+// FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader.
+
+namespace WebCore {
+
+MainResourceLoader::MainResourceLoader(Frame* frame)
+ : ResourceLoader(frame, true, true)
+ , m_dataLoadTimer(this, &MainResourceLoader::handleDataLoadNow)
+ , m_loadingMultipartContent(false)
+ , m_waitingForContentPolicy(false)
+{
+}
+
+MainResourceLoader::~MainResourceLoader()
+{
+}
+
+PassRefPtr<MainResourceLoader> MainResourceLoader::create(Frame* frame)
+{
+ return adoptRef(new MainResourceLoader(frame));
+}
+
+void MainResourceLoader::receivedError(const ResourceError& error)
+{
+ // Calling receivedMainResourceError will likely result in the last reference to this object to go away.
+ RefPtr<MainResourceLoader> protect(this);
+ RefPtr<Frame> protectFrame(m_frame);
+
+ // It is important that we call FrameLoader::receivedMainResourceError before calling
+ // FrameLoader::didFailToLoad because receivedMainResourceError clears out the relevant
+ // document loaders. Also, receivedMainResourceError ends up calling a FrameLoadDelegate method
+ // and didFailToLoad calls a ResourceLoadDelegate method and they need to be in the correct order.
+ frameLoader()->receivedMainResourceError(error, true);
+
+ if (!cancelled()) {
+ ASSERT(!reachedTerminalState());
+ frameLoader()->notifier()->didFailToLoad(this, error);
+
+ releaseResources();
+ }
+
+ ASSERT(reachedTerminalState());
+}
+
+void MainResourceLoader::didCancel(const ResourceError& error)
+{
+ m_dataLoadTimer.stop();
+
+ // Calling receivedMainResourceError will likely result in the last reference to this object to go away.
+ RefPtr<MainResourceLoader> protect(this);
+
+ if (m_waitingForContentPolicy) {
+ frameLoader()->policyChecker()->cancelCheck();
+ ASSERT(m_waitingForContentPolicy);
+ m_waitingForContentPolicy = false;
+ deref(); // balances ref in didReceiveResponse
+ }
+ frameLoader()->receivedMainResourceError(error, true);
+ ResourceLoader::didCancel(error);
+}
+
+ResourceError MainResourceLoader::interruptionForPolicyChangeError() const
+{
+ return frameLoader()->interruptionForPolicyChangeError(request());
+}
+
+void MainResourceLoader::stopLoadingForPolicyChange()
+{
+ cancel(interruptionForPolicyChangeError());
+}
+
+void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
+{
+ static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, 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
+}
+
+bool MainResourceLoader::isPostOrRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
+{
+ if (newRequest.httpMethod() == "POST")
+ return true;
+
+ int status = redirectResponse.httpStatusCode();
+ if (((status >= 301 && status <= 303) || status == 307)
+ && frameLoader()->initialRequest().httpMethod() == "POST")
+ return true;
+
+ return false;
+}
+
+void MainResourceLoader::addData(const char* data, int length, bool allAtOnce)
+{
+ ResourceLoader::addData(data, length, allAtOnce);
+ documentLoader()->receivedData(data, length);
+}
+
+void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
+{
+ // Note that there are no asserts here as there are for the other callbacks. This is due to the
+ // fact that this "callback" is sent when starting every load, and the state of callback
+ // deferrals plays less of a part in this function in preventing the bad behavior deferring
+ // callbacks is meant to prevent.
+ ASSERT(!newRequest.isNull());
+
+ // 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();
+
+ // Check if the redirected url is allowed to access the redirecting url's timing information.
+ RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(newRequest.url());
+ if (!securityOrigin->canRequest(redirectResponse.url()))
+ documentLoadTiming->hasCrossOriginRedirect = true;
+
+ 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())
+ newRequest.setFirstPartyForCookies(newRequest.url());
+
+ // If we're fielding a redirect in response to a POST, force a load from origin, since
+ // this is a common site technique to return to a page viewing some data that the POST
+ // just modified.
+ // Also, POST requests always load from origin, but this does not affect subresources.
+ if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse))
+ newRequest.setCachePolicy(ReloadIgnoringCacheData);
+
+ ResourceLoader::willSendRequest(newRequest, redirectResponse);
+
+ // Don't set this on the first request. It is set when the main load was started.
+ m_documentLoader->setRequest(newRequest);
+
+ Frame* top = m_frame->tree()->top();
+ 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
+ // synchronously for these redirect cases.
+ if (!redirectResponse.isNull()) {
+ ref(); // balanced by deref in continueAfterNavigationPolicy
+ frameLoader()->policyChecker()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this);
+ }
+}
+
+static bool shouldLoadAsEmptyDocument(const KURL& url)
+{
+#if PLATFORM(TORCHMOBILE)
+ return url.isEmpty() || (url.protocolIs("about") && equalIgnoringRef(url, blankURL()));
+#else
+ return url.isEmpty() || SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(url.protocol());
+#endif
+}
+
+void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, const ResourceResponse& r)
+{
+ KURL url = request().url();
+ const String& mimeType = r.mimeType();
+
+ switch (contentPolicy) {
+ case PolicyUse: {
+ // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255).
+ bool isRemoteWebArchive = equalIgnoringCase("application/x-webarchive", mimeType) && !m_substituteData.isValid() && !url.isLocalFile();
+ if (!frameLoader()->canShowMIMEType(mimeType) || isRemoteWebArchive) {
+ frameLoader()->policyChecker()->cannotShowMIMEType(r);
+ // Check reachedTerminalState since the load may have already been cancelled inside of _handleUnimplementablePolicyWithErrorCode::.
+ if (!reachedTerminalState())
+ stopLoadingForPolicyChange();
+ return;
+ }
+ break;
+ }
+
+ case PolicyDownload:
+ // m_handle can be null, e.g. when loading a substitute resource from application cache.
+ if (!m_handle) {
+ receivedError(cannotShowURLError());
+ return;
+ }
+ frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->firstRequest(), r);
+ // It might have gone missing
+ if (frameLoader())
+ receivedError(interruptionForPolicyChangeError());
+ return;
+
+ case PolicyIgnore:
+ stopLoadingForPolicyChange();
+ return;
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ RefPtr<MainResourceLoader> protect(this);
+
+ if (r.isHTTP()) {
+ int status = r.httpStatusCode();
+ if (status < 200 || status >= 300) {
+ bool hostedByObject = frameLoader()->isHostedByObjectElement();
+
+ frameLoader()->handleFallbackContent();
+ // object elements are no longer rendered after we fallback, so don't
+ // keep trying to process data from their load
+
+ if (hostedByObject)
+ cancel();
+ }
+ }
+
+ // we may have cancelled this load as part of switching to fallback content
+ if (!reachedTerminalState())
+ ResourceLoader::didReceiveResponse(r);
+
+ if (frameLoader() && !frameLoader()->isStopping()) {
+ if (m_substituteData.isValid()) {
+ if (m_substituteData.content()->size())
+ didReceiveData(m_substituteData.content()->data(), m_substituteData.content()->size(), m_substituteData.content()->size(), true);
+ if (frameLoader() && !frameLoader()->isStopping())
+ didFinishLoading(0);
+ } else if (shouldLoadAsEmptyDocument(url) || frameLoader()->representationExistsForURLScheme(url.protocol()))
+ didFinishLoading(0);
+ }
+}
+
+void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy)
+{
+ static_cast<MainResourceLoader*>(argument)->continueAfterContentPolicy(policy);
+}
+
+void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy)
+{
+ ASSERT(m_waitingForContentPolicy);
+ m_waitingForContentPolicy = false;
+ if (frameLoader() && !frameLoader()->isStopping())
+ continueAfterContentPolicy(policy, m_response);
+ deref(); // balances ref in didReceiveResponse
+}
+
+#if PLATFORM(QT)
+void MainResourceLoader::substituteMIMETypeFromPluginDatabase(const ResourceResponse& r)
+{
+ if (!m_frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin))
+ return;
+
+ String filename = r.url().lastPathComponent();
+ if (filename.endsWith("/"))
+ return;
+
+ size_t extensionPos = filename.reverseFind('.');
+ if (extensionPos == notFound)
+ return;
+
+ String extension = filename.substring(extensionPos + 1);
+ String mimeType = PluginDatabase::installedPlugins()->MIMETypeForExtension(extension);
+ if (!mimeType.isEmpty()) {
+ ResourceResponse* response = const_cast<ResourceResponse*>(&r);
+ response->setMimeType(mimeType);
+ }
+}
+#endif
+
+void MainResourceLoader::didReceiveResponse(const ResourceResponse& r)
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainResponse(request(), r))
+ return;
+#endif
+
+ HTTPHeaderMap::const_iterator it = r.httpHeaderFields().find(AtomicString("x-frame-options"));
+ if (it != r.httpHeaderFields().end()) {
+ String content = it->second;
+ if (m_frame->loader()->shouldInterruptLoadForXFrameOptions(content, r.url())) {
+ cancel();
+ return;
+ }
+ }
+
+ // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
+ // See <rdar://problem/6304600> for more details.
+#if !PLATFORM(CF)
+ ASSERT(shouldLoadAsEmptyDocument(r.url()) || !defersLoading());
+#endif
+
+#if PLATFORM(QT)
+ if (r.mimeType() == "application/octet-stream")
+ substituteMIMETypeFromPluginDatabase(r);
+#endif
+
+ if (m_loadingMultipartContent) {
+ frameLoader()->setupForReplaceByMIMEType(r.mimeType());
+ clearResourceData();
+ }
+
+ if (r.isMultipart())
+ m_loadingMultipartContent = true;
+
+ // 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);
+
+ m_documentLoader->setResponse(r);
+
+ m_response = r;
+
+ ASSERT(!m_waitingForContentPolicy);
+ m_waitingForContentPolicy = true;
+ ref(); // balanced by deref in continueAfterContentPolicy and didCancel
+
+ ASSERT(frameLoader()->activeDocumentLoader());
+
+ // Always show content with valid substitute data.
+ if (frameLoader()->activeDocumentLoader()->substituteData().isValid()) {
+ callContinueAfterContentPolicy(this, PolicyUse);
+ return;
+ }
+
+#if ENABLE(FTPDIR)
+ // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it
+ Settings* settings = m_frame->settings();
+ if (settings && settings->forceFTPDirectoryListings() && m_response.mimeType() == "application/x-ftp-directory") {
+ callContinueAfterContentPolicy(this, PolicyUse);
+ return;
+ }
+#endif
+
+ frameLoader()->policyChecker()->checkContentPolicy(m_response.mimeType(), callContinueAfterContentPolicy, this);
+}
+
+void MainResourceLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce)
+{
+ ASSERT(data);
+ ASSERT(length != 0);
+
+ ASSERT(!m_response.isNull());
+
+#if USE(CFNETWORK) || (PLATFORM(MAC) && !defined(BUILDING_ON_TIGER))
+ // Workaround for <rdar://problem/6060782>
+ if (m_response.isNull()) {
+ m_response = ResourceResponse(KURL(), "text/html", 0, String(), String());
+ if (DocumentLoader* documentLoader = frameLoader()->activeDocumentLoader())
+ documentLoader->setResponse(m_response);
+ }
+#endif
+
+ // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
+ // See <rdar://problem/6304600> for more details.
+#if !PLATFORM(CF)
+ ASSERT(!defersLoading());
+#endif
+
+ #if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ documentLoader()->applicationCacheHost()->mainResourceDataReceived(data, length, lengthReceived, allAtOnce);
+#endif
+
+ // 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);
+
+ m_timeOfLastDataReceived = currentTime();
+
+ ResourceLoader::didReceiveData(data, length, lengthReceived, allAtOnce);
+}
+
+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.
+#if !PLATFORM(CF)
+ ASSERT(shouldLoadAsEmptyDocument(frameLoader()->activeDocumentLoader()->url()) || !defersLoading());
+#endif
+
+ // The additional processing can do anything including possibly removing the last
+ // reference to this object.
+ RefPtr<MainResourceLoader> protect(this);
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ RefPtr<DocumentLoader> dl = documentLoader();
+#endif
+
+ ASSERT(!documentLoader()->timing()->responseEnd);
+ documentLoader()->timing()->responseEnd = finishTime ? finishTime : m_timeOfLastDataReceived;
+ frameLoader()->finishedLoading();
+ ResourceLoader::didFinishLoading(finishTime);
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ dl->applicationCacheHost()->finishedLoadingMainResource();
+#endif
+}
+
+void MainResourceLoader::didFail(const ResourceError& error)
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainError(request(), error))
+ return;
+#endif
+
+ // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
+ // See <rdar://problem/6304600> for more details.
+#if !PLATFORM(CF)
+ ASSERT(!defersLoading());
+#endif
+
+ receivedError(error);
+}
+
+void MainResourceLoader::handleEmptyLoad(const KURL& url, bool forURLScheme)
+{
+ String mimeType;
+ if (forURLScheme)
+ mimeType = frameLoader()->generatedMIMETypeForURLScheme(url.protocol());
+ else
+ mimeType = "text/html";
+
+ ResourceResponse response(url, mimeType, 0, String(), String());
+ didReceiveResponse(response);
+}
+
+void MainResourceLoader::handleDataLoadNow(MainResourceLoaderTimer*)
+{
+ RefPtr<MainResourceLoader> protect(this);
+
+ 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);
+}
+
+void MainResourceLoader::startDataLoadTimer()
+{
+ m_dataLoadTimer.startOneShot(0);
+
+#if HAVE(RUNLOOP_TIMER)
+ if (SchedulePairHashSet* scheduledPairs = m_frame->page()->scheduledRunLoopPairs())
+ m_dataLoadTimer.schedule(*scheduledPairs);
+#endif
+}
+
+void MainResourceLoader::handleDataLoadSoon(const ResourceRequest& r)
+{
+ m_initialRequest = r;
+
+ if (m_documentLoader->deferMainResourceDataLoad())
+ startDataLoadTimer();
+ else
+ handleDataLoadNow(0);
+}
+
+bool MainResourceLoader::loadNow(ResourceRequest& r)
+{
+ bool shouldLoadEmptyBeforeRedirect = shouldLoadAsEmptyDocument(r.url());
+
+ ASSERT(!m_handle);
+ ASSERT(shouldLoadEmptyBeforeRedirect || !defersLoading());
+
+ // Send this synthetic delegate callback since clients expect it, and
+ // we no longer send the callback from within NSURLConnection for
+ // initial requests.
+ willSendRequest(r, ResourceResponse());
+
+ // <rdar://problem/4801066>
+ // willSendRequest() is liable to make the call to frameLoader() return NULL, so we need to check that here
+ if (!frameLoader())
+ return false;
+
+ const KURL& url = r.url();
+ bool shouldLoadEmpty = shouldLoadAsEmptyDocument(url) && !m_substituteData.isValid();
+
+ if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && defersLoading())
+ return true;
+
+ resourceLoadScheduler()->addMainResourceLoad(this);
+ if (m_substituteData.isValid())
+ handleDataLoadSoon(r);
+ else if (shouldLoadEmpty || frameLoader()->representationExistsForURLScheme(url.protocol()))
+ handleEmptyLoad(url, !shouldLoadEmpty);
+ else
+ m_handle = ResourceHandle::create(m_frame->loader()->networkingContext(), r, this, false, true);
+
+ return false;
+}
+
+bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData)
+{
+ ASSERT(!m_handle);
+
+ m_substituteData = substituteData;
+
+ ASSERT(documentLoader()->timing()->navigationStart);
+ ASSERT(!documentLoader()->timing()->fetchStart);
+ documentLoader()->timing()->fetchStart = currentTime();
+ ResourceRequest request(r);
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ documentLoader()->applicationCacheHost()->maybeLoadMainResource(request, m_substituteData);
+#endif
+
+ bool defer = defersLoading();
+ if (defer) {
+ bool shouldLoadEmpty = shouldLoadAsEmptyDocument(request.url());
+ if (shouldLoadEmpty)
+ defer = false;
+ }
+ if (!defer) {
+ if (loadNow(request)) {
+ // Started as an empty document, but was redirected to something non-empty.
+ ASSERT(defersLoading());
+ defer = true;
+ }
+ }
+ if (defer)
+ m_initialRequest = request;
+
+ return true;
+}
+
+void MainResourceLoader::setDefersLoading(bool defers)
+{
+ ResourceLoader::setDefersLoading(defers);
+
+ if (defers) {
+ if (m_dataLoadTimer.isActive())
+ m_dataLoadTimer.stop();
+ } else {
+ if (m_initialRequest.isNull())
+ return;
+
+ if (m_substituteData.isValid() && m_documentLoader->deferMainResourceDataLoad())
+ startDataLoadTimer();
+ else {
+ ResourceRequest r(m_initialRequest);
+ m_initialRequest = ResourceRequest();
+ loadNow(r);
+ }
+ }
+}
+
+}
diff --git a/Source/WebCore/loader/MainResourceLoader.h b/Source/WebCore/loader/MainResourceLoader.h
new file mode 100644
index 0000000..1620f7a
--- /dev/null
+++ b/Source/WebCore/loader/MainResourceLoader.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MainResourceLoader_h
+#define MainResourceLoader_h
+
+#include "FrameLoaderTypes.h"
+#include "ResourceLoader.h"
+#include "SubstituteData.h"
+#include <wtf/Forward.h>
+
+#if HAVE(RUNLOOP_TIMER)
+#include "RunLoopTimer.h"
+#else
+#include "Timer.h"
+#endif
+
+namespace WebCore {
+
+ class FormState;
+ class ResourceRequest;
+
+ class MainResourceLoader : public ResourceLoader {
+ public:
+ static PassRefPtr<MainResourceLoader> create(Frame*);
+ virtual ~MainResourceLoader();
+
+ virtual bool load(const ResourceRequest&, const SubstituteData&);
+ virtual void addData(const char*, int, bool allAtOnce);
+
+ virtual void setDefersLoading(bool);
+
+ 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(double finishTime);
+ virtual void didFail(const ResourceError&);
+
+#if HAVE(RUNLOOP_TIMER)
+ typedef RunLoopTimer<MainResourceLoader> MainResourceLoaderTimer;
+#else
+ typedef Timer<MainResourceLoader> MainResourceLoaderTimer;
+#endif
+
+ void handleDataLoadNow(MainResourceLoaderTimer*);
+
+ bool isLoadingMultipartContent() const { return m_loadingMultipartContent; }
+
+ private:
+ MainResourceLoader(Frame*);
+
+ virtual void didCancel(const ResourceError&);
+
+ bool loadNow(ResourceRequest&);
+
+ void handleEmptyLoad(const KURL&, bool forURLScheme);
+ void handleDataLoadSoon(const ResourceRequest& r);
+
+ void startDataLoadTimer();
+ void handleDataLoad(ResourceRequest&);
+
+ void receivedError(const ResourceError&);
+ ResourceError interruptionForPolicyChangeError() const;
+ void stopLoadingForPolicyChange();
+ bool isPostOrRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse);
+
+ static void callContinueAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue);
+ void continueAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue);
+
+ static void callContinueAfterContentPolicy(void*, PolicyAction);
+ void continueAfterContentPolicy(PolicyAction);
+ void continueAfterContentPolicy(PolicyAction, const ResourceResponse&);
+
+#if PLATFORM(QT)
+ void substituteMIMETypeFromPluginDatabase(const ResourceResponse&);
+#endif
+
+ ResourceRequest m_initialRequest;
+ SubstituteData m_substituteData;
+
+ MainResourceLoaderTimer m_dataLoadTimer;
+
+ bool m_loadingMultipartContent;
+ bool m_waitingForContentPolicy;
+ double m_timeOfLastDataReceived;
+ };
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/NavigationAction.cpp b/Source/WebCore/loader/NavigationAction.cpp
new file mode 100644
index 0000000..ed68e43
--- /dev/null
+++ b/Source/WebCore/loader/NavigationAction.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "NavigationAction.h"
+
+#include "Event.h"
+#include "FrameLoader.h"
+
+namespace WebCore {
+
+static NavigationType navigationType(FrameLoadType frameLoadType, bool isFormSubmission, bool haveEvent)
+{
+ if (isFormSubmission)
+ return NavigationTypeFormSubmitted;
+ if (haveEvent)
+ return NavigationTypeLinkClicked;
+ if (frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin)
+ return NavigationTypeReload;
+ if (isBackForwardLoadType(frameLoadType))
+ return NavigationTypeBackForward;
+ return NavigationTypeOther;
+}
+
+NavigationAction::NavigationAction()
+ : m_type(NavigationTypeOther)
+{
+}
+
+NavigationAction::NavigationAction(const KURL& url, NavigationType type)
+ : m_URL(url)
+ , m_type(type)
+{
+}
+
+NavigationAction::NavigationAction(const KURL& url, FrameLoadType frameLoadType,
+ bool isFormSubmission)
+ : m_URL(url)
+ , m_type(navigationType(frameLoadType, isFormSubmission, 0))
+{
+}
+
+NavigationAction::NavigationAction(const KURL& url, NavigationType type, PassRefPtr<Event> event)
+ : m_URL(url)
+ , m_type(type)
+ , m_event(event)
+{
+}
+
+NavigationAction::NavigationAction(const KURL& url, FrameLoadType frameLoadType,
+ bool isFormSubmission, PassRefPtr<Event> event)
+ : m_URL(url)
+ , m_type(navigationType(frameLoadType, isFormSubmission, event))
+ , m_event(event)
+{
+}
+
+}
diff --git a/Source/WebCore/loader/NavigationAction.h b/Source/WebCore/loader/NavigationAction.h
new file mode 100644
index 0000000..0477c6e
--- /dev/null
+++ b/Source/WebCore/loader/NavigationAction.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef NavigationAction_h
+#define NavigationAction_h
+
+#include "Event.h"
+#include "FrameLoaderTypes.h"
+#include "KURL.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+ class NavigationAction {
+ public:
+ NavigationAction();
+ NavigationAction(const KURL&, NavigationType);
+ NavigationAction(const KURL&, FrameLoadType, bool isFormSubmission);
+ NavigationAction(const KURL&, NavigationType, PassRefPtr<Event>);
+ NavigationAction(const KURL&, FrameLoadType, bool isFormSubmission, PassRefPtr<Event>);
+
+ bool isEmpty() const { return m_URL.isEmpty(); }
+
+ KURL url() const { return m_URL; }
+ NavigationType type() const { return m_type; }
+ const Event* event() const { return m_event.get(); }
+
+ private:
+ KURL m_URL;
+ NavigationType m_type;
+ RefPtr<Event> m_event;
+ };
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/NavigationScheduler.cpp b/Source/WebCore/loader/NavigationScheduler.cpp
new file mode 100644
index 0000000..175219c
--- /dev/null
+++ b/Source/WebCore/loader/NavigationScheduler.cpp
@@ -0,0 +1,442 @@
+/*
+ * 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 {
+
+unsigned NavigationDisablerForBeforeUnload::s_navigationDisableCount = 0;
+
+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; }
+
+protected:
+ void clearUserGesture() { m_wasUserGesture = false; }
+
+private:
+ double m_delay;
+ bool m_lockHistory;
+ bool m_lockBackForwardList;
+ bool m_wasDuringLoad;
+ bool m_isLocationChange;
+ bool m_wasUserGesture;
+};
+
+class ScheduledURLNavigation : public ScheduledNavigation {
+protected:
+ ScheduledURLNavigation(double delay, PassRefPtr<SecurityOrigin> securityOrigin, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool duringLoad, bool isLocationChange)
+ : ScheduledNavigation(delay, lockHistory, lockBackForwardList, duringLoad, isLocationChange)
+ , m_securityOrigin(securityOrigin)
+ , m_url(url)
+ , m_referrer(referrer)
+ , m_haveToldClient(false)
+ {
+ }
+
+ virtual void fire(Frame* frame)
+ {
+ UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture);
+ frame->loader()->changeLocation(m_securityOrigin, 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);
+ }
+
+ SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); }
+ String url() const { return m_url; }
+ String referrer() const { return m_referrer; }
+
+private:
+ RefPtr<SecurityOrigin> m_securityOrigin;
+ String m_url;
+ String m_referrer;
+ bool m_haveToldClient;
+};
+
+class ScheduledRedirect : public ScheduledURLNavigation {
+public:
+ ScheduledRedirect(double delay, PassRefPtr<SecurityOrigin> securityOrigin, const String& url, bool lockHistory, bool lockBackForwardList)
+ : ScheduledURLNavigation(delay, securityOrigin, url, String(), lockHistory, lockBackForwardList, false, false)
+ {
+ clearUserGesture();
+ }
+
+ virtual bool shouldStartTimer(Frame* frame) { return frame->loader()->allAncestorsAreComplete(); }
+};
+
+class ScheduledLocationChange : public ScheduledURLNavigation {
+public:
+ ScheduledLocationChange(PassRefPtr<SecurityOrigin> securityOrigin, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool duringLoad)
+ : ScheduledURLNavigation(0.0, securityOrigin, url, referrer, lockHistory, lockBackForwardList, duringLoad, true) { }
+};
+
+class ScheduledRefresh : public ScheduledURLNavigation {
+public:
+ ScheduledRefresh(PassRefPtr<SecurityOrigin> securityOrigin, const String& url, const String& referrer)
+ : ScheduledURLNavigation(0.0, securityOrigin, url, referrer, true, true, false, true)
+ {
+ }
+
+ virtual void fire(Frame* frame)
+ {
+ UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture);
+ frame->loader()->changeLocation(securityOrigin(), 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.
+ Frame* requestingFrame = m_submission->state()->sourceFrame();
+ if (!requestingFrame->loader()->shouldAllowNavigation(frame))
+ return;
+ FrameLoadRequest frameRequest(requestingFrame->document()->securityOrigin());
+ 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();
+}
+
+inline bool NavigationScheduler::shouldScheduleNavigation() const
+{
+ return m_frame->page();
+}
+
+inline bool NavigationScheduler::shouldScheduleNavigation(const String& url) const
+{
+ return shouldScheduleNavigation() && (protocolIsJavaScript(url) || NavigationDisablerForBeforeUnload::isNavigationAllowed());
+}
+
+void NavigationScheduler::scheduleRedirect(double delay, const String& url)
+{
+ if (!shouldScheduleNavigation(url))
+ 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, m_frame->document()->securityOrigin(), 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(PassRefPtr<SecurityOrigin> securityOrigin, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList)
+{
+ if (!shouldScheduleNavigation(url))
+ 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(securityOrigin, 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(securityOrigin, 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 (!shouldScheduleNavigation())
+ return;
+ const KURL& url = m_frame->loader()->url();
+ if (url.isEmpty())
+ return;
+
+ schedule(adoptPtr(new ScheduledRefresh(m_frame->document()->securityOrigin(), url.string(), m_frame->loader()->outgoingReferrer())));
+}
+
+void NavigationScheduler::scheduleHistoryNavigation(int steps)
+{
+ if (!shouldScheduleNavigation())
+ 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/Source/WebCore/loader/NavigationScheduler.h b/Source/WebCore/loader/NavigationScheduler.h
new file mode 100644
index 0000000..2014ff2
--- /dev/null
+++ b/Source/WebCore/loader/NavigationScheduler.h
@@ -0,0 +1,108 @@
+/*
+ * 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) 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.
+ */
+
+#ifndef NavigationScheduler_h
+#define NavigationScheduler_h
+
+#include "Event.h"
+#include "Timer.h"
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class FormState;
+class FormSubmission;
+class Frame;
+class ScheduledNavigation;
+class SecurityOrigin;
+
+struct FrameLoadRequest;
+
+class NavigationDisablerForBeforeUnload {
+ WTF_MAKE_NONCOPYABLE(NavigationDisablerForBeforeUnload);
+
+public:
+ NavigationDisablerForBeforeUnload()
+ {
+ s_navigationDisableCount++;
+ }
+ ~NavigationDisablerForBeforeUnload()
+ {
+ ASSERT(s_navigationDisableCount);
+ s_navigationDisableCount--;
+ }
+ static bool isNavigationAllowed() { return !s_navigationDisableCount; }
+
+private:
+ static unsigned s_navigationDisableCount;
+};
+
+class NavigationScheduler {
+ WTF_MAKE_NONCOPYABLE(NavigationScheduler);
+
+public:
+ NavigationScheduler(Frame*);
+ ~NavigationScheduler();
+
+ bool redirectScheduledDuringLoad();
+ bool locationChangePending();
+
+ void scheduleRedirect(double delay, const String& url);
+ void scheduleLocationChange(PassRefPtr<SecurityOrigin>, const String& url, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true);
+ void scheduleFormSubmission(PassRefPtr<FormSubmission>);
+ void scheduleRefresh();
+ void scheduleHistoryNavigation(int steps);
+
+ void startTimer();
+
+ void cancel(bool newLoadInProgress = false);
+ void clear();
+
+private:
+ bool shouldScheduleNavigation() const;
+ bool shouldScheduleNavigation(const String& url) const;
+
+ void timerFired(Timer<NavigationScheduler>*);
+ void schedule(PassOwnPtr<ScheduledNavigation>);
+
+ static bool mustLockBackForwardList(Frame* targetFrame);
+
+ Frame* m_frame;
+ Timer<NavigationScheduler> m_timer;
+ OwnPtr<ScheduledNavigation> m_redirect;
+};
+
+} // namespace WebCore
+
+#endif // NavigationScheduler_h
diff --git a/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp b/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp
new file mode 100644
index 0000000..75a2fe9
--- /dev/null
+++ b/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "NetscapePlugInStreamLoader.h"
+
+#include "FrameLoader.h"
+#include "DocumentLoader.h"
+
+namespace WebCore {
+
+NetscapePlugInStreamLoader::NetscapePlugInStreamLoader(Frame* frame, NetscapePlugInStreamLoaderClient* client)
+ : ResourceLoader(frame, true, true)
+ , m_client(client)
+{
+}
+
+NetscapePlugInStreamLoader::~NetscapePlugInStreamLoader()
+{
+}
+
+PassRefPtr<NetscapePlugInStreamLoader> NetscapePlugInStreamLoader::create(Frame* frame, NetscapePlugInStreamLoaderClient* client, const ResourceRequest& request)
+{
+ RefPtr<NetscapePlugInStreamLoader> loader(adoptRef(new NetscapePlugInStreamLoader(frame, client)));
+ loader->setShouldBufferData(false);
+ loader->documentLoader()->addPlugInStreamLoader(loader.get());
+ if (!loader->init(request))
+ return 0;
+
+ return loader.release();
+}
+
+bool NetscapePlugInStreamLoader::isDone() const
+{
+ return !m_client;
+}
+
+void NetscapePlugInStreamLoader::releaseResources()
+{
+ m_client = 0;
+ ResourceLoader::releaseResources();
+}
+
+void NetscapePlugInStreamLoader::didReceiveResponse(const ResourceResponse& response)
+{
+ RefPtr<NetscapePlugInStreamLoader> protect(this);
+
+ m_client->didReceiveResponse(this, response);
+
+ // Don't continue if the stream is cancelled
+ if (!m_client)
+ return;
+
+ ResourceLoader::didReceiveResponse(response);
+
+ // Don't continue if the stream is cancelled
+ if (!m_client)
+ return;
+
+ if (!response.isHTTP())
+ return;
+
+ if (m_client->wantsAllStreams())
+ return;
+
+ if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400)
+ didCancel(frameLoader()->fileDoesNotExistError(response));
+}
+
+void NetscapePlugInStreamLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce)
+{
+ RefPtr<NetscapePlugInStreamLoader> protect(this);
+
+ m_client->didReceiveData(this, data, length);
+
+ ResourceLoader::didReceiveData(data, length, lengthReceived, allAtOnce);
+}
+
+void NetscapePlugInStreamLoader::didFinishLoading(double finishTime)
+{
+ RefPtr<NetscapePlugInStreamLoader> protect(this);
+
+ m_documentLoader->removePlugInStreamLoader(this);
+ m_client->didFinishLoading(this);
+ ResourceLoader::didFinishLoading(finishTime);
+}
+
+void NetscapePlugInStreamLoader::didFail(const ResourceError& error)
+{
+ RefPtr<NetscapePlugInStreamLoader> protect(this);
+
+ m_documentLoader->removePlugInStreamLoader(this);
+ m_client->didFail(this, error);
+ ResourceLoader::didFail(error);
+}
+
+void NetscapePlugInStreamLoader::didCancel(const ResourceError& error)
+{
+ RefPtr<NetscapePlugInStreamLoader> protect(this);
+
+ m_client->didFail(this, error);
+
+ // If calling didFail spins the run loop the stream loader can reach the terminal state.
+ // If that's the case we just return early.
+ if (reachedTerminalState())
+ return;
+
+ // We need to remove the stream loader after the call to didFail, since didFail can
+ // spawn a new run loop and if the loader has been removed it won't be deferred when
+ // the document loader is asked to defer loading.
+ m_documentLoader->removePlugInStreamLoader(this);
+
+ ResourceLoader::didCancel(error);
+}
+
+}
diff --git a/Source/WebCore/loader/NetscapePlugInStreamLoader.h b/Source/WebCore/loader/NetscapePlugInStreamLoader.h
new file mode 100644
index 0000000..4d7d03b
--- /dev/null
+++ b/Source/WebCore/loader/NetscapePlugInStreamLoader.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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 "ResourceLoader.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+ class NetscapePlugInStreamLoader;
+
+ class NetscapePlugInStreamLoaderClient {
+ public:
+ virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&) = 0;
+ virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int) = 0;
+ virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&) = 0;
+ virtual void didFinishLoading(NetscapePlugInStreamLoader*) { }
+ virtual bool wantsAllStreams() const { return false; }
+
+ protected:
+ virtual ~NetscapePlugInStreamLoaderClient() { }
+ };
+
+ class NetscapePlugInStreamLoader : public ResourceLoader {
+ public:
+ static PassRefPtr<NetscapePlugInStreamLoader> create(Frame*, NetscapePlugInStreamLoaderClient*, const ResourceRequest&);
+ virtual ~NetscapePlugInStreamLoader();
+
+ bool isDone() const;
+
+ private:
+ virtual void didReceiveResponse(const ResourceResponse&);
+ virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce);
+ virtual void didFinishLoading(double finishTime);
+ virtual void didFail(const ResourceError&);
+
+ virtual void releaseResources();
+
+ NetscapePlugInStreamLoader(Frame*, NetscapePlugInStreamLoaderClient*);
+
+ virtual void didCancel(const ResourceError& error);
+
+ NetscapePlugInStreamLoaderClient* m_client;
+ };
+
+}
diff --git a/Source/WebCore/loader/PingLoader.cpp b/Source/WebCore/loader/PingLoader.cpp
new file mode 100644
index 0000000..280620a
--- /dev/null
+++ b/Source/WebCore/loader/PingLoader.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 "FrameLoaderClient.h"
+#include "Page.h"
+#include "ProgressTracker.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)
+{
+ unsigned long identifier = frame->page()->progress()->createUniqueIdentifier();
+ m_shouldUseCredentialStorage = frame->loader()->client()->shouldUseCredentialStorage(frame->loader()->activeDocumentLoader(), identifier);
+ 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/Source/WebCore/loader/PingLoader.h b/Source/WebCore/loader/PingLoader.h
new file mode 100644
index 0000000..3a076fb
--- /dev/null
+++ b/Source/WebCore/loader/PingLoader.h
@@ -0,0 +1,77 @@
+/*
+ * 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; }
+ bool shouldUseCredentialStorage(ResourceHandle*) { return m_shouldUseCredentialStorage; }
+
+ RefPtr<ResourceHandle> m_handle;
+ Timer<PingLoader> m_timeout;
+ bool m_shouldUseCredentialStorage;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/PlaceholderDocument.cpp b/Source/WebCore/loader/PlaceholderDocument.cpp
new file mode 100644
index 0000000..93a26db
--- /dev/null
+++ b/Source/WebCore/loader/PlaceholderDocument.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 "PlaceholderDocument.h"
+
+namespace WebCore {
+
+void PlaceholderDocument::attach()
+{
+ ASSERT(!attached());
+
+ // Skipping Document::attach().
+ ContainerNode::attach();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/PlaceholderDocument.h b/Source/WebCore/loader/PlaceholderDocument.h
new file mode 100644
index 0000000..3d40a6e
--- /dev/null
+++ b/Source/WebCore/loader/PlaceholderDocument.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 PlaceholderDocument_h
+#define PlaceholderDocument_h
+
+#include "Document.h"
+
+namespace WebCore {
+
+class PlaceholderDocument : public Document {
+public:
+ static PassRefPtr<PlaceholderDocument> create(Frame* frame, const KURL& url)
+ {
+ return adoptRef(new PlaceholderDocument(frame, url));
+ }
+
+ virtual void attach();
+
+private:
+ PlaceholderDocument(Frame* frame, const KURL& url) : Document(frame, url, false, false) { }
+};
+
+} // namespace WebCore
+
+#endif // PlaceholderDocument_h
diff --git a/Source/WebCore/loader/PolicyCallback.cpp b/Source/WebCore/loader/PolicyCallback.cpp
new file mode 100644
index 0000000..4ec2c84
--- /dev/null
+++ b/Source/WebCore/loader/PolicyCallback.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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/)
+ *
+ * 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 "PolicyCallback.h"
+
+#include "FormState.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "HTMLFormElement.h"
+
+namespace WebCore {
+
+PolicyCallback::PolicyCallback()
+ : m_navigationFunction(0)
+ , m_newWindowFunction(0)
+ , m_contentFunction(0)
+{
+}
+
+PolicyCallback::~PolicyCallback()
+{
+}
+
+void PolicyCallback::clear()
+{
+ clearRequest();
+ m_navigationFunction = 0;
+ m_newWindowFunction = 0;
+ m_contentFunction = 0;
+}
+
+void PolicyCallback::set(const ResourceRequest& request, PassRefPtr<FormState> formState,
+ NavigationPolicyDecisionFunction function, void* argument)
+{
+ m_request = request;
+ m_formState = formState;
+ m_frameName = String();
+
+ m_navigationFunction = function;
+ m_newWindowFunction = 0;
+ m_contentFunction = 0;
+ m_argument = argument;
+}
+
+void PolicyCallback::set(const ResourceRequest& request, PassRefPtr<FormState> formState,
+ 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;
+ m_contentFunction = 0;
+ m_argument = argument;
+}
+
+void PolicyCallback::set(ContentPolicyDecisionFunction function, void* argument)
+{
+ m_request = ResourceRequest();
+ m_formState = 0;
+ m_frameName = String();
+
+ m_navigationFunction = 0;
+ m_newWindowFunction = 0;
+ m_contentFunction = function;
+ m_argument = argument;
+}
+
+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, m_navigationAction, shouldContinue);
+ ASSERT(!m_contentFunction);
+}
+
+void PolicyCallback::call(PolicyAction action)
+{
+ ASSERT(!m_navigationFunction);
+ ASSERT(!m_newWindowFunction);
+ ASSERT(m_contentFunction);
+ m_contentFunction(m_argument, action);
+}
+
+void PolicyCallback::clearRequest()
+{
+ m_request = ResourceRequest();
+ m_formState = 0;
+ m_frameName = String();
+}
+
+void PolicyCallback::cancel()
+{
+ clearRequest();
+ 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, m_navigationAction, false);
+ if (m_contentFunction)
+ m_contentFunction(m_argument, PolicyIgnore);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/PolicyCallback.h b/Source/WebCore/loader/PolicyCallback.h
new file mode 100644
index 0000000..415a3e3
--- /dev/null
+++ b/Source/WebCore/loader/PolicyCallback.h
@@ -0,0 +1,82 @@
+/*
+ * 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/)
+ *
+ * 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 PolicyCallback_h
+#define PolicyCallback_h
+
+#include "FrameLoaderTypes.h"
+#include "NavigationAction.h"
+#include "ResourceRequest.h"
+#include <wtf/RefPtr.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+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, const NavigationAction&, bool shouldContinue);
+typedef void (*ContentPolicyDecisionFunction)(void* argument, PolicyAction);
+
+class PolicyCallback {
+public:
+ PolicyCallback();
+ ~PolicyCallback();
+
+ void clear();
+ void set(const ResourceRequest&, PassRefPtr<FormState>,
+ NavigationPolicyDecisionFunction, void* argument);
+ void set(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&,
+ NewWindowPolicyDecisionFunction, void* argument);
+ void set(ContentPolicyDecisionFunction, void* argument);
+
+ const ResourceRequest& request() const { return m_request; }
+ void clearRequest();
+
+ void call(bool shouldContinue);
+ void call(PolicyAction);
+ void cancel();
+
+private:
+ ResourceRequest m_request;
+ RefPtr<FormState> m_formState;
+ String m_frameName;
+ NavigationAction m_navigationAction;
+
+ NavigationPolicyDecisionFunction m_navigationFunction;
+ NewWindowPolicyDecisionFunction m_newWindowFunction;
+ ContentPolicyDecisionFunction m_contentFunction;
+ void* m_argument;
+};
+
+} // namespace WebCore
+
+#endif // PolicyCallback_h
diff --git a/Source/WebCore/loader/PolicyChecker.cpp b/Source/WebCore/loader/PolicyChecker.cpp
new file mode 100644
index 0000000..2680386
--- /dev/null
+++ b/Source/WebCore/loader/PolicyChecker.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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/)
+ *
+ * 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 "PolicyChecker.h"
+
+#include "DocumentLoader.h"
+#include "FormState.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "HTMLFormElement.h"
+
+namespace WebCore {
+
+PolicyChecker::PolicyChecker(Frame* frame)
+ : m_frame(frame)
+ , m_delegateIsDecidingNavigationPolicy(false)
+ , m_delegateIsHandlingUnimplementablePolicy(false)
+ , m_loadType(FrameLoadTypeStandard)
+{
+}
+
+void PolicyChecker::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function, void* argument)
+{
+ checkNavigationPolicy(newRequest, m_frame->loader()->activeDocumentLoader(), 0, function, argument);
+}
+
+void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader,
+ PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument)
+{
+ NavigationAction action = loader->triggeringAction();
+ if (action.isEmpty()) {
+ action = NavigationAction(request.url(), NavigationTypeOther);
+ loader->setTriggeringAction(action);
+ }
+
+ // Don't ask more than once for the same request or if we are loading an empty URL.
+ // This avoids confusion on the part of the client.
+ if (equalIgnoringHeaderFields(request, loader->lastCheckedRequest()) || (!request.isNull() && request.url().isEmpty())) {
+ function(argument, request, 0, true);
+ loader->setLastCheckedRequest(request);
+ return;
+ }
+
+ // We are always willing to show alternate content for unreachable URLs;
+ // treat it like a reload so it maintains the right state for b/f list.
+ if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) {
+ if (isBackForwardLoadType(m_loadType))
+ m_loadType = FrameLoadTypeReload;
+ function(argument, request, 0, true);
+ return;
+ }
+
+ loader->setLastCheckedRequest(request);
+
+ m_callback.set(request, formState.get(), function, argument);
+
+ m_delegateIsDecidingNavigationPolicy = true;
+ m_frame->loader()->client()->dispatchDecidePolicyForNavigationAction(&PolicyChecker::continueAfterNavigationPolicy,
+ action, request, formState);
+ m_delegateIsDecidingNavigationPolicy = false;
+}
+
+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, action, function, argument);
+ m_frame->loader()->client()->dispatchDecidePolicyForNewWindowAction(&PolicyChecker::continueAfterNewWindowPolicy,
+ action, request, formState, frameName);
+}
+
+void PolicyChecker::checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction function, void* argument)
+{
+ m_callback.set(function, argument);
+ m_frame->loader()->client()->dispatchDecidePolicyForMIMEType(&PolicyChecker::continueAfterContentPolicy,
+ MIMEType, m_frame->loader()->activeDocumentLoader()->request());
+}
+
+void PolicyChecker::cancelCheck()
+{
+ m_frame->loader()->client()->cancelPolicyCheck();
+ m_callback.clear();
+}
+
+void PolicyChecker::stopCheck()
+{
+ m_frame->loader()->client()->cancelPolicyCheck();
+ PolicyCallback callback = m_callback;
+ m_callback.clear();
+ callback.cancel();
+}
+
+void PolicyChecker::cannotShowMIMEType(const ResourceResponse& response)
+{
+ handleUnimplementablePolicy(m_frame->loader()->client()->cannotShowMIMETypeError(response));
+}
+
+void PolicyChecker::continueLoadAfterWillSubmitForm(PolicyAction)
+{
+ // See header file for an explaination of why this function
+ // isn't like the others.
+ m_frame->loader()->continueLoadAfterWillSubmitForm();
+}
+
+void PolicyChecker::continueAfterNavigationPolicy(PolicyAction policy)
+{
+ PolicyCallback callback = m_callback;
+ m_callback.clear();
+
+ bool shouldContinue = policy == PolicyUse;
+
+ switch (policy) {
+ case PolicyIgnore:
+ callback.clearRequest();
+ break;
+ case PolicyDownload:
+ m_frame->loader()->client()->startDownload(callback.request());
+ callback.clearRequest();
+ break;
+ case PolicyUse: {
+ ResourceRequest request(callback.request());
+
+ if (!m_frame->loader()->client()->canHandleRequest(request)) {
+ handleUnimplementablePolicy(m_frame->loader()->cannotShowURLError(callback.request()));
+ callback.clearRequest();
+ shouldContinue = false;
+ }
+ break;
+ }
+ }
+
+ callback.call(shouldContinue);
+}
+
+void PolicyChecker::continueAfterNewWindowPolicy(PolicyAction policy)
+{
+ PolicyCallback callback = m_callback;
+ m_callback.clear();
+
+ switch (policy) {
+ case PolicyIgnore:
+ callback.clearRequest();
+ break;
+ case PolicyDownload:
+ m_frame->loader()->client()->startDownload(callback.request());
+ callback.clearRequest();
+ break;
+ case PolicyUse:
+ break;
+ }
+
+ callback.call(policy == PolicyUse);
+}
+
+void PolicyChecker::continueAfterContentPolicy(PolicyAction policy)
+{
+ PolicyCallback callback = m_callback;
+ m_callback.clear();
+ callback.call(policy);
+}
+
+void PolicyChecker::handleUnimplementablePolicy(const ResourceError& error)
+{
+ m_delegateIsHandlingUnimplementablePolicy = true;
+ m_frame->loader()->client()->dispatchUnableToImplementPolicy(error);
+ m_delegateIsHandlingUnimplementablePolicy = false;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/PolicyChecker.h b/Source/WebCore/loader/PolicyChecker.h
new file mode 100644
index 0000000..541729c
--- /dev/null
+++ b/Source/WebCore/loader/PolicyChecker.h
@@ -0,0 +1,97 @@
+/*
+ * 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/)
+ *
+ * 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 PolicyChecker_h
+#define PolicyChecker_h
+
+#include "FrameLoaderTypes.h"
+#include "PlatformString.h"
+#include "PolicyCallback.h"
+#include "ResourceRequest.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class DocumentLoader;
+class FormState;
+class Frame;
+class NavigationAction;
+class ResourceError;
+class ResourceResponse;
+
+class PolicyChecker : public Noncopyable {
+public:
+ PolicyChecker(Frame*);
+
+ void checkNavigationPolicy(const ResourceRequest&, DocumentLoader*, PassRefPtr<FormState>, NavigationPolicyDecisionFunction, void* argument);
+ void checkNavigationPolicy(const ResourceRequest&, NavigationPolicyDecisionFunction, void* argument);
+ void checkNewWindowPolicy(const NavigationAction&, NewWindowPolicyDecisionFunction, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, void* argument);
+ void checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction, void* argument);
+
+ // FIXME: These are different. They could use better names.
+ void cancelCheck();
+ void stopCheck();
+
+ void cannotShowMIMEType(const ResourceResponse&);
+
+ FrameLoadType loadType() const { return m_loadType; }
+ void setLoadType(FrameLoadType loadType) { m_loadType = loadType; }
+
+ bool delegateIsDecidingNavigationPolicy() const { return m_delegateIsDecidingNavigationPolicy; }
+ bool delegateIsHandlingUnimplementablePolicy() const { return m_delegateIsHandlingUnimplementablePolicy; }
+
+ // FIXME: This function is a cheat. Basically, this is just an asynchronouc callback
+ // from the FrameLoaderClient, but this callback uses the policy types and so has to
+ // live on this object. In the long term, we should create a type for non-policy
+ // callbacks from the FrameLoaderClient and remove this vestige. I just don't have
+ // the heart to hack on all the platforms to make that happen right now.
+ void continueLoadAfterWillSubmitForm(PolicyAction);
+
+private:
+ void continueAfterNavigationPolicy(PolicyAction);
+ void continueAfterNewWindowPolicy(PolicyAction);
+ void continueAfterContentPolicy(PolicyAction);
+
+ void handleUnimplementablePolicy(const ResourceError&);
+
+ Frame* m_frame;
+
+ bool m_delegateIsDecidingNavigationPolicy;
+ bool m_delegateIsHandlingUnimplementablePolicy;
+
+ // This identifies the type of navigation action which prompted this load. Note
+ // that WebKit conveys this value as the WebActionNavigationTypeKey value
+ // on navigation action delegate callbacks.
+ FrameLoadType m_loadType;
+ PolicyCallback m_callback;
+};
+
+} // namespace WebCore
+
+#endif // PolicyChecker_h
diff --git a/Source/WebCore/loader/ProgressTracker.cpp b/Source/WebCore/loader/ProgressTracker.cpp
new file mode 100644
index 0000000..cd15433
--- /dev/null
+++ b/Source/WebCore/loader/ProgressTracker.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ProgressTracker.h"
+
+#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;
+
+namespace WebCore {
+
+// Always start progress at initialProgressValue. This helps provide feedback as
+// soon as a load starts.
+static const double initialProgressValue = 0.1;
+
+// Similarly, always leave space at the end. This helps show the user that we're not done
+// until we're done.
+static const double finalProgressValue = 0.9; // 1.0 - initialProgressValue
+
+static const int progressItemDefaultEstimatedLength = 1024 * 16;
+
+struct ProgressItem : Noncopyable {
+ ProgressItem(long long length)
+ : bytesReceived(0)
+ , estimatedLength(length) { }
+
+ long long bytesReceived;
+ long long estimatedLength;
+};
+
+unsigned long ProgressTracker::s_uniqueIdentifier = 0;
+
+ProgressTracker::ProgressTracker()
+ : m_totalPageAndResourceBytesToLoad(0)
+ , m_totalBytesReceived(0)
+ , m_lastNotifiedProgressValue(0)
+ , m_lastNotifiedProgressTime(0)
+ , m_progressNotificationInterval(0.02)
+ , m_progressNotificationTimeInterval(0.1)
+ , m_finalProgressChangedSent(false)
+ , m_progressValue(0)
+ , m_numProgressTrackedFrames(0)
+{
+}
+
+ProgressTracker::~ProgressTracker()
+{
+ deleteAllValues(m_progressItems);
+}
+
+double ProgressTracker::estimatedProgress() const
+{
+ return m_progressValue;
+}
+
+void ProgressTracker::reset()
+{
+ deleteAllValues(m_progressItems);
+ m_progressItems.clear();
+
+ m_totalPageAndResourceBytesToLoad = 0;
+ m_totalBytesReceived = 0;
+ m_progressValue = 0;
+ m_lastNotifiedProgressValue = 0;
+ m_lastNotifiedProgressTime = 0;
+ m_finalProgressChangedSent = false;
+ m_numProgressTrackedFrames = 0;
+ m_originatingProgressFrame = 0;
+}
+
+void ProgressTracker::progressStarted(Frame* frame)
+{
+ 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();
+
+ if (m_numProgressTrackedFrames == 0 || m_originatingProgressFrame == frame) {
+ reset();
+ m_progressValue = initialProgressValue;
+ m_originatingProgressFrame = frame;
+
+ m_originatingProgressFrame->loader()->client()->postProgressStartedNotification();
+ }
+ m_numProgressTrackedFrames++;
+
+ frame->loader()->client()->didChangeEstimatedProgress();
+}
+
+void ProgressTracker::progressCompleted(Frame* frame)
+{
+ 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;
+
+ frame->loader()->client()->willChangeEstimatedProgress();
+
+ m_numProgressTrackedFrames--;
+ if (m_numProgressTrackedFrames == 0 ||
+ (frame == m_originatingProgressFrame && m_numProgressTrackedFrames != 0))
+ finalProgressComplete();
+
+ frame->loader()->client()->didChangeEstimatedProgress();
+}
+
+void ProgressTracker::finalProgressComplete()
+{
+ LOG(Progress, "Final progress complete (%p)", this);
+
+ RefPtr<Frame> frame = m_originatingProgressFrame.release();
+
+ // Before resetting progress value be sure to send client a least one notification
+ // with final progress value.
+ if (!m_finalProgressChangedSent) {
+ m_progressValue = 1;
+ frame->loader()->client()->postProgressEstimateChangedNotification();
+ }
+
+ reset();
+
+ frame->loader()->client()->setMainFrameDocumentReady(true);
+ frame->loader()->client()->postProgressFinishedNotification();
+}
+
+void ProgressTracker::incrementProgress(unsigned long identifier, const ResourceResponse& response)
+{
+ 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;
+
+ long long estimatedLength = response.expectedContentLength();
+ if (estimatedLength < 0)
+ estimatedLength = progressItemDefaultEstimatedLength;
+
+ m_totalPageAndResourceBytesToLoad += estimatedLength;
+
+ if (ProgressItem* item = m_progressItems.get(identifier)) {
+ item->bytesReceived = 0;
+ item->estimatedLength = estimatedLength;
+ } else
+ m_progressItems.set(identifier, adoptPtr(new ProgressItem(estimatedLength)).leakPtr());
+}
+
+void ProgressTracker::incrementProgress(unsigned long identifier, const char*, int length)
+{
+ ProgressItem* item = m_progressItems.get(identifier);
+
+ // FIXME: Can this ever happen?
+ if (!item)
+ return;
+
+ RefPtr<Frame> frame = m_originatingProgressFrame;
+
+ frame->loader()->client()->willChangeEstimatedProgress();
+
+ unsigned bytesReceived = length;
+ double increment, percentOfRemainingBytes;
+ long long remainingBytes, estimatedBytesForPendingRequests;
+
+ item->bytesReceived += bytesReceived;
+ if (item->bytesReceived > item->estimatedLength) {
+ m_totalPageAndResourceBytesToLoad += ((item->bytesReceived * 2) - item->estimatedLength);
+ item->estimatedLength = item->bytesReceived * 2;
+ }
+
+ int numPendingOrLoadingRequests = frame->loader()->numPendingOrLoadingRequests(true);
+ estimatedBytesForPendingRequests = progressItemDefaultEstimatedLength * numPendingOrLoadingRequests;
+ remainingBytes = ((m_totalPageAndResourceBytesToLoad + estimatedBytesForPendingRequests) - m_totalBytesReceived);
+ if (remainingBytes > 0) // Prevent divide by 0.
+ percentOfRemainingBytes = (double)bytesReceived / (double)remainingBytes;
+ else
+ percentOfRemainingBytes = 1.0;
+
+ // 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()->stateMachine()->firstLayoutDone();
+ double maxProgressValue = useClampedMaxProgress ? 0.5 : finalProgressValue;
+ increment = (maxProgressValue - m_progressValue) * percentOfRemainingBytes;
+ m_progressValue += increment;
+ m_progressValue = min(m_progressValue, maxProgressValue);
+ ASSERT(m_progressValue >= initialProgressValue);
+
+ m_totalBytesReceived += bytesReceived;
+
+ double now = currentTime();
+ double notifiedProgressTimeDelta = now - m_lastNotifiedProgressTime;
+
+ 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) &&
+ m_numProgressTrackedFrames > 0) {
+ if (!m_finalProgressChangedSent) {
+ if (m_progressValue == 1)
+ m_finalProgressChangedSent = true;
+
+ frame->loader()->client()->postProgressEstimateChangedNotification();
+
+ m_lastNotifiedProgressValue = m_progressValue;
+ m_lastNotifiedProgressTime = now;
+ }
+ }
+
+ frame->loader()->client()->didChangeEstimatedProgress();
+}
+
+void ProgressTracker::completeProgress(unsigned long identifier)
+{
+ ProgressItem* item = m_progressItems.get(identifier);
+
+ // This can happen if a load fails without receiving any response data.
+ if (!item)
+ return;
+
+ // Adjust the total expected bytes to account for any overage/underage.
+ long long delta = item->bytesReceived - item->estimatedLength;
+ m_totalPageAndResourceBytesToLoad += delta;
+ item->estimatedLength = item->bytesReceived;
+
+ m_progressItems.remove(identifier);
+ delete item;
+}
+
+unsigned long ProgressTracker::createUniqueIdentifier()
+{
+ return ++s_uniqueIdentifier;
+}
+
+
+}
diff --git a/Source/WebCore/loader/ProgressTracker.h b/Source/WebCore/loader/ProgressTracker.h
new file mode 100644
index 0000000..5d5b6b2
--- /dev/null
+++ b/Source/WebCore/loader/ProgressTracker.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ProgressTracker_h
+#define ProgressTracker_h
+
+#include <wtf/HashMap.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class Frame;
+class ResourceResponse;
+struct ProgressItem;
+
+class ProgressTracker : public Noncopyable {
+public:
+ ProgressTracker();
+ ~ProgressTracker();
+
+ static unsigned long createUniqueIdentifier();
+
+ double estimatedProgress() const;
+
+ void progressStarted(Frame*);
+ void progressCompleted(Frame*);
+
+ void incrementProgress(unsigned long identifier, const ResourceResponse&);
+ void incrementProgress(unsigned long identifier, const char*, int);
+ void completeProgress(unsigned long identifier);
+
+ long long totalPageAndResourceBytesToLoad() const { return m_totalPageAndResourceBytesToLoad; }
+ long long totalBytesReceived() const { return m_totalBytesReceived; }
+
+private:
+ void reset();
+ void finalProgressComplete();
+
+ static unsigned long s_uniqueIdentifier;
+
+ long long m_totalPageAndResourceBytesToLoad;
+ long long m_totalBytesReceived;
+ double m_lastNotifiedProgressValue;
+ double m_lastNotifiedProgressTime;
+ double m_progressNotificationInterval;
+ double m_progressNotificationTimeInterval;
+ bool m_finalProgressChangedSent;
+ double m_progressValue;
+ RefPtr<Frame> m_originatingProgressFrame;
+
+ int m_numProgressTrackedFrames;
+ HashMap<unsigned long, ProgressItem*> m_progressItems;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/ResourceLoadNotifier.cpp b/Source/WebCore/loader/ResourceLoadNotifier.cpp
new file mode 100644
index 0000000..d002ef3
--- /dev/null
+++ b/Source/WebCore/loader/ResourceLoadNotifier.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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/)
+ *
+ * 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 "ResourceLoadNotifier.h"
+
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "InspectorController.h"
+#include "Page.h"
+#include "ProgressTracker.h"
+#include "ResourceLoader.h"
+
+namespace WebCore {
+
+ResourceLoadNotifier::ResourceLoadNotifier(Frame* frame)
+ : m_frame(frame)
+{
+}
+
+void ResourceLoadNotifier::didReceiveAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge)
+{
+ m_frame->loader()->client()->dispatchDidReceiveAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge);
+}
+
+void ResourceLoadNotifier::didCancelAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge)
+{
+ m_frame->loader()->client()->dispatchDidCancelAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge);
+}
+
+void ResourceLoadNotifier::willSendRequest(ResourceLoader* loader, ResourceRequest& clientRequest, const ResourceResponse& redirectResponse)
+{
+ m_frame->loader()->applyUserAgent(clientRequest);
+
+ dispatchWillSendRequest(loader->documentLoader(), loader->identifier(), clientRequest, redirectResponse);
+}
+
+void ResourceLoadNotifier::didReceiveResponse(ResourceLoader* loader, const ResourceResponse& r)
+{
+ loader->documentLoader()->addResponse(r);
+
+ if (Page* page = m_frame->page())
+ page->progress()->incrementProgress(loader->identifier(), r);
+
+ dispatchDidReceiveResponse(loader->documentLoader(), loader->identifier(), r);
+}
+
+void ResourceLoadNotifier::didReceiveData(ResourceLoader* loader, const char* data, int length, int lengthReceived)
+{
+ if (Page* page = m_frame->page())
+ page->progress()->incrementProgress(loader->identifier(), data, length);
+
+ dispatchDidReceiveContentLength(loader->documentLoader(), loader->identifier(), lengthReceived);
+}
+
+void ResourceLoadNotifier::didFinishLoad(ResourceLoader* loader, double finishTime)
+{
+ if (Page* page = m_frame->page())
+ page->progress()->completeProgress(loader->identifier());
+ dispatchDidFinishLoading(loader->documentLoader(), loader->identifier(), finishTime);
+}
+
+void ResourceLoadNotifier::didFailToLoad(ResourceLoader* loader, const ResourceError& error)
+{
+ if (Page* page = m_frame->page())
+ page->progress()->completeProgress(loader->identifier());
+
+ if (!error.isNull())
+ m_frame->loader()->client()->dispatchDidFailLoading(loader->documentLoader(), loader->identifier(), error);
+
+#if ENABLE(INSPECTOR)
+ if (Page* page = m_frame->page())
+ page->inspectorController()->didFailLoading(loader->identifier(), error);
+#endif
+}
+
+void ResourceLoadNotifier::assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
+{
+ m_frame->loader()->client()->assignIdentifierToInitialRequest(identifier, loader, request);
+
+#if ENABLE(INSPECTOR)
+ if (Page* page = m_frame->page())
+ page->inspectorController()->identifierForInitialRequest(identifier, loader, request);
+#endif
+}
+
+void ResourceLoadNotifier::dispatchWillSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
+{
+ StringImpl* oldRequestURL = request.url().string().impl();
+ m_frame->loader()->documentLoader()->didTellClientAboutLoad(request.url());
+
+ m_frame->loader()->client()->dispatchWillSendRequest(loader, identifier, request, redirectResponse);
+
+ // If the URL changed, then we want to put that new URL in the "did tell client" set too.
+ if (!request.isNull() && oldRequestURL != request.url().string().impl())
+ m_frame->loader()->documentLoader()->didTellClientAboutLoad(request.url());
+
+#if ENABLE(INSPECTOR)
+ if (Page* page = m_frame->page())
+ page->inspectorController()->willSendRequest(identifier, request, redirectResponse);
+#endif
+}
+
+void ResourceLoadNotifier::dispatchDidReceiveResponse(DocumentLoader* loader, unsigned long identifier, const ResourceResponse& r)
+{
+ m_frame->loader()->client()->dispatchDidReceiveResponse(loader, identifier, r);
+
+#if ENABLE(INSPECTOR)
+ if (Page* page = m_frame->page())
+ page->inspectorController()->didReceiveResponse(identifier, loader, r);
+#endif
+}
+
+void ResourceLoadNotifier::dispatchDidReceiveContentLength(DocumentLoader* loader, unsigned long identifier, int length)
+{
+ m_frame->loader()->client()->dispatchDidReceiveContentLength(loader, identifier, length);
+
+#if ENABLE(INSPECTOR)
+ if (Page* page = m_frame->page())
+ page->inspectorController()->didReceiveContentLength(identifier, length);
+#endif
+}
+
+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, finishTime);
+#endif
+}
+
+void ResourceLoadNotifier::dispatchTransferLoadingResourceFromPage(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request, Page* oldPage)
+{
+ ASSERT(oldPage != m_frame->page());
+ m_frame->loader()->client()->transferLoadingResourceFromPage(identifier, loader, request, oldPage);
+
+ oldPage->progress()->completeProgress(identifier);
+}
+
+void ResourceLoadNotifier::sendRemainingDelegateMessages(DocumentLoader* loader, unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error)
+{
+ if (!response.isNull())
+ dispatchDidReceiveResponse(loader, identifier, response);
+
+ if (length > 0)
+ dispatchDidReceiveContentLength(loader, identifier, length);
+
+ if (error.isNull())
+ dispatchDidFinishLoading(loader, identifier, 0);
+ else
+ m_frame->loader()->client()->dispatchDidFailLoading(loader, identifier, error);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/ResourceLoadNotifier.h b/Source/WebCore/loader/ResourceLoadNotifier.h
new file mode 100644
index 0000000..2f10856
--- /dev/null
+++ b/Source/WebCore/loader/ResourceLoadNotifier.h
@@ -0,0 +1,74 @@
+/*
+ * 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/)
+ *
+ * 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 ResourceLoadNotifier_h
+#define ResourceLoadNotifier_h
+
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class AuthenticationChallenge;
+class DocumentLoader;
+class Frame;
+class Page;
+class ResourceError;
+class ResourceLoader;
+class ResourceResponse;
+class ResourceRequest;
+
+class ResourceLoadNotifier : public Noncopyable {
+public:
+ ResourceLoadNotifier(Frame*);
+
+ void didReceiveAuthenticationChallenge(ResourceLoader*, const AuthenticationChallenge&);
+ void didCancelAuthenticationChallenge(ResourceLoader*, const AuthenticationChallenge&);
+
+ void willSendRequest(ResourceLoader*, ResourceRequest&, const ResourceResponse& redirectResponse);
+ void didReceiveResponse(ResourceLoader*, const ResourceResponse&);
+ void didReceiveData(ResourceLoader*, const char*, int, int lengthReceived);
+ void didFinishLoad(ResourceLoader*, double finishTime);
+ void didFailToLoad(ResourceLoader*, const ResourceError&);
+
+ 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, double finishTime);
+ void dispatchTransferLoadingResourceFromPage(unsigned long, DocumentLoader*, const ResourceRequest&, Page*);
+
+ void sendRemainingDelegateMessages(DocumentLoader*, unsigned long identifier, const ResourceResponse&, int length, const ResourceError&);
+
+private:
+ Frame* m_frame;
+};
+
+} // namespace WebCore
+
+#endif // ResourceLoadNotifier_h
diff --git a/Source/WebCore/loader/ResourceLoadScheduler.cpp b/Source/WebCore/loader/ResourceLoadScheduler.cpp
new file mode 100644
index 0000000..8cf2c18
--- /dev/null
+++ b/Source/WebCore/loader/ResourceLoadScheduler.cpp
@@ -0,0 +1,289 @@
+/*
+ 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.
+ Copyright (C) 2010 Google Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "ResourceLoadScheduler.h"
+
+#include "Document.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "InspectorInstrumentation.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "NetscapePlugInStreamLoader.h"
+#include "ResourceLoader.h"
+#include "ResourceRequest.h"
+#include "SubresourceLoader.h"
+#include <wtf/text/CString.h>
+
+#define REQUEST_MANAGEMENT_ENABLED 1
+
+namespace WebCore {
+
+#if REQUEST_MANAGEMENT_ENABLED
+static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20;
+// Match the parallel connection count used by the networking layer.
+static unsigned maxRequestsInFlightPerHost;
+#else
+static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000;
+static const unsigned maxRequestsInFlightPerHost = 10000;
+#endif
+
+ResourceLoadScheduler::HostInformation* ResourceLoadScheduler::hostForURL(const KURL& url, CreateHostPolicy createHostPolicy)
+{
+ if (!url.protocolInHTTPFamily())
+ return m_nonHTTPProtocolHost;
+
+ m_hosts.checkConsistency();
+ String hostName = url.host();
+ HostInformation* host = m_hosts.get(hostName);
+ if (!host && createHostPolicy == CreateIfNotFound) {
+ host = new HostInformation(hostName, maxRequestsInFlightPerHost);
+ m_hosts.add(hostName, host);
+ }
+ return host;
+}
+
+ResourceLoadScheduler* resourceLoadScheduler()
+{
+ ASSERT(isMainThread());
+ DEFINE_STATIC_LOCAL(ResourceLoadScheduler, resourceLoadScheduler, ());
+ return &resourceLoadScheduler;
+}
+
+ResourceLoadScheduler::ResourceLoadScheduler()
+ : m_nonHTTPProtocolHost(new HostInformation(String(), maxRequestsInFlightForNonHTTPProtocols))
+ , m_requestTimer(this, &ResourceLoadScheduler::requestTimerFired)
+ , m_isSuspendingPendingRequests(false)
+ , m_isSerialLoadingEnabled(false)
+{
+#if REQUEST_MANAGEMENT_ENABLED
+ maxRequestsInFlightPerHost = initializeMaximumHTTPConnectionCountPerHost();
+#endif
+}
+
+PassRefPtr<SubresourceLoader> ResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, SubresourceLoaderClient* client, const ResourceRequest& request, ResourceLoadPriority priority, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks, bool shouldContentSniff)
+{
+ PassRefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, client, request, securityCheck, sendResourceLoadCallbacks, shouldContentSniff);
+ if (loader)
+ scheduleLoad(loader.get(), priority);
+ return loader;
+}
+
+PassRefPtr<NetscapePlugInStreamLoader> ResourceLoadScheduler::schedulePluginStreamLoad(Frame* frame, NetscapePlugInStreamLoaderClient* client, const ResourceRequest& request)
+{
+ PassRefPtr<NetscapePlugInStreamLoader> loader = NetscapePlugInStreamLoader::create(frame, client, request);
+ if (loader)
+ scheduleLoad(loader.get(), ResourceLoadPriorityLow);
+ return loader;
+}
+
+void ResourceLoadScheduler::addMainResourceLoad(ResourceLoader* resourceLoader)
+{
+ hostForURL(resourceLoader->url(), CreateIfNotFound)->addLoadInProgress(resourceLoader);
+}
+
+void ResourceLoadScheduler::scheduleLoad(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
+{
+ ASSERT(resourceLoader);
+ ASSERT(priority != ResourceLoadPriorityUnresolved);
+#if !REQUEST_MANAGEMENT_ENABLED
+ priority = ResourceLoadPriorityHighest;
+#endif
+
+ LOG(ResourceLoading, "ResourceLoadScheduler::load resource %p '%s'", resourceLoader, resourceLoader->url().string().latin1().data());
+ HostInformation* host = hostForURL(resourceLoader->url(), CreateIfNotFound);
+ bool hadRequests = host->hasRequests();
+ host->schedule(resourceLoader, priority);
+
+ if (priority > ResourceLoadPriorityLow || !resourceLoader->url().protocolInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) {
+ // Try to request important resources immediately.
+ servePendingRequests(host, priority);
+ } else {
+ // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones.
+ InspectorInstrumentation::didScheduleResourceRequest(resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame()->document() : 0, resourceLoader->url());
+ scheduleServePendingRequests();
+ }
+}
+
+void ResourceLoadScheduler::remove(ResourceLoader* resourceLoader)
+{
+ ASSERT(resourceLoader);
+
+ HostInformation* host = hostForURL(resourceLoader->url());
+ if (host)
+ host->remove(resourceLoader);
+ scheduleServePendingRequests();
+}
+
+void ResourceLoadScheduler::crossOriginRedirectReceived(ResourceLoader* resourceLoader, const KURL& redirectURL)
+{
+ HostInformation* oldHost = hostForURL(resourceLoader->url());
+ ASSERT(oldHost);
+ HostInformation* newHost = hostForURL(redirectURL, CreateIfNotFound);
+
+ if (oldHost->name() == newHost->name())
+ return;
+
+ newHost->addLoadInProgress(resourceLoader);
+ oldHost->remove(resourceLoader);
+}
+
+void ResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority)
+{
+ LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests. m_isSuspendingPendingRequests=%d", m_isSuspendingPendingRequests);
+ if (m_isSuspendingPendingRequests)
+ return;
+
+ m_requestTimer.stop();
+
+ servePendingRequests(m_nonHTTPProtocolHost, minimumPriority);
+
+ Vector<HostInformation*> hostsToServe;
+ m_hosts.checkConsistency();
+ HostMap::iterator end = m_hosts.end();
+ for (HostMap::iterator iter = m_hosts.begin(); iter != end; ++iter)
+ hostsToServe.append(iter->second);
+
+ int size = hostsToServe.size();
+ for (int i = 0; i < size; ++i) {
+ HostInformation* host = hostsToServe[i];
+ if (host->hasRequests())
+ servePendingRequests(host, minimumPriority);
+ else
+ delete m_hosts.take(host->name());
+ }
+}
+
+void ResourceLoadScheduler::servePendingRequests(HostInformation* host, ResourceLoadPriority minimumPriority)
+{
+ LOG(ResourceLoading, "ResourceLoadScheduler::servePendingRequests HostInformation.m_name='%s'", host->name().latin1().data());
+
+ for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) {
+ HostInformation::RequestQueue& requestsPending = host->requestsPending((ResourceLoadPriority) priority);
+
+ while (!requestsPending.isEmpty()) {
+ RefPtr<ResourceLoader> resourceLoader = requestsPending.first();
+
+ // 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.
+ Document* document = resourceLoader->frameLoader() ? resourceLoader->frameLoader()->frame()->document() : 0;
+ bool shouldLimitRequests = !host->name().isNull() || (document && (document->parsing() || !document->haveStylesheetsLoaded()));
+ if (shouldLimitRequests && host->limitRequests())
+ return;
+
+ requestsPending.removeFirst();
+ host->addLoadInProgress(resourceLoader.get());
+ resourceLoader->start();
+ }
+ }
+}
+
+void ResourceLoadScheduler::suspendPendingRequests()
+{
+ ASSERT(!m_isSuspendingPendingRequests);
+ m_isSuspendingPendingRequests = true;
+}
+
+void ResourceLoadScheduler::resumePendingRequests()
+{
+ ASSERT(m_isSuspendingPendingRequests);
+ m_isSuspendingPendingRequests = false;
+ if (!m_hosts.isEmpty() || m_nonHTTPProtocolHost->hasRequests())
+ scheduleServePendingRequests();
+}
+
+void ResourceLoadScheduler::scheduleServePendingRequests()
+{
+ LOG(ResourceLoading, "ResourceLoadScheduler::scheduleServePendingRequests, m_requestTimer.isActive()=%u", m_requestTimer.isActive());
+ if (!m_requestTimer.isActive())
+ m_requestTimer.startOneShot(0);
+}
+
+void ResourceLoadScheduler::requestTimerFired(Timer<ResourceLoadScheduler>*)
+{
+ LOG(ResourceLoading, "ResourceLoadScheduler::requestTimerFired\n");
+ servePendingRequests();
+}
+
+ResourceLoadScheduler::HostInformation::HostInformation(const String& name, unsigned maxRequestsInFlight)
+ : m_name(name)
+ , m_maxRequestsInFlight(maxRequestsInFlight)
+{
+}
+
+ResourceLoadScheduler::HostInformation::~HostInformation()
+{
+ ASSERT(m_requestsLoading.isEmpty());
+ for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++)
+ ASSERT(m_requestsPending[p].isEmpty());
+}
+
+void ResourceLoadScheduler::HostInformation::schedule(ResourceLoader* resourceLoader, ResourceLoadPriority priority)
+{
+ m_requestsPending[priority].append(resourceLoader);
+}
+
+void ResourceLoadScheduler::HostInformation::addLoadInProgress(ResourceLoader* resourceLoader)
+{
+ LOG(ResourceLoading, "HostInformation '%s' loading '%s'. Current count %d", m_name.latin1().data(), resourceLoader->url().string().latin1().data(), m_requestsLoading.size());
+ m_requestsLoading.add(resourceLoader);
+}
+
+void ResourceLoadScheduler::HostInformation::remove(ResourceLoader* resourceLoader)
+{
+ if (m_requestsLoading.contains(resourceLoader)) {
+ m_requestsLoading.remove(resourceLoader);
+ return;
+ }
+
+ for (int priority = ResourceLoadPriorityHighest; priority >= ResourceLoadPriorityLowest; --priority) {
+ RequestQueue::iterator end = m_requestsPending[priority].end();
+ for (RequestQueue::iterator it = m_requestsPending[priority].begin(); it != end; ++it) {
+ if (*it == resourceLoader) {
+ m_requestsPending[priority].remove(it);
+ return;
+ }
+ }
+ }
+}
+
+bool ResourceLoadScheduler::HostInformation::hasRequests() const
+{
+ if (!m_requestsLoading.isEmpty())
+ return true;
+ for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) {
+ if (!m_requestsPending[p].isEmpty())
+ return true;
+ }
+ return false;
+}
+
+bool ResourceLoadScheduler::HostInformation::limitRequests() const
+{
+ return m_requestsLoading.size() >= (resourceLoadScheduler()->isSerialLoadingEnabled() ? 1 : m_maxRequestsInFlight);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/ResourceLoadScheduler.h b/Source/WebCore/loader/ResourceLoadScheduler.h
new file mode 100644
index 0000000..163b67e
--- /dev/null
+++ b/Source/WebCore/loader/ResourceLoadScheduler.h
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ Copyright (C) 2010 Google 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.
+ */
+
+#ifndef ResourceLoadScheduler_h
+#define ResourceLoadScheduler_h
+
+#include "FrameLoaderTypes.h"
+#include "PlatformString.h"
+#include "ResourceLoadPriority.h"
+#include "Timer.h"
+#include <wtf/Deque.h>
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+class Frame;
+class KURL;
+class NetscapePlugInStreamLoader;
+class NetscapePlugInStreamLoaderClient;
+class ResourceLoader;
+class ResourceRequest;
+class SubresourceLoader;
+class SubresourceLoaderClient;
+
+class ResourceLoadScheduler : public Noncopyable {
+public:
+ friend ResourceLoadScheduler* resourceLoadScheduler();
+
+ PassRefPtr<SubresourceLoader> scheduleSubresourceLoad(Frame*, SubresourceLoaderClient*, const ResourceRequest&, ResourceLoadPriority = ResourceLoadPriorityLow, SecurityCheckPolicy = DoSecurityCheck, bool sendResourceLoadCallbacks = true, bool shouldContentSniff = true);
+ PassRefPtr<NetscapePlugInStreamLoader> schedulePluginStreamLoad(Frame*, NetscapePlugInStreamLoaderClient*, const ResourceRequest&);
+ void addMainResourceLoad(ResourceLoader*);
+ void remove(ResourceLoader*);
+ void crossOriginRedirectReceived(ResourceLoader*, const KURL& redirectURL);
+
+ void servePendingRequests(ResourceLoadPriority minimumPriority = ResourceLoadPriorityVeryLow);
+ void suspendPendingRequests();
+ void resumePendingRequests();
+
+ bool isSerialLoadingEnabled() const { return m_isSerialLoadingEnabled; }
+ void setSerialLoadingEnabled(bool b) { m_isSerialLoadingEnabled = b; }
+
+private:
+ ResourceLoadScheduler();
+ ~ResourceLoadScheduler();
+
+ void scheduleLoad(ResourceLoader*, ResourceLoadPriority);
+ void scheduleServePendingRequests();
+ void requestTimerFired(Timer<ResourceLoadScheduler>*);
+
+ class HostInformation : public Noncopyable {
+ public:
+ HostInformation(const String&, unsigned);
+ ~HostInformation();
+
+ const String& name() const { return m_name; }
+ void schedule(ResourceLoader*, ResourceLoadPriority = ResourceLoadPriorityVeryLow);
+ void addLoadInProgress(ResourceLoader*);
+ void remove(ResourceLoader*);
+ bool hasRequests() const;
+ bool limitRequests() const;
+
+ typedef Deque<RefPtr<ResourceLoader> > RequestQueue;
+ RequestQueue& requestsPending(ResourceLoadPriority priority) { return m_requestsPending[priority]; }
+
+ private:
+ RequestQueue m_requestsPending[ResourceLoadPriorityHighest + 1];
+ typedef HashSet<RefPtr<ResourceLoader> > RequestMap;
+ RequestMap m_requestsLoading;
+ const String m_name;
+ const int m_maxRequestsInFlight;
+ };
+
+ enum CreateHostPolicy {
+ CreateIfNotFound,
+ FindOnly
+ };
+
+ HostInformation* hostForURL(const KURL&, CreateHostPolicy = FindOnly);
+ void servePendingRequests(HostInformation*, ResourceLoadPriority);
+
+ typedef HashMap<String, HostInformation*, StringHash> HostMap;
+ HostMap m_hosts;
+ HostInformation* m_nonHTTPProtocolHost;
+
+ Timer<ResourceLoadScheduler> m_requestTimer;
+
+ bool m_isSuspendingPendingRequests;
+ bool m_isSerialLoadingEnabled;
+};
+
+ResourceLoadScheduler* resourceLoadScheduler();
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/ResourceLoader.cpp b/Source/WebCore/loader/ResourceLoader.cpp
new file mode 100644
index 0000000..bc56000
--- /dev/null
+++ b/Source/WebCore/loader/ResourceLoader.cpp
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * (C) 2007 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ResourceLoader.h"
+
+#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 "ResourceError.h"
+#include "ResourceHandle.h"
+#include "ResourceLoadScheduler.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+PassRefPtr<SharedBuffer> ResourceLoader::resourceData()
+{
+ if (m_resourceData)
+ return m_resourceData;
+
+ if (ResourceHandle::supportsBufferedData() && m_handle)
+ return m_handle->bufferedData();
+
+ return 0;
+}
+
+ResourceLoader::ResourceLoader(Frame* frame, bool sendResourceLoadCallbacks, bool shouldContentSniff)
+ : m_frame(frame)
+ , m_documentLoader(frame->loader()->activeDocumentLoader())
+ , m_identifier(0)
+ , m_reachedTerminalState(false)
+ , m_cancelled(false)
+ , m_calledDidFinishLoad(false)
+ , m_sendResourceLoadCallbacks(sendResourceLoadCallbacks)
+ , m_shouldContentSniff(shouldContentSniff)
+ , m_shouldBufferData(true)
+ , m_defersLoading(frame->page()->defersLoading())
+{
+}
+
+ResourceLoader::~ResourceLoader()
+{
+ ASSERT(m_reachedTerminalState);
+}
+
+void ResourceLoader::releaseResources()
+{
+ ASSERT(!m_reachedTerminalState);
+
+ // It's possible that when we release the handle, it will be
+ // deallocated and release the last reference to this object.
+ // We need to retain to avoid accessing the object after it
+ // has been deallocated and also to avoid reentering this method.
+ RefPtr<ResourceLoader> protector(this);
+
+ m_frame = 0;
+ m_documentLoader = 0;
+
+ // We need to set reachedTerminalState to true before we release
+ // the resources to prevent a double dealloc of WebView <rdar://problem/4372628>
+ m_reachedTerminalState = true;
+
+ m_identifier = 0;
+
+ resourceLoadScheduler()->remove(this);
+
+ if (m_handle) {
+ // Clear out the ResourceHandle's client so that it doesn't try to call
+ // us back after we release it, unless it has been replaced by someone else.
+ if (m_handle->client() == this)
+ m_handle->setClient(0);
+ m_handle = 0;
+ }
+
+ m_resourceData = 0;
+ m_deferredRequest = ResourceRequest();
+}
+
+bool ResourceLoader::init(const ResourceRequest& r)
+{
+ ASSERT(!m_handle);
+ ASSERT(m_request.isNull());
+ ASSERT(m_deferredRequest.isNull());
+ 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());
+ }
+
+ m_request = clientRequest;
+
+ willSendRequest(m_request, ResourceResponse());
+ if (m_request.isNull()) {
+ didFail(frameLoader()->cancelledError(m_request));
+ return false;
+ }
+
+ return true;
+}
+
+void ResourceLoader::start()
+{
+ ASSERT(!m_handle);
+ ASSERT(!m_request.isNull());
+ ASSERT(m_deferredRequest.isNull());
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ if (m_documentLoader->scheduleArchiveLoad(this, m_request, m_request.url()))
+ return;
+#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (m_documentLoader->applicationCacheHost()->maybeLoadResource(this, m_request, m_request.url()))
+ return;
+#endif
+
+ if (m_defersLoading) {
+ m_deferredRequest = m_request;
+ return;
+ }
+
+ if (!m_reachedTerminalState)
+ m_handle = ResourceHandle::create(m_frame->loader()->networkingContext(), m_request, this, m_defersLoading, m_shouldContentSniff);
+}
+
+void ResourceLoader::setDefersLoading(bool defers)
+{
+ m_defersLoading = defers;
+ if (m_handle)
+ m_handle->setDefersLoading(defers);
+ if (!defers && !m_deferredRequest.isNull()) {
+ m_request = m_deferredRequest;
+ m_deferredRequest = ResourceRequest();
+ start();
+ }
+}
+
+#if PLATFORM(ANDROID)
+// TODO: This needs upstreaming to WebKit.
+void ResourceLoader::pauseLoad(bool pause)
+{
+ if (m_handle)
+ m_handle->pauseLoad(pause);
+}
+#endif
+
+FrameLoader* ResourceLoader::frameLoader() const
+{
+ if (!m_frame)
+ return 0;
+ return m_frame->loader();
+}
+
+void ResourceLoader::setShouldBufferData(bool shouldBufferData)
+{
+ m_shouldBufferData = shouldBufferData;
+
+ // Reset any already buffered data
+ if (!m_shouldBufferData)
+ m_resourceData = 0;
+}
+
+
+void ResourceLoader::addData(const char* data, int length, bool allAtOnce)
+{
+ if (!m_shouldBufferData)
+ return;
+
+ if (allAtOnce) {
+ m_resourceData = SharedBuffer::create(data, length);
+ return;
+ }
+
+ if (ResourceHandle::supportsBufferedData()) {
+ // Buffer data only if the connection has handed us the data because is has stopped buffering it.
+ if (m_resourceData)
+ m_resourceData->append(data, length);
+ } else {
+ if (!m_resourceData)
+ m_resourceData = SharedBuffer::create(data, length);
+ else
+ m_resourceData->append(data, length);
+ }
+}
+
+void ResourceLoader::clearResourceData()
+{
+ if (m_resourceData)
+ m_resourceData->clear();
+}
+
+void ResourceLoader::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
+{
+ // Protect this in this delegate method since the additional processing can do
+ // anything including possibly derefing this; one example of this is Radar 3266216.
+ RefPtr<ResourceLoader> protector(this);
+
+ ASSERT(!m_reachedTerminalState);
+
+ if (m_sendResourceLoadCallbacks) {
+ if (!m_identifier) {
+ m_identifier = m_frame->page()->progress()->createUniqueIdentifier();
+ frameLoader()->notifier()->assignIdentifierToInitialRequest(m_identifier, documentLoader(), request);
+ }
+
+ frameLoader()->notifier()->willSendRequest(this, request, redirectResponse);
+ }
+
+ if (!redirectResponse.isNull())
+ resourceLoadScheduler()->crossOriginRedirectReceived(this, request.url());
+ m_request = request;
+}
+
+void ResourceLoader::didSendData(unsigned long long, unsigned long long)
+{
+}
+
+void ResourceLoader::didReceiveResponse(const ResourceResponse& r)
+{
+ ASSERT(!m_reachedTerminalState);
+
+ // Protect this in this delegate method since the additional processing can do
+ // anything including possibly derefing this; one example of this is Radar 3266216.
+ RefPtr<ResourceLoader> protector(this);
+
+ m_response = r;
+
+ if (FormData* data = m_request.httpBody())
+ data->removeGeneratedFilesIfNeeded();
+
+ if (m_sendResourceLoadCallbacks)
+ frameLoader()->notifier()->didReceiveResponse(this, m_response);
+}
+
+void ResourceLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce)
+{
+ // The following assertions are not quite valid here, since a subclass
+ // might override didReceiveData in a way that invalidates them. This
+ // happens with the steps listed in 3266216
+ // ASSERT(con == connection);
+ // ASSERT(!m_reachedTerminalState);
+
+ // Protect this in this delegate method since the additional processing can do
+ // anything including possibly derefing this; one example of this is Radar 3266216.
+ RefPtr<ResourceLoader> protector(this);
+
+ addData(data, length, allAtOnce);
+ // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
+ // However, with today's computers and networking speeds, this won't happen in practice.
+ // Could be an issue with a giant local file.
+ if (m_sendResourceLoadCallbacks && m_frame)
+ frameLoader()->notifier()->didReceiveData(this, data, length, static_cast<int>(lengthReceived));
+}
+
+void ResourceLoader::willStopBufferingData(const char* data, int length)
+{
+ if (!m_shouldBufferData)
+ return;
+
+ ASSERT(!m_resourceData);
+ m_resourceData = SharedBuffer::create(data, length);
+}
+
+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.
+ if (m_cancelled)
+ return;
+ ASSERT(!m_reachedTerminalState);
+
+ didFinishLoadingOnePart(finishTime);
+ releaseResources();
+}
+
+void ResourceLoader::didFinishLoadingOnePart(double finishTime)
+{
+ if (m_cancelled)
+ return;
+ ASSERT(!m_reachedTerminalState);
+
+ if (m_calledDidFinishLoad)
+ return;
+ m_calledDidFinishLoad = true;
+ if (m_sendResourceLoadCallbacks)
+ frameLoader()->notifier()->didFinishLoad(this, finishTime);
+}
+
+void ResourceLoader::didFail(const ResourceError& error)
+{
+ if (m_cancelled)
+ return;
+ ASSERT(!m_reachedTerminalState);
+
+ // Protect this in this delegate method since the additional processing can do
+ // anything including possibly derefing this; one example of this is Radar 3266216.
+ RefPtr<ResourceLoader> protector(this);
+
+ if (FormData* data = m_request.httpBody())
+ data->removeGeneratedFilesIfNeeded();
+
+ if (m_sendResourceLoadCallbacks && !m_calledDidFinishLoad)
+ frameLoader()->notifier()->didFailToLoad(this, error);
+
+ releaseResources();
+}
+
+void ResourceLoader::didCancel(const ResourceError& error)
+{
+ ASSERT(!m_cancelled);
+ ASSERT(!m_reachedTerminalState);
+
+ if (FormData* data = m_request.httpBody())
+ data->removeGeneratedFilesIfNeeded();
+
+ // This flag prevents bad behavior when loads that finish cause the
+ // load itself to be cancelled (which could happen with a javascript that
+ // changes the window location). This is used to prevent both the body
+ // of this method and the body of connectionDidFinishLoading: running
+ // for a single delegate. Canceling wins.
+ m_cancelled = true;
+
+ if (m_handle)
+ m_handle->clearAuthentication();
+
+ m_documentLoader->cancelPendingSubstituteLoad(this);
+ if (m_handle) {
+ m_handle->cancel();
+ m_handle = 0;
+ }
+ if (m_sendResourceLoadCallbacks && m_identifier && !m_calledDidFinishLoad)
+ frameLoader()->notifier()->didFailToLoad(this, error);
+
+ releaseResources();
+}
+
+void ResourceLoader::cancel()
+{
+ cancel(ResourceError());
+}
+
+void ResourceLoader::cancel(const ResourceError& error)
+{
+ if (m_reachedTerminalState)
+ return;
+ if (!error.isNull())
+ didCancel(error);
+ else
+ didCancel(cancelledError());
+}
+
+const ResourceResponse& ResourceLoader::response() const
+{
+ return m_response;
+}
+
+ResourceError ResourceLoader::cancelledError()
+{
+ return frameLoader()->cancelledError(m_request);
+}
+
+ResourceError ResourceLoader::blockedError()
+{
+ return frameLoader()->blockedError(m_request);
+}
+
+ResourceError ResourceLoader::cannotShowURLError()
+{
+ return frameLoader()->cannotShowURLError(m_request);
+}
+
+void ResourceLoader::willSendRequest(ResourceHandle*, ResourceRequest& request, const ResourceResponse& redirectResponse)
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForRedirect(this, request, redirectResponse))
+ return;
+#endif
+ willSendRequest(request, redirectResponse);
+}
+
+void ResourceLoader::didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+{
+ didSendData(bytesSent, totalBytesToBeSent);
+}
+
+void ResourceLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ 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*, double finishTime)
+{
+ didFinishLoading(finishTime);
+}
+
+void ResourceLoader::didFail(ResourceHandle*, const ResourceError& error)
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForError(this, error))
+ return;
+#endif
+ didFail(error);
+}
+
+void ResourceLoader::wasBlocked(ResourceHandle*)
+{
+ didFail(blockedError());
+}
+
+void ResourceLoader::cannotShowURL(ResourceHandle*)
+{
+ didFail(cannotShowURLError());
+}
+
+bool ResourceLoader::shouldUseCredentialStorage()
+{
+ RefPtr<ResourceLoader> protector(this);
+ return frameLoader()->shouldUseCredentialStorage(this);
+}
+
+void ResourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
+{
+ // Protect this in this delegate method since the additional processing can do
+ // anything including possibly derefing this; one example of this is Radar 3266216.
+ RefPtr<ResourceLoader> protector(this);
+ frameLoader()->notifier()->didReceiveAuthenticationChallenge(this, challenge);
+}
+
+void ResourceLoader::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
+{
+ // Protect this in this delegate method since the additional processing can do
+ // anything including possibly derefing this; one example of this is Radar 3266216.
+ RefPtr<ResourceLoader> protector(this);
+ 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();
+}
+
+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() && 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/Source/WebCore/loader/ResourceLoader.h b/Source/WebCore/loader/ResourceLoader.h
new file mode 100644
index 0000000..e5c5d3f
--- /dev/null
+++ b/Source/WebCore/loader/ResourceLoader.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ResourceLoader_h
+#define ResourceLoader_h
+
+#include "ResourceHandleClient.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include <wtf/RefCounted.h>
+#include "AuthenticationChallenge.h"
+#include "KURL.h"
+
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+ class ApplicationCacheHost;
+ class DocumentLoader;
+ class Frame;
+ class FrameLoader;
+ class ProtectionSpace;
+ class ResourceHandle;
+ class SharedBuffer;
+
+ class ResourceLoader : public RefCounted<ResourceLoader>, protected ResourceHandleClient {
+ public:
+ virtual ~ResourceLoader();
+
+ void cancel();
+
+ virtual bool init(const ResourceRequest&);
+
+ FrameLoader* frameLoader() const;
+ DocumentLoader* documentLoader() const { return m_documentLoader.get(); }
+
+ virtual void cancel(const ResourceError&);
+ ResourceError cancelledError();
+ ResourceError blockedError();
+ ResourceError cannotShowURLError();
+
+ virtual void setDefersLoading(bool);
+#if PLATFORM(ANDROID)
+// TODO: This needs upstreaming to WebKit.
+ virtual void pauseLoad(bool);
+#endif
+
+ void setIdentifier(unsigned long identifier) { m_identifier = identifier; }
+ unsigned long identifier() const { return m_identifier; }
+
+ virtual void releaseResources();
+ const ResourceResponse& response() const;
+
+ virtual void addData(const char*, int, bool allAtOnce);
+ virtual PassRefPtr<SharedBuffer> resourceData();
+ void clearResourceData();
+
+ virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
+ virtual void didReceiveResponse(const ResourceResponse&);
+ virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce);
+ virtual void didReceiveCachedMetadata(const char*, int) { }
+ void willStopBufferingData(const char*, int);
+ 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
+ virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual void didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
+ virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
+ virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived);
+ virtual void 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*);
+ virtual void willStopBufferingData(ResourceHandle*, const char* data, int length) { willStopBufferingData(data, length); }
+ virtual bool shouldUseCredentialStorage(ResourceHandle*) { return shouldUseCredentialStorage(); }
+ virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didReceiveAuthenticationChallenge(challenge); }
+ virtual void didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didCancelAuthenticationChallenge(challenge); }
+#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)
+ virtual NSCachedURLResponse* willCacheResponse(ResourceHandle*, NSCachedURLResponse*);
+#endif
+#if USE(CFNETWORK)
+ virtual bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef);
+#endif
+#if ENABLE(BLOB)
+ virtual AsyncFileStream* createAsyncFileStream(FileStreamClient*);
+#endif
+
+ const KURL& url() const { return m_request.url(); }
+ ResourceHandle* handle() const { return m_handle.get(); }
+ bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; }
+
+ bool reachedTerminalState() const { return m_reachedTerminalState; }
+
+ void setShouldBufferData(bool shouldBufferData);
+
+ protected:
+ ResourceLoader(Frame*, bool sendResourceLoadCallbacks, bool shouldContentSniff);
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ friend class ApplicationCacheHost; // for access to request()
+#endif
+ friend class ResourceLoadScheduler; // for access to start()
+ // start() actually sends the load to the network (unless the load is being
+ // deferred) and should only be called by ResourceLoadScheduler or setDefersLoading().
+ void start();
+
+ virtual void didCancel(const ResourceError&);
+ void didFinishLoadingOnePart(double finishTime);
+
+ const ResourceRequest& request() const { return m_request; }
+ bool cancelled() const { return m_cancelled; }
+ bool defersLoading() const { return m_defersLoading; }
+
+ RefPtr<ResourceHandle> m_handle;
+ RefPtr<Frame> m_frame;
+ RefPtr<DocumentLoader> m_documentLoader;
+ ResourceResponse m_response;
+
+ private:
+ ResourceRequest m_request;
+ RefPtr<SharedBuffer> m_resourceData;
+
+ unsigned long m_identifier;
+
+ bool m_reachedTerminalState;
+ bool m_cancelled;
+ bool m_calledDidFinishLoad;
+
+ bool m_sendResourceLoadCallbacks;
+ bool m_shouldContentSniff;
+ bool m_shouldBufferData;
+ bool m_defersLoading;
+ ResourceRequest m_deferredRequest;
+ };
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/SinkDocument.cpp b/Source/WebCore/loader/SinkDocument.cpp
new file mode 100644
index 0000000..47535dc
--- /dev/null
+++ b/Source/WebCore/loader/SinkDocument.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SinkDocument.h"
+
+#include "RawDataDocumentParser.h"
+
+namespace WebCore {
+
+class SinkDocumentParser : public RawDataDocumentParser {
+public:
+ static PassRefPtr<SinkDocumentParser> create(SinkDocument* document)
+ {
+ return adoptRef(new SinkDocumentParser(document));
+ }
+
+private:
+ SinkDocumentParser(SinkDocument* document)
+ : RawDataDocumentParser(document)
+ {
+ }
+
+ // Ignore all data.
+ virtual void appendBytes(DocumentWriter*, const char*, int, bool) { }
+};
+
+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/Source/WebCore/loader/SinkDocument.h b/Source/WebCore/loader/SinkDocument.h
new file mode 100644
index 0000000..50152ff
--- /dev/null
+++ b/Source/WebCore/loader/SinkDocument.h
@@ -0,0 +1,49 @@
+/*
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SinkDocument_h
+#define SinkDocument_h
+
+#include "HTMLDocument.h"
+
+namespace WebCore {
+
+class SinkDocument : public HTMLDocument {
+public:
+ static PassRefPtr<SinkDocument> create(Frame* frame, const KURL& url)
+ {
+ return adoptRef(new SinkDocument(frame, url));
+ }
+
+private:
+ SinkDocument(Frame*, const KURL&);
+
+ virtual PassRefPtr<DocumentParser> createParser();
+};
+
+
+}; // namespace WebCore
+
+#endif // SinkDocument_h
diff --git a/Source/WebCore/loader/SubframeLoader.cpp b/Source/WebCore/loader/SubframeLoader.cpp
new file mode 100644
index 0000000..eba3173
--- /dev/null
+++ b/Source/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(m_frame->document()->securityOrigin(), 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->marginWidth();
+ marginHeight = o->marginHeight();
+ }
+
+ 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/Source/WebCore/loader/SubframeLoader.h b/Source/WebCore/loader/SubframeLoader.h
new file mode 100644
index 0000000..a573045
--- /dev/null
+++ b/Source/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/Source/WebCore/loader/SubresourceLoader.cpp b/Source/WebCore/loader/SubresourceLoader.cpp
new file mode 100644
index 0000000..69f7906
--- /dev/null
+++ b/Source/WebCore/loader/SubresourceLoader.cpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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 "SubresourceLoader.h"
+
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "ResourceHandle.h"
+#include "SecurityOrigin.h"
+#include "SubresourceLoaderClient.h"
+#include <wtf/RefCountedLeakCounter.h>
+
+namespace WebCore {
+
+#ifndef NDEBUG
+static WTF::RefCountedLeakCounter subresourceLoaderCounter("SubresourceLoader");
+#endif
+
+SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* client, bool sendResourceLoadCallbacks, bool shouldContentSniff)
+ : ResourceLoader(frame, sendResourceLoadCallbacks, shouldContentSniff)
+ , m_client(client)
+ , m_loadingMultipartContent(false)
+{
+#ifndef NDEBUG
+ subresourceLoaderCounter.increment();
+#endif
+}
+
+SubresourceLoader::~SubresourceLoader()
+{
+#ifndef NDEBUG
+ subresourceLoaderCounter.decrement();
+#endif
+}
+
+PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, SubresourceLoaderClient* client, const ResourceRequest& request, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks, bool shouldContentSniff)
+{
+ if (!frame)
+ return 0;
+
+ FrameLoader* fl = frame->loader();
+ if (securityCheck == DoSecurityCheck && (fl->state() == FrameStateProvisional || !fl->activeDocumentLoader() || fl->activeDocumentLoader()->isStopping()))
+ return 0;
+
+ ResourceRequest newRequest = request;
+
+ 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();
+ else if (!request.httpReferrer())
+ newRequest.setHTTPReferrer(fl->outgoingReferrer());
+ FrameLoader::addHTTPOriginIfNeeded(newRequest, fl->outgoingOrigin());
+
+ fl->addExtraFieldsToSubresourceRequest(newRequest);
+
+ RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, client, sendResourceLoadCallbacks, shouldContentSniff)));
+ subloader->documentLoader()->addSubresourceLoader(subloader.get());
+ if (!subloader->init(newRequest))
+ return 0;
+
+ return subloader.release();
+}
+
+void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
+{
+ // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
+ KURL previousURL = request().url();
+
+ ResourceLoader::willSendRequest(newRequest, redirectResponse);
+ if (!previousURL.isNull() && !newRequest.isNull() && previousURL != newRequest.url() && m_client)
+ m_client->willSendRequest(this, newRequest, redirectResponse);
+}
+
+void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+{
+ RefPtr<SubresourceLoader> protect(this);
+
+ if (m_client)
+ m_client->didSendData(this, bytesSent, totalBytesToBeSent);
+}
+
+void SubresourceLoader::didReceiveResponse(const ResourceResponse& r)
+{
+ ASSERT(!r.isNull());
+
+ if (r.isMultipart())
+ m_loadingMultipartContent = true;
+
+ // 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->didReceiveResponse(this, r);
+
+ // The loader can cancel a load if it receives a multipart response for a non-image
+ if (reachedTerminalState())
+ return;
+ ResourceLoader::didReceiveResponse(r);
+
+ RefPtr<SharedBuffer> buffer = resourceData();
+ if (m_loadingMultipartContent && buffer && buffer->size()) {
+ // Since a subresource loader does not load multipart sections progressively,
+ // deliver the previously received data to the loader all at once now.
+ // Then clear the data to make way for the next multipart section.
+ if (m_client)
+ m_client->didReceiveData(this, buffer->data(), buffer->size());
+ clearResourceData();
+
+ // After the first multipart section is complete, signal to delegates that this load is "finished"
+ m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
+ didFinishLoadingOnePart(0);
+ }
+}
+
+void SubresourceLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce)
+{
+ // 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);
+
+ ResourceLoader::didReceiveData(data, length, lengthReceived, allAtOnce);
+
+ // A subresource loader does not load multipart sections progressively.
+ // So don't deliver any data to the loader yet.
+ if (!m_loadingMultipartContent && m_client)
+ m_client->didReceiveData(this, data, length);
+}
+
+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;
+ ASSERT(!reachedTerminalState());
+
+ // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
+ RefPtr<SubresourceLoader> protect(this);
+
+ if (m_client)
+ m_client->didFinishLoading(this);
+
+ m_handle = 0;
+
+ if (cancelled())
+ return;
+ m_documentLoader->removeSubresourceLoader(this);
+ ResourceLoader::didFinishLoading(finishTime);
+}
+
+void SubresourceLoader::didFail(const ResourceError& error)
+{
+ if (cancelled())
+ return;
+ ASSERT(!reachedTerminalState());
+
+ // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
+ RefPtr<SubresourceLoader> protect(this);
+
+ if (m_client)
+ m_client->didFail(this, error);
+
+ m_handle = 0;
+
+ if (cancelled())
+ return;
+ m_documentLoader->removeSubresourceLoader(this);
+ ResourceLoader::didFail(error);
+}
+
+void SubresourceLoader::didCancel(const ResourceError& error)
+{
+ ASSERT(!reachedTerminalState());
+
+ // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
+ RefPtr<SubresourceLoader> protect(this);
+
+ if (m_client)
+ m_client->didFail(this, error);
+
+ if (cancelled())
+ return;
+
+ // The only way the subresource loader can reach the terminal state here is if the run loop spins when calling
+ // m_client->didFail. This should in theory not happen which is why the assert is here.
+ ASSERT(!reachedTerminalState());
+ if (reachedTerminalState())
+ return;
+
+ m_documentLoader->removeSubresourceLoader(this);
+ ResourceLoader::didCancel(error);
+}
+
+bool SubresourceLoader::shouldUseCredentialStorage()
+{
+ RefPtr<SubresourceLoader> protect(this);
+
+ bool shouldUse;
+ if (m_client && m_client->getShouldUseCredentialStorage(this, shouldUse))
+ return shouldUse;
+
+ return ResourceLoader::shouldUseCredentialStorage();
+}
+
+void SubresourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
+{
+ RefPtr<SubresourceLoader> protect(this);
+
+ 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 (reachedTerminalState())
+ return;
+
+ // It may have also handled authentication on its own.
+ if (!handle()->hasAuthenticationChallenge())
+ return;
+
+ ResourceLoader::didReceiveAuthenticationChallenge(challenge);
+}
+
+void SubresourceLoader::receivedCancellation(const AuthenticationChallenge& challenge)
+{
+ ASSERT(!reachedTerminalState());
+
+ RefPtr<SubresourceLoader> protect(this);
+
+ if (m_client)
+ m_client->receivedCancellation(this, challenge);
+
+ ResourceLoader::receivedCancellation(challenge);
+}
+
+
+}
diff --git a/Source/WebCore/loader/SubresourceLoader.h b/Source/WebCore/loader/SubresourceLoader.h
new file mode 100644
index 0000000..cb7ed81
--- /dev/null
+++ b/Source/WebCore/loader/SubresourceLoader.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2005, 2006, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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 SubresourceLoader_h
+#define SubresourceLoader_h
+
+#include "FrameLoaderTypes.h"
+#include "ResourceLoader.h"
+
+namespace WebCore {
+
+ class ResourceRequest;
+ class SubresourceLoaderClient;
+
+ class SubresourceLoader : public ResourceLoader {
+ public:
+ static PassRefPtr<SubresourceLoader> create(Frame*, SubresourceLoaderClient*, const ResourceRequest&, SecurityCheckPolicy = DoSecurityCheck, bool sendResourceLoadCallbacks = true, bool shouldContentSniff = true);
+
+ void clearClient() { m_client = 0; }
+
+ private:
+ SubresourceLoader(Frame*, SubresourceLoaderClient*, bool sendResourceLoadCallbacks, bool shouldContentSniff);
+ virtual ~SubresourceLoader();
+
+ virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
+ virtual void didReceiveResponse(const ResourceResponse&);
+ virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce);
+ virtual void didReceiveCachedMetadata(const char*, int);
+ virtual void didFinishLoading(double finishTime);
+ virtual void didFail(const ResourceError&);
+ virtual bool shouldUseCredentialStorage();
+ virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&);
+ virtual void receivedCancellation(const AuthenticationChallenge&);
+ virtual void didCancel(const ResourceError&);
+
+ SubresourceLoaderClient* m_client;
+ bool m_loadingMultipartContent;
+ };
+
+}
+
+#endif // SubresourceLoader_h
diff --git a/Source/WebCore/loader/SubresourceLoaderClient.h b/Source/WebCore/loader/SubresourceLoaderClient.h
new file mode 100644
index 0000000..e18abe3
--- /dev/null
+++ b/Source/WebCore/loader/SubresourceLoaderClient.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SubresourceLoaderClient_h
+#define SubresourceLoaderClient_h
+
+namespace WebCore {
+
+class AuthenticationChallenge;
+class ResourceError;
+class ResourceRequest;
+class ResourceResponse;
+class SubresourceLoader;
+
+class SubresourceLoaderClient {
+public:
+ virtual ~SubresourceLoaderClient() { }
+
+ // request may be modified
+ virtual void willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/) { }
+ virtual void didSendData(SubresourceLoader*, unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) { }
+
+ virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&) { }
+ virtual void didReceiveData(SubresourceLoader*, const char*, int /*lengthReceived*/) { }
+ virtual void didReceiveCachedMetadata(SubresourceLoader*, const char*, int /*lengthReceived*/) { }
+ virtual void didFinishLoading(SubresourceLoader*) { }
+ virtual void didFail(SubresourceLoader*, const ResourceError&) { }
+
+ virtual bool getShouldUseCredentialStorage(SubresourceLoader*, bool& /*shouldUseCredentialStorage*/) { return false; }
+ virtual void didReceiveAuthenticationChallenge(SubresourceLoader*, const AuthenticationChallenge&) { }
+ virtual void receivedCancellation(SubresourceLoader*, const AuthenticationChallenge&) { }
+
+};
+
+} // namespace WebCore
+
+#endif // SubresourceLoaderClient_h
diff --git a/Source/WebCore/loader/SubstituteData.h b/Source/WebCore/loader/SubstituteData.h
new file mode 100644
index 0000000..0b87b62
--- /dev/null
+++ b/Source/WebCore/loader/SubstituteData.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SubstituteData_h
+#define SubstituteData_h
+
+#include "KURL.h"
+#include "SharedBuffer.h"
+#include "PlatformString.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+ class SubstituteData {
+ public:
+ SubstituteData() { }
+
+ SubstituteData(PassRefPtr<SharedBuffer> content, const String& mimeType, const String& textEncoding, const KURL& failingURL, const KURL& responseURL = KURL())
+ : m_content(content)
+ , m_mimeType(mimeType)
+ , m_textEncoding(textEncoding)
+ , m_failingURL(failingURL)
+ , m_responseURL(responseURL)
+ {
+ }
+
+ bool isValid() const { return m_content != 0; }
+
+ const SharedBuffer* content() const { return m_content.get(); }
+ const String& mimeType() const { return m_mimeType; }
+ const String& textEncoding() const { return m_textEncoding; }
+ const KURL& failingURL() const { return m_failingURL; }
+ const KURL& responseURL() const { return m_responseURL; }
+
+ private:
+ RefPtr<SharedBuffer> m_content;
+ String m_mimeType;
+ String m_textEncoding;
+ KURL m_failingURL;
+ KURL m_responseURL;
+ };
+
+}
+
+#endif // SubstituteData_h
+
diff --git a/Source/WebCore/loader/SubstituteResource.h b/Source/WebCore/loader/SubstituteResource.h
new file mode 100644
index 0000000..15cbc6f
--- /dev/null
+++ b/Source/WebCore/loader/SubstituteResource.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SubstituteResource_h
+#define SubstituteResource_h
+
+#include <wtf/RefCounted.h>
+
+#include "KURL.h"
+#include "ResourceResponse.h"
+#include "SharedBuffer.h"
+
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class SubstituteResource : public RefCounted<SubstituteResource> {
+public:
+ virtual ~SubstituteResource() { }
+
+ const KURL& url() const { return m_url; }
+ const ResourceResponse& response() const { return m_response; }
+ SharedBuffer* data() const { return m_data.get(); }
+
+protected:
+ SubstituteResource(const KURL& url, const ResourceResponse& response, PassRefPtr<SharedBuffer> data)
+ : m_url(url)
+ , m_response(response)
+ , m_data(data)
+ {
+ ASSERT(m_data);
+ }
+
+private:
+ KURL m_url;
+ ResourceResponse m_response;
+ RefPtr<SharedBuffer> m_data;
+};
+
+}
+
+#endif // SubstituteResource_h
diff --git a/Source/WebCore/loader/TextResourceDecoder.cpp b/Source/WebCore/loader/TextResourceDecoder.cpp
new file mode 100644
index 0000000..c8198e3
--- /dev/null
+++ b/Source/WebCore/loader/TextResourceDecoder.cpp
@@ -0,0 +1,694 @@
+/*
+ Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ Copyright (C) 2005, 2006, 2007 Alexey Proskuryakov (ap@nypop.com)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+#include "config.h"
+#include "TextResourceDecoder.h"
+
+#include "DOMImplementation.h"
+#include "HTMLMetaCharsetParser.h"
+#include "HTMLNames.h"
+#include "TextCodec.h"
+#include "TextEncoding.h"
+#include "TextEncodingDetector.h"
+#include "TextEncodingRegistry.h"
+#include <wtf/ASCIICType.h>
+#include <wtf/StringExtras.h>
+
+using namespace WTF;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+// You might think we should put these find functions elsewhere, perhaps with the
+// similar functions that operate on UChar, but arguably only the decoder has
+// a reason to process strings of char rather than UChar.
+
+static int find(const char* subject, size_t subjectLength, const char* target)
+{
+ size_t targetLength = strlen(target);
+ if (targetLength > subjectLength)
+ return -1;
+ for (size_t i = 0; i <= subjectLength - targetLength; ++i) {
+ bool match = true;
+ for (size_t j = 0; j < targetLength; ++j) {
+ if (subject[i + j] != target[j]) {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ return i;
+ }
+ return -1;
+}
+
+static TextEncoding findTextEncoding(const char* encodingName, int length)
+{
+ Vector<char, 64> buffer(length + 1);
+ memcpy(buffer.data(), encodingName, length);
+ buffer[length] = '\0';
+ return buffer.data();
+}
+
+class KanjiCode {
+public:
+ enum Type { ASCII, JIS, EUC, SJIS, UTF16, UTF8 };
+ static enum Type judge(const char* str, int length);
+ static const int ESC = 0x1b;
+ static const unsigned char sjisMap[256];
+ static int ISkanji(int code)
+ {
+ if (code >= 0x100)
+ return 0;
+ return sjisMap[code & 0xff] & 1;
+ }
+ static int ISkana(int code)
+ {
+ if (code >= 0x100)
+ return 0;
+ return sjisMap[code & 0xff] & 2;
+ }
+};
+
+const unsigned char KanjiCode::sjisMap[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0
+};
+
+/*
+ * EUC-JP is
+ * [0xa1 - 0xfe][0xa1 - 0xfe]
+ * 0x8e[0xa1 - 0xfe](SS2)
+ * 0x8f[0xa1 - 0xfe][0xa1 - 0xfe](SS3)
+ *
+ * Shift_Jis is
+ * [0x81 - 0x9f, 0xe0 - 0xef(0xfe?)][0x40 - 0x7e, 0x80 - 0xfc]
+ *
+ * Shift_Jis Hankaku Kana is
+ * [0xa1 - 0xdf]
+ */
+
+/*
+ * KanjiCode::judge() is based on judge_jcode() from jvim
+ * http://hp.vector.co.jp/authors/VA003457/vim/
+ *
+ * Special Thanks to Kenichi Tsuchida
+ */
+
+enum KanjiCode::Type KanjiCode::judge(const char* str, int size)
+{
+ enum Type code;
+ int i;
+ int bfr = false; /* Kana Moji */
+ int bfk = 0; /* EUC Kana */
+ int sjis = 0;
+ int euc = 0;
+
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(str);
+
+ code = ASCII;
+
+ i = 0;
+ while (i < size) {
+ if (ptr[i] == ESC && (size - i >= 3)) {
+ if ((ptr[i + 1] == '$' && ptr[i + 2] == 'B')
+ || (ptr[i + 1] == '(' && ptr[i + 2] == 'B')) {
+ code = JIS;
+ goto breakBreak;
+ } else if ((ptr[i + 1] == '$' && ptr[i + 2] == '@')
+ || (ptr[i + 1] == '(' && ptr[i + 2] == 'J')) {
+ code = JIS;
+ goto breakBreak;
+ } else if (ptr[i + 1] == '(' && ptr[i + 2] == 'I') {
+ code = JIS;
+ i += 3;
+ } else if (ptr[i + 1] == ')' && ptr[i + 2] == 'I') {
+ code = JIS;
+ i += 3;
+ } else {
+ i++;
+ }
+ bfr = false;
+ bfk = 0;
+ } else {
+ if (ptr[i] < 0x20) {
+ bfr = false;
+ bfk = 0;
+ /* ?? check kudokuten ?? && ?? hiragana ?? */
+ if ((i >= 2) && (ptr[i - 2] == 0x81)
+ && (0x41 <= ptr[i - 1] && ptr[i - 1] <= 0x49)) {
+ code = SJIS;
+ sjis += 100; /* kudokuten */
+ } else if ((i >= 2) && (ptr[i - 2] == 0xa1)
+ && (0xa2 <= ptr[i - 1] && ptr[i - 1] <= 0xaa)) {
+ code = EUC;
+ euc += 100; /* kudokuten */
+ } else if ((i >= 2) && (ptr[i - 2] == 0x82) && (0xa0 <= ptr[i - 1])) {
+ sjis += 40; /* hiragana */
+ } else if ((i >= 2) && (ptr[i - 2] == 0xa4) && (0xa0 <= ptr[i - 1])) {
+ euc += 40; /* hiragana */
+ }
+ } else {
+ /* ?? check hiragana or katana ?? */
+ if ((size - i > 1) && (ptr[i] == 0x82) && (0xa0 <= ptr[i + 1])) {
+ sjis++; /* hiragana */
+ } else if ((size - i > 1) && (ptr[i] == 0x83)
+ && (0x40 <= ptr[i + 1] && ptr[i + 1] <= 0x9f)) {
+ sjis++; /* katakana */
+ } else if ((size - i > 1) && (ptr[i] == 0xa4) && (0xa0 <= ptr[i + 1])) {
+ euc++; /* hiragana */
+ } else if ((size - i > 1) && (ptr[i] == 0xa5) && (0xa0 <= ptr[i + 1])) {
+ euc++; /* katakana */
+ }
+ if (bfr) {
+ if ((i >= 1) && (0x40 <= ptr[i] && ptr[i] <= 0xa0) && ISkanji(ptr[i - 1])) {
+ code = SJIS;
+ goto breakBreak;
+ } else if ((i >= 1) && (0x81 <= ptr[i - 1] && ptr[i - 1] <= 0x9f) && ((0x40 <= ptr[i] && ptr[i] < 0x7e) || (0x7e < ptr[i] && ptr[i] <= 0xfc))) {
+ code = SJIS;
+ goto breakBreak;
+ } else if ((i >= 1) && (0xfd <= ptr[i] && ptr[i] <= 0xfe) && (0xa1 <= ptr[i - 1] && ptr[i - 1] <= 0xfe)) {
+ code = EUC;
+ goto breakBreak;
+ } else if ((i >= 1) && (0xfd <= ptr[i - 1] && ptr[i - 1] <= 0xfe) && (0xa1 <= ptr[i] && ptr[i] <= 0xfe)) {
+ code = EUC;
+ goto breakBreak;
+ } else if ((i >= 1) && (ptr[i] < 0xa0 || 0xdf < ptr[i]) && (0x8e == ptr[i - 1])) {
+ code = SJIS;
+ goto breakBreak;
+ } else if (ptr[i] <= 0x7f) {
+ code = SJIS;
+ goto breakBreak;
+ } else {
+ if (0xa1 <= ptr[i] && ptr[i] <= 0xa6) {
+ euc++; /* sjis hankaku kana kigo */
+ } else if (0xa1 <= ptr[i] && ptr[i] <= 0xdf) {
+ ; /* sjis hankaku kana */
+ } else if (0xa1 <= ptr[i] && ptr[i] <= 0xfe) {
+ euc++;
+ } else if (0x8e == ptr[i]) {
+ euc++;
+ } else if (0x20 <= ptr[i] && ptr[i] <= 0x7f) {
+ sjis++;
+ }
+ bfr = false;
+ bfk = 0;
+ }
+ } else if (0x8e == ptr[i]) {
+ if (size - i <= 1) {
+ ;
+ } else if (0xa1 <= ptr[i + 1] && ptr[i + 1] <= 0xdf) {
+ /* EUC KANA or SJIS KANJI */
+ if (bfk == 1) {
+ euc += 100;
+ }
+ bfk++;
+ i++;
+ } else {
+ /* SJIS only */
+ code = SJIS;
+ goto breakBreak;
+ }
+ } else if (0x81 <= ptr[i] && ptr[i] <= 0x9f) {
+ /* SJIS only */
+ code = SJIS;
+ if ((size - i >= 1)
+ && ((0x40 <= ptr[i + 1] && ptr[i + 1] <= 0x7e)
+ || (0x80 <= ptr[i + 1] && ptr[i + 1] <= 0xfc))) {
+ goto breakBreak;
+ }
+ } else if (0xfd <= ptr[i] && ptr[i] <= 0xfe) {
+ /* EUC only */
+ code = EUC;
+ if ((size - i >= 1)
+ && (0xa1 <= ptr[i + 1] && ptr[i + 1] <= 0xfe)) {
+ goto breakBreak;
+ }
+ } else if (ptr[i] <= 0x7f) {
+ ;
+ } else {
+ bfr = true;
+ bfk = 0;
+ }
+ }
+ i++;
+ }
+ }
+ if (code == ASCII) {
+ if (sjis > euc) {
+ code = SJIS;
+ } else if (sjis < euc) {
+ code = EUC;
+ }
+ }
+breakBreak:
+ return (code);
+}
+
+TextResourceDecoder::ContentType TextResourceDecoder::determineContentType(const String& mimeType)
+{
+ if (equalIgnoringCase(mimeType, "text/css"))
+ return CSS;
+ if (equalIgnoringCase(mimeType, "text/html"))
+ return HTML;
+ if (DOMImplementation::isXMLMIMEType(mimeType))
+ return XML;
+ return PlainText;
+}
+
+const TextEncoding& TextResourceDecoder::defaultEncoding(ContentType contentType, const TextEncoding& specifiedDefaultEncoding)
+{
+ // Despite 8.5 "Text/xml with Omitted Charset" of RFC 3023, we assume UTF-8 instead of US-ASCII
+ // for text/xml. This matches Firefox.
+ if (contentType == XML)
+ return UTF8Encoding();
+ if (!specifiedDefaultEncoding.isValid())
+ return Latin1Encoding();
+ return specifiedDefaultEncoding;
+}
+
+TextResourceDecoder::TextResourceDecoder(const String& mimeType, const TextEncoding& specifiedDefaultEncoding, bool usesEncodingDetector)
+ : m_contentType(determineContentType(mimeType))
+ , m_encoding(defaultEncoding(m_contentType, specifiedDefaultEncoding))
+ , m_source(DefaultEncoding)
+ , m_hintEncoding(0)
+ , m_checkedForBOM(false)
+ , m_checkedForCSSCharset(false)
+ , m_checkedForHeadCharset(false)
+ , m_useLenientXMLDecoding(false)
+ , m_sawError(false)
+ , m_usesEncodingDetector(usesEncodingDetector)
+{
+}
+
+TextResourceDecoder::~TextResourceDecoder()
+{
+}
+
+void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSource source)
+{
+ // In case the encoding didn't exist, we keep the old one (helps some sites specifying invalid encodings).
+ if (!encoding.isValid())
+ return;
+
+ // When encoding comes from meta tag (i.e. it cannot be XML files sent via XHR),
+ // treat x-user-defined as windows-1252 (bug 18270)
+ if (source == EncodingFromMetaTag && strcasecmp(encoding.name(), "x-user-defined") == 0)
+ m_encoding = "windows-1252";
+ else if (source == EncodingFromMetaTag || source == EncodingFromXMLHeader || source == EncodingFromCSSCharset)
+ m_encoding = encoding.closestByteBasedEquivalent();
+ else
+ m_encoding = encoding;
+
+ m_codec.clear();
+ m_source = source;
+}
+
+// Returns the position of the encoding string.
+static int findXMLEncoding(const char* str, int len, int& encodingLength)
+{
+ int pos = find(str, len, "encoding");
+ if (pos == -1)
+ return -1;
+ pos += 8;
+
+ // Skip spaces and stray control characters.
+ while (pos < len && str[pos] <= ' ')
+ ++pos;
+
+ // Skip equals sign.
+ if (pos >= len || str[pos] != '=')
+ return -1;
+ ++pos;
+
+ // Skip spaces and stray control characters.
+ while (pos < len && str[pos] <= ' ')
+ ++pos;
+
+ // Skip quotation mark.
+ if (pos >= len)
+ return - 1;
+ char quoteMark = str[pos];
+ if (quoteMark != '"' && quoteMark != '\'')
+ return -1;
+ ++pos;
+
+ // Find the trailing quotation mark.
+ int end = pos;
+ while (end < len && str[end] != quoteMark)
+ ++end;
+ if (end >= len)
+ return -1;
+
+ encodingLength = end - pos;
+ return pos;
+}
+
+// true if there is more to parse
+static inline bool skipWhitespace(const char*& pos, const char* dataEnd)
+{
+ while (pos < dataEnd && (*pos == '\t' || *pos == ' '))
+ ++pos;
+ return pos != dataEnd;
+}
+
+size_t TextResourceDecoder::checkForBOM(const char* data, size_t len)
+{
+ // Check for UTF-16/32 or UTF-8 BOM mark at the beginning, which is a sure sign of a Unicode encoding.
+ // We let it override even a user-chosen encoding.
+ ASSERT(!m_checkedForBOM);
+
+ size_t lengthOfBOM = 0;
+
+ size_t bufferLength = m_buffer.size();
+
+ size_t buf1Len = bufferLength;
+ size_t buf2Len = len;
+ const unsigned char* buf1 = reinterpret_cast<const unsigned char*>(m_buffer.data());
+ const unsigned char* buf2 = reinterpret_cast<const unsigned char*>(data);
+ unsigned char c1 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
+ unsigned char c2 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
+ unsigned char c3 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0;
+ unsigned char c4 = buf2Len ? (--buf2Len, *buf2++) : 0;
+
+ // Check for the BOM.
+ if (c1 == 0xFF && c2 == 0xFE) {
+ if (c3 != 0 || c4 != 0) {
+ setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding);
+ lengthOfBOM = 2;
+ } else {
+ setEncoding(UTF32LittleEndianEncoding(), AutoDetectedEncoding);
+ lengthOfBOM = 4;
+ }
+ } else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) {
+ setEncoding(UTF8Encoding(), AutoDetectedEncoding);
+ lengthOfBOM = 3;
+ } else if (c1 == 0xFE && c2 == 0xFF) {
+ setEncoding(UTF16BigEndianEncoding(), AutoDetectedEncoding);
+ lengthOfBOM = 2;
+ } else if (c1 == 0 && c2 == 0 && c3 == 0xFE && c4 == 0xFF) {
+ setEncoding(UTF32BigEndianEncoding(), AutoDetectedEncoding);
+ lengthOfBOM = 4;
+ }
+
+ if (lengthOfBOM || bufferLength + len >= 4)
+ m_checkedForBOM = true;
+
+ return lengthOfBOM;
+}
+
+bool TextResourceDecoder::checkForCSSCharset(const char* data, size_t len, bool& movedDataToBuffer)
+{
+ if (m_source != DefaultEncoding && m_source != EncodingFromParentFrame) {
+ m_checkedForCSSCharset = true;
+ return true;
+ }
+
+ size_t oldSize = m_buffer.size();
+ m_buffer.grow(oldSize + len);
+ memcpy(m_buffer.data() + oldSize, data, len);
+
+ movedDataToBuffer = true;
+
+ if (m_buffer.size() > 8) { // strlen("@charset") == 8
+ const char* dataStart = m_buffer.data();
+ const char* dataEnd = dataStart + m_buffer.size();
+
+ if (dataStart[0] == '@' && dataStart[1] == 'c' && dataStart[2] == 'h' && dataStart[3] == 'a' && dataStart[4] == 'r' &&
+ dataStart[5] == 's' && dataStart[6] == 'e' && dataStart[7] == 't') {
+
+ dataStart += 8;
+ const char* pos = dataStart;
+ if (!skipWhitespace(pos, dataEnd))
+ return false;
+
+ if (*pos == '"' || *pos == '\'') {
+ char quotationMark = *pos;
+ ++pos;
+ dataStart = pos;
+
+ while (pos < dataEnd && *pos != quotationMark)
+ ++pos;
+ if (pos == dataEnd)
+ return false;
+
+ int encodingNameLength = pos - dataStart;
+
+ ++pos;
+ if (!skipWhitespace(pos, dataEnd))
+ return false;
+
+ if (*pos == ';')
+ setEncoding(findTextEncoding(dataStart, encodingNameLength), EncodingFromCSSCharset);
+ }
+ }
+ m_checkedForCSSCharset = true;
+ return true;
+ }
+ return false;
+}
+
+// Other browsers allow comments in the head section, so we need to also.
+// It's important not to look for tags inside the comments.
+static inline void skipComment(const char*& ptr, const char* pEnd)
+{
+ const char* p = ptr;
+ if (p == pEnd)
+ return;
+ // Allow <!-->; other browsers do.
+ if (*p == '>') {
+ p++;
+ } else {
+ while (p + 2 < pEnd) {
+ if (*p == '-') {
+ // This is the real end of comment, "-->".
+ if (p[1] == '-' && p[2] == '>') {
+ p += 3;
+ break;
+ }
+ // This is the incorrect end of comment that other browsers allow, "--!>".
+ if (p + 3 < pEnd && p[1] == '-' && p[2] == '!' && p[3] == '>') {
+ p += 4;
+ break;
+ }
+ }
+ p++;
+ }
+ }
+ ptr = p;
+}
+
+bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool& movedDataToBuffer)
+{
+ if (m_source != DefaultEncoding && m_source != EncodingFromParentFrame) {
+ m_checkedForHeadCharset = true;
+ return true;
+ }
+
+ // This is not completely efficient, since the function might go
+ // through the HTML head several times.
+
+ size_t oldSize = m_buffer.size();
+ m_buffer.grow(oldSize + len);
+ memcpy(m_buffer.data() + oldSize, data, len);
+
+ movedDataToBuffer = true;
+
+ // Continue with checking for an HTML meta tag if we were already doing so.
+ if (m_charsetParser)
+ return checkForMetaCharset(data, len);
+
+ const char* ptr = m_buffer.data();
+ const char* pEnd = ptr + m_buffer.size();
+
+ // Is there enough data available to check for XML declaration?
+ if (m_buffer.size() < 8)
+ return false;
+
+ // Handle XML declaration, which can have encoding in it. This encoding is honored even for HTML documents.
+ // It is an error for an XML declaration not to be at the start of an XML document, and it is ignored in HTML documents in such case.
+ if (ptr[0] == '<' && ptr[1] == '?' && ptr[2] == 'x' && ptr[3] == 'm' && ptr[4] == 'l') {
+ const char* xmlDeclarationEnd = ptr;
+ while (xmlDeclarationEnd != pEnd && *xmlDeclarationEnd != '>')
+ ++xmlDeclarationEnd;
+ if (xmlDeclarationEnd == pEnd)
+ return false;
+ // No need for +1, because we have an extra "?" to lose at the end of XML declaration.
+ int len = 0;
+ int pos = findXMLEncoding(ptr, xmlDeclarationEnd - ptr, len);
+ if (pos != -1)
+ setEncoding(findTextEncoding(ptr + pos, len), EncodingFromXMLHeader);
+ // continue looking for a charset - it may be specified in an HTTP-Equiv meta
+ } else if (ptr[0] == '<' && ptr[1] == 0 && ptr[2] == '?' && ptr[3] == 0 && ptr[4] == 'x' && ptr[5] == 0) {
+ setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding);
+ return true;
+ } else if (ptr[0] == 0 && ptr[1] == '<' && ptr[2] == 0 && ptr[3] == '?' && ptr[4] == 0 && ptr[5] == 'x') {
+ setEncoding(UTF16BigEndianEncoding(), AutoDetectedEncoding);
+ return true;
+ } else if (ptr[0] == '<' && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0 && ptr[4] == '?' && ptr[5] == 0 && ptr[6] == 0 && ptr[7] == 0) {
+ setEncoding(UTF32LittleEndianEncoding(), AutoDetectedEncoding);
+ return true;
+ } else if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == '<' && ptr[4] == 0 && ptr[5] == 0 && ptr[6] == 0 && ptr[7] == '?') {
+ setEncoding(UTF32BigEndianEncoding(), AutoDetectedEncoding);
+ return true;
+ }
+
+ // The HTTP-EQUIV meta has no effect on XHTML.
+ if (m_contentType == XML)
+ return true;
+
+ m_charsetParser = HTMLMetaCharsetParser::create();
+ return checkForMetaCharset(data, len);
+}
+
+bool TextResourceDecoder::checkForMetaCharset(const char* data, size_t length)
+{
+ if (!m_charsetParser->checkForMetaCharset(data, length))
+ return false;
+
+ setEncoding(m_charsetParser->encoding(), EncodingFromMetaTag);
+ m_charsetParser.clear();
+ m_checkedForHeadCharset = true;
+ return true;
+}
+
+void TextResourceDecoder::detectJapaneseEncoding(const char* data, size_t len)
+{
+ switch (KanjiCode::judge(data, len)) {
+ case KanjiCode::JIS:
+ setEncoding("ISO-2022-JP", AutoDetectedEncoding);
+ break;
+ case KanjiCode::EUC:
+ setEncoding("EUC-JP", AutoDetectedEncoding);
+ break;
+ case KanjiCode::SJIS:
+ setEncoding("Shift_JIS", AutoDetectedEncoding);
+ break;
+ case KanjiCode::ASCII:
+ case KanjiCode::UTF16:
+ case KanjiCode::UTF8:
+ break;
+ }
+}
+
+// We use the encoding detector in two cases:
+// 1. Encoding detector is turned ON and no other encoding source is
+// available (that is, it's DefaultEncoding).
+// 2. Encoding detector is turned ON and the encoding is set to
+// the encoding of the parent frame, which is also auto-detected.
+// Note that condition #2 is NOT satisfied unless parent-child frame
+// relationship is compliant to the same-origin policy. If they're from
+// different domains, |m_source| would not be set to EncodingFromParentFrame
+// in the first place.
+bool TextResourceDecoder::shouldAutoDetect() const
+{
+ // Just checking m_hintEncoding suffices here because it's only set
+ // in setHintEncoding when the source is AutoDetectedEncoding.
+ return m_usesEncodingDetector
+ && (m_source == DefaultEncoding || (m_source == EncodingFromParentFrame && m_hintEncoding));
+}
+
+String TextResourceDecoder::decode(const char* data, size_t len)
+{
+ size_t lengthOfBOM = 0;
+ if (!m_checkedForBOM)
+ lengthOfBOM = checkForBOM(data, len);
+
+ bool movedDataToBuffer = false;
+
+ if (m_contentType == CSS && !m_checkedForCSSCharset)
+ if (!checkForCSSCharset(data, len, movedDataToBuffer))
+ return "";
+
+ if ((m_contentType == HTML || m_contentType == XML) && !m_checkedForHeadCharset) // HTML and XML
+ if (!checkForHeadCharset(data, len, movedDataToBuffer))
+ return "";
+
+ // FIXME: It is wrong to change the encoding downstream after we have already done some decoding.
+ if (shouldAutoDetect()) {
+ if (m_encoding.isJapanese())
+ detectJapaneseEncoding(data, len); // FIXME: We should use detectTextEncoding() for all languages.
+ else {
+ TextEncoding detectedEncoding;
+ if (detectTextEncoding(data, len, m_hintEncoding, &detectedEncoding))
+ setEncoding(detectedEncoding, AutoDetectedEncoding);
+ }
+ }
+
+ ASSERT(m_encoding.isValid());
+
+ if (!m_codec)
+ m_codec = newTextCodec(m_encoding);
+
+ if (m_buffer.isEmpty())
+ return m_codec->decode(data + lengthOfBOM, len - lengthOfBOM, false, m_contentType == XML, m_sawError);
+
+ if (!movedDataToBuffer) {
+ size_t oldSize = m_buffer.size();
+ m_buffer.grow(oldSize + len);
+ memcpy(m_buffer.data() + oldSize, data, len);
+ }
+
+ String result = m_codec->decode(m_buffer.data() + lengthOfBOM, m_buffer.size() - lengthOfBOM, false, m_contentType == XML && !m_useLenientXMLDecoding, m_sawError);
+ m_buffer.clear();
+ return result;
+}
+
+String TextResourceDecoder::flush()
+{
+ // If we can not identify the encoding even after a document is completely
+ // loaded, we need to detect the encoding if other conditions for
+ // autodetection is satisfied.
+ if (m_buffer.size() && shouldAutoDetect()
+ && ((!m_checkedForHeadCharset && (m_contentType == HTML || m_contentType == XML)) || (!m_checkedForCSSCharset && (m_contentType == CSS)))) {
+ TextEncoding detectedEncoding;
+ if (detectTextEncoding(m_buffer.data(), m_buffer.size(),
+ m_hintEncoding, &detectedEncoding))
+ setEncoding(detectedEncoding, AutoDetectedEncoding);
+ }
+
+ if (!m_codec)
+ m_codec = 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();
+ m_codec.clear();
+ m_checkedForBOM = false; // Skip BOM again when re-decoding.
+ return result;
+}
+
+}
diff --git a/Source/WebCore/loader/TextResourceDecoder.h b/Source/WebCore/loader/TextResourceDecoder.h
new file mode 100644
index 0000000..0bb855b
--- /dev/null
+++ b/Source/WebCore/loader/TextResourceDecoder.h
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ 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 TextResourceDecoder_h
+#define TextResourceDecoder_h
+
+#include "TextEncoding.h"
+
+namespace WebCore {
+
+class HTMLMetaCharsetParser;
+
+class TextResourceDecoder : public RefCounted<TextResourceDecoder> {
+public:
+ enum EncodingSource {
+ DefaultEncoding,
+ AutoDetectedEncoding,
+ EncodingFromXMLHeader,
+ EncodingFromMetaTag,
+ EncodingFromCSSCharset,
+ EncodingFromHTTPHeader,
+ UserChosenEncoding,
+ EncodingFromParentFrame
+ };
+
+ static PassRefPtr<TextResourceDecoder> create(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding(), bool usesEncodingDetector = false)
+ {
+ return adoptRef(new TextResourceDecoder(mimeType, defaultEncoding, usesEncodingDetector));
+ }
+ ~TextResourceDecoder();
+
+ void setEncoding(const TextEncoding&, EncodingSource);
+ const TextEncoding& encoding() const { return m_encoding; }
+
+ String decode(const char* data, size_t length);
+ String flush();
+
+ void setHintEncoding(const TextResourceDecoder* hintDecoder)
+ {
+ // hintEncoding is for use with autodetection, which should be
+ // only invoked when hintEncoding comes from auto-detection.
+ if (hintDecoder && hintDecoder->m_source == AutoDetectedEncoding)
+ m_hintEncoding = hintDecoder->encoding().name();
+ }
+
+ void useLenientXMLDecoding() { m_useLenientXMLDecoding = true; }
+ bool sawError() const { return m_sawError; }
+
+private:
+ TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding,
+ bool usesEncodingDetector);
+
+ enum ContentType { PlainText, HTML, XML, CSS }; // PlainText only checks for BOM.
+ static ContentType determineContentType(const String& mimeType);
+ static const TextEncoding& defaultEncoding(ContentType, const TextEncoding& defaultEncoding);
+
+ size_t checkForBOM(const char*, size_t);
+ bool checkForCSSCharset(const char*, size_t, bool& movedDataToBuffer);
+ bool checkForHeadCharset(const char*, size_t, bool& movedDataToBuffer);
+ bool checkForMetaCharset(const char*, size_t);
+ void detectJapaneseEncoding(const char*, size_t);
+ bool shouldAutoDetect() const;
+
+ ContentType m_contentType;
+ TextEncoding m_encoding;
+ OwnPtr<TextCodec> m_codec;
+ EncodingSource m_source;
+ const char* m_hintEncoding;
+ Vector<char> m_buffer;
+ bool m_checkedForBOM;
+ bool m_checkedForCSSCharset;
+ bool m_checkedForHeadCharset;
+ bool m_useLenientXMLDecoding; // Don't stop on XML decoding errors.
+ bool m_sawError;
+ bool m_usesEncodingDetector;
+
+ OwnPtr<HTMLMetaCharsetParser> m_charsetParser;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/ThreadableLoader.cpp b/Source/WebCore/loader/ThreadableLoader.cpp
new file mode 100644
index 0000000..720ba4e
--- /dev/null
+++ b/Source/WebCore/loader/ThreadableLoader.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ThreadableLoader.h"
+
+#include "ScriptExecutionContext.h"
+#include "Document.h"
+#include "DocumentThreadableLoader.h"
+#include "WorkerContext.h"
+#include "WorkerRunLoop.h"
+#include "WorkerThreadableLoader.h"
+
+namespace WebCore {
+
+PassRefPtr<ThreadableLoader> ThreadableLoader::create(ScriptExecutionContext* context, ThreadableLoaderClient* client, const ResourceRequest& request, const ThreadableLoaderOptions& options)
+{
+ ASSERT(client);
+ ASSERT(context);
+
+#if ENABLE(WORKERS)
+ if (context->isWorkerContext())
+ return WorkerThreadableLoader::create(static_cast<WorkerContext*>(context), client, WorkerRunLoop::defaultMode(), request, options);
+#endif // ENABLE(WORKERS)
+
+ ASSERT(context->isDocument());
+ return DocumentThreadableLoader::create(static_cast<Document*>(context), client, request, options);
+}
+
+void ThreadableLoader::loadResourceSynchronously(ScriptExecutionContext* context, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options)
+{
+ ASSERT(context);
+
+#if ENABLE(WORKERS)
+ if (context->isWorkerContext()) {
+ WorkerThreadableLoader::loadResourceSynchronously(static_cast<WorkerContext*>(context), request, client, options);
+ return;
+ }
+#endif // ENABLE(WORKERS)
+
+ ASSERT(context->isDocument());
+ DocumentThreadableLoader::loadResourceSynchronously(static_cast<Document*>(context), request, client, options);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/ThreadableLoader.h b/Source/WebCore/loader/ThreadableLoader.h
new file mode 100644
index 0000000..f41a774
--- /dev/null
+++ b/Source/WebCore/loader/ThreadableLoader.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ThreadableLoader_h
+#define ThreadableLoader_h
+
+#include <wtf/Noncopyable.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+ class ResourceError;
+ class ResourceRequest;
+ class ResourceResponse;
+ class ScriptExecutionContext;
+ class ThreadableLoaderClient;
+
+ enum StoredCredentials {
+ AllowStoredCredentials,
+ DoNotAllowStoredCredentials
+ };
+
+ enum CrossOriginRequestPolicy {
+ DenyCrossOriginRequests,
+ UseAccessControl,
+ AllowCrossOriginRequests
+ };
+
+ struct ThreadableLoaderOptions {
+ ThreadableLoaderOptions() : sendLoadCallbacks(false), sniffContent(false), allowCredentials(false), forcePreflight(false), crossOriginRequestPolicy(DenyCrossOriginRequests) { }
+ bool sendLoadCallbacks;
+ bool sniffContent;
+ bool allowCredentials; // Whether HTTP credentials and cookies are sent with the request.
+ bool forcePreflight; // If AccessControl is used, whether to force a preflight.
+ CrossOriginRequestPolicy crossOriginRequestPolicy;
+ };
+
+ // Useful for doing loader operations from any thread (not threadsafe,
+ // just able to run on threads other than the main thread).
+ class ThreadableLoader : public Noncopyable {
+ public:
+ static void loadResourceSynchronously(ScriptExecutionContext*, const ResourceRequest&, ThreadableLoaderClient&, const ThreadableLoaderOptions&);
+ static PassRefPtr<ThreadableLoader> create(ScriptExecutionContext*, ThreadableLoaderClient*, const ResourceRequest&, const ThreadableLoaderOptions&);
+
+ virtual void cancel() = 0;
+ void ref() { refThreadableLoader(); }
+ void deref() { derefThreadableLoader(); }
+
+ protected:
+ virtual ~ThreadableLoader() { }
+ virtual void refThreadableLoader() = 0;
+ virtual void derefThreadableLoader() = 0;
+ };
+
+} // namespace WebCore
+
+#endif // ThreadableLoader_h
diff --git a/Source/WebCore/loader/ThreadableLoaderClient.h b/Source/WebCore/loader/ThreadableLoaderClient.h
new file mode 100644
index 0000000..bcf68be
--- /dev/null
+++ b/Source/WebCore/loader/ThreadableLoaderClient.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ThreadableLoaderClient_h
+#define ThreadableLoaderClient_h
+
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+ class ResourceError;
+ class ResourceResponse;
+
+ class ThreadableLoaderClient : public Noncopyable {
+ public:
+ virtual void didSendData(unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) { }
+
+ virtual void didReceiveResponse(const ResourceResponse&) { }
+ virtual void didReceiveData(const char*, int /*lengthReceived*/) { }
+ virtual void didFinishLoading(unsigned long /*identifier*/) { }
+ virtual void didFail(const ResourceError&) { }
+ virtual void didFailRedirectCheck() { }
+
+ virtual void didReceiveAuthenticationCancellation(const ResourceResponse&) { }
+
+ protected:
+ virtual ~ThreadableLoaderClient() { }
+ };
+
+} // namespace WebCore
+
+#endif // ThreadableLoaderClient_h
diff --git a/Source/WebCore/loader/ThreadableLoaderClientWrapper.h b/Source/WebCore/loader/ThreadableLoaderClientWrapper.h
new file mode 100644
index 0000000..d3c1a9f
--- /dev/null
+++ b/Source/WebCore/loader/ThreadableLoaderClientWrapper.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ThreadableLoaderClientWrapper_h
+#define ThreadableLoaderClientWrapper_h
+
+#include "ThreadableLoaderClient.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+ class ThreadableLoaderClientWrapper : public ThreadSafeShared<ThreadableLoaderClientWrapper> {
+ public:
+ static PassRefPtr<ThreadableLoaderClientWrapper> create(ThreadableLoaderClient* client)
+ {
+ return adoptRef(new ThreadableLoaderClientWrapper(client));
+ }
+
+ void clearClient()
+ {
+ m_done = true;
+ m_client = 0;
+ }
+
+ bool done() const
+ {
+ return m_done;
+ }
+
+ void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+ {
+ if (m_client)
+ m_client->didSendData(bytesSent, totalBytesToBeSent);
+ }
+
+ void didReceiveResponse(const ResourceResponse& response)
+ {
+ if (m_client)
+ m_client->didReceiveResponse(response);
+ }
+
+ void didReceiveData(const char* data, int lengthReceived)
+ {
+ if (m_client)
+ m_client->didReceiveData(data, lengthReceived);
+ }
+
+ void didFinishLoading(unsigned long identifier)
+ {
+ m_done = true;
+ if (m_client)
+ m_client->didFinishLoading(identifier);
+ }
+
+ void didFail(const ResourceError& error)
+ {
+ m_done = true;
+ if (m_client)
+ m_client->didFail(error);
+ }
+
+ void didFailRedirectCheck()
+ {
+ m_done = true;
+ if (m_client)
+ m_client->didFailRedirectCheck();
+ }
+
+ void didReceiveAuthenticationCancellation(const ResourceResponse& response)
+ {
+ if (m_client)
+ m_client->didReceiveResponse(response);
+ }
+
+ protected:
+ ThreadableLoaderClientWrapper(ThreadableLoaderClient* client)
+ : m_client(client)
+ , m_done(false)
+ {
+ }
+
+ ThreadableLoaderClient* m_client;
+ bool m_done;
+ };
+
+} // namespace WebCore
+
+#endif // ThreadableLoaderClientWrapper_h
diff --git a/Source/WebCore/loader/WorkerThreadableLoader.cpp b/Source/WebCore/loader/WorkerThreadableLoader.cpp
new file mode 100644
index 0000000..4d18c28
--- /dev/null
+++ b/Source/WebCore/loader/WorkerThreadableLoader.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2009, 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"
+
+#if ENABLE(WORKERS)
+
+#include "WorkerThreadableLoader.h"
+
+#include "CrossThreadTask.h"
+#include "ResourceError.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "ThreadableLoader.h"
+#include "WorkerContext.h"
+#include "WorkerLoaderProxy.h"
+#include "WorkerThread.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+using namespace std;
+
+namespace WebCore {
+
+static const char loadResourceSynchronouslyMode[] = "loadResourceSynchronouslyMode";
+
+WorkerThreadableLoader::WorkerThreadableLoader(WorkerContext* workerContext, ThreadableLoaderClient* client, const String& taskMode, const ResourceRequest& request, const ThreadableLoaderOptions& options)
+ : m_workerContext(workerContext)
+ , m_workerClientWrapper(ThreadableLoaderClientWrapper::create(client))
+ , m_bridge(*(new MainThreadBridge(m_workerClientWrapper, m_workerContext->thread()->workerLoaderProxy(), taskMode, request, options)))
+{
+}
+
+WorkerThreadableLoader::~WorkerThreadableLoader()
+{
+ m_bridge.destroy();
+}
+
+void WorkerThreadableLoader::loadResourceSynchronously(WorkerContext* workerContext, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options)
+{
+ WorkerRunLoop& runLoop = workerContext->thread()->runLoop();
+
+ // Create a unique mode just for this synchronous resource load.
+ String mode = loadResourceSynchronouslyMode;
+ mode.append(String::number(runLoop.createUniqueId()));
+
+ RefPtr<WorkerThreadableLoader> loader = WorkerThreadableLoader::create(workerContext, &client, mode, request, options);
+ MessageQueueWaitResult result = MessageQueueMessageReceived;
+ while (!loader->done() && result != MessageQueueTerminated)
+ result = runLoop.runInMode(workerContext, mode);
+
+ if (!loader->done() && result == MessageQueueTerminated)
+ loader->cancel();
+}
+
+void WorkerThreadableLoader::cancel()
+{
+ m_bridge.cancel();
+}
+
+WorkerThreadableLoader::MainThreadBridge::MainThreadBridge(PassRefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, WorkerLoaderProxy& loaderProxy, const String& taskMode,
+ const ResourceRequest& request, const ThreadableLoaderOptions& options)
+ : m_workerClientWrapper(workerClientWrapper)
+ , m_loaderProxy(loaderProxy)
+ , m_taskMode(taskMode.crossThreadString())
+{
+ ASSERT(m_workerClientWrapper.get());
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&MainThreadBridge::mainThreadCreateLoader, this, request, options));
+}
+
+WorkerThreadableLoader::MainThreadBridge::~MainThreadBridge()
+{
+}
+
+void WorkerThreadableLoader::MainThreadBridge::mainThreadCreateLoader(ScriptExecutionContext* context, MainThreadBridge* thisPtr, PassOwnPtr<CrossThreadResourceRequestData> requestData, ThreadableLoaderOptions options)
+{
+ ASSERT(isMainThread());
+ ASSERT(context->isDocument());
+
+ // FIXME: the created loader has no knowledge of the origin of the worker doing the load request.
+ // Basically every setting done in SubresourceLoader::create (including the contents of addExtraFieldsToRequest)
+ // needs to be examined for how it should take into account a different originator.
+ OwnPtr<ResourceRequest> request(ResourceRequest::adopt(requestData));
+ // FIXME: If the a site requests a local resource, then this will return a non-zero value but the sync path
+ // will return a 0 value. Either this should return 0 or the other code path should do a callback with
+ // a failure.
+ thisPtr->m_mainThreadLoader = ThreadableLoader::create(context, thisPtr, *request, options);
+ ASSERT(thisPtr->m_mainThreadLoader);
+}
+
+void WorkerThreadableLoader::MainThreadBridge::mainThreadDestroy(ScriptExecutionContext* context, MainThreadBridge* thisPtr)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+ delete thisPtr;
+}
+
+void WorkerThreadableLoader::MainThreadBridge::destroy()
+{
+ // Ensure that no more client callbacks are done in the worker context's thread.
+ clearClientWrapper();
+
+ // "delete this" and m_mainThreadLoader::deref() on the worker object's thread.
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&MainThreadBridge::mainThreadDestroy, this));
+}
+
+void WorkerThreadableLoader::MainThreadBridge::mainThreadCancel(ScriptExecutionContext* context, MainThreadBridge* thisPtr)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+
+ if (!thisPtr->m_mainThreadLoader)
+ return;
+ thisPtr->m_mainThreadLoader->cancel();
+ thisPtr->m_mainThreadLoader = 0;
+}
+
+void WorkerThreadableLoader::MainThreadBridge::cancel()
+{
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&MainThreadBridge::mainThreadCancel, this));
+ ThreadableLoaderClientWrapper* clientWrapper = m_workerClientWrapper.get();
+ if (!clientWrapper->done()) {
+ // If the client hasn't reached a termination state, then transition it by sending a cancellation error.
+ // Note: no more client callbacks will be done after this method -- the clearClientWrapper() call ensures that.
+ ResourceError error(String(), 0, String(), String());
+ error.setIsCancellation(true);
+ clientWrapper->didFail(error);
+ }
+ clearClientWrapper();
+}
+
+void WorkerThreadableLoader::MainThreadBridge::clearClientWrapper()
+{
+ m_workerClientWrapper->clearClient();
+}
+
+static void workerContextDidSendData(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->didSendData(bytesSent, totalBytesToBeSent);
+}
+
+void WorkerThreadableLoader::MainThreadBridge::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+{
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidSendData, m_workerClientWrapper, bytesSent, totalBytesToBeSent), m_taskMode);
+}
+
+static void workerContextDidReceiveResponse(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, PassOwnPtr<CrossThreadResourceResponseData> responseData)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ OwnPtr<ResourceResponse> response(ResourceResponse::adopt(responseData));
+ workerClientWrapper->didReceiveResponse(*response);
+}
+
+void WorkerThreadableLoader::MainThreadBridge::didReceiveResponse(const ResourceResponse& response)
+{
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveResponse, m_workerClientWrapper, response), m_taskMode);
+}
+
+static void workerContextDidReceiveData(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, PassOwnPtr<Vector<char> > vectorData)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->didReceiveData(vectorData->data(), vectorData->size());
+}
+
+void WorkerThreadableLoader::MainThreadBridge::didReceiveData(const char* data, int lengthReceived)
+{
+ 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.release()), m_taskMode);
+}
+
+static void workerContextDidFinishLoading(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, unsigned long identifier)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->didFinishLoading(identifier);
+}
+
+void WorkerThreadableLoader::MainThreadBridge::didFinishLoading(unsigned long identifier)
+{
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidFinishLoading, m_workerClientWrapper, identifier), m_taskMode);
+}
+
+static void workerContextDidFail(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, const ResourceError& error)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->didFail(error);
+}
+
+void WorkerThreadableLoader::MainThreadBridge::didFail(const ResourceError& error)
+{
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidFail, m_workerClientWrapper, error), m_taskMode);
+}
+
+static void workerContextDidFailRedirectCheck(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->didFailRedirectCheck();
+}
+
+void WorkerThreadableLoader::MainThreadBridge::didFailRedirectCheck()
+{
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidFailRedirectCheck, m_workerClientWrapper), m_taskMode);
+}
+
+static void workerContextDidReceiveAuthenticationCancellation(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, PassOwnPtr<CrossThreadResourceResponseData> responseData)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ OwnPtr<ResourceResponse> response(ResourceResponse::adopt(responseData));
+ workerClientWrapper->didReceiveAuthenticationCancellation(*response);
+}
+
+void WorkerThreadableLoader::MainThreadBridge::didReceiveAuthenticationCancellation(const ResourceResponse& response)
+{
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveAuthenticationCancellation, m_workerClientWrapper, response), m_taskMode);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WORKERS)
diff --git a/Source/WebCore/loader/WorkerThreadableLoader.h b/Source/WebCore/loader/WorkerThreadableLoader.h
new file mode 100644
index 0000000..81da2e0
--- /dev/null
+++ b/Source/WebCore/loader/WorkerThreadableLoader.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WorkerThreadableLoader_h
+#define WorkerThreadableLoader_h
+
+#if ENABLE(WORKERS)
+
+#include "PlatformString.h"
+#include "ThreadableLoader.h"
+#include "ThreadableLoaderClient.h"
+#include "ThreadableLoaderClientWrapper.h"
+
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+ class ResourceError;
+ class ResourceRequest;
+ class WorkerContext;
+ class WorkerLoaderProxy;
+ struct CrossThreadResourceResponseData;
+ struct CrossThreadResourceRequestData;
+
+ class WorkerThreadableLoader : public RefCounted<WorkerThreadableLoader>, public ThreadableLoader {
+ public:
+ static void loadResourceSynchronously(WorkerContext*, const ResourceRequest&, ThreadableLoaderClient&, const ThreadableLoaderOptions&);
+ static PassRefPtr<WorkerThreadableLoader> create(WorkerContext* workerContext, ThreadableLoaderClient* client, const String& taskMode, const ResourceRequest& request, const ThreadableLoaderOptions& options)
+ {
+ return adoptRef(new WorkerThreadableLoader(workerContext, client, taskMode, request, options));
+ }
+
+ ~WorkerThreadableLoader();
+
+ virtual void cancel();
+
+ bool done() const { return m_workerClientWrapper->done(); }
+
+ using RefCounted<WorkerThreadableLoader>::ref;
+ using RefCounted<WorkerThreadableLoader>::deref;
+
+ protected:
+ virtual void refThreadableLoader() { ref(); }
+ virtual void derefThreadableLoader() { deref(); }
+
+ private:
+ // Creates a loader on the main thread and bridges communication between
+ // the main thread and the worker context's thread where WorkerThreadableLoader runs.
+ //
+ // Regarding the bridge and lifetimes of items used in callbacks, there are a few cases:
+ //
+ // all cases. All tasks posted from the worker context's thread are ok because
+ // the last task posted always is "mainThreadDestroy", so MainThreadBridge is
+ // around for all tasks that use it on the main thread.
+ //
+ // case 1. worker.terminate is called.
+ // In this case, no more tasks are posted from the worker object's thread to the worker
+ // context's thread -- WorkerContextProxy implementation enforces this.
+ //
+ // case 2. xhr gets aborted and the worker context continues running.
+ // The ThreadableLoaderClientWrapper has the underlying client cleared, so no more calls
+ // go through it. All tasks posted from the worker object's thread to the worker context's
+ // thread do "ThreadableLoaderClientWrapper::ref" (automatically inside of the cross thread copy
+ // done in createCallbackTask), so the ThreadableLoaderClientWrapper instance is there until all
+ // tasks are executed.
+ class MainThreadBridge : public ThreadableLoaderClient {
+ public:
+ // All executed on the worker context's thread.
+ MainThreadBridge(PassRefPtr<ThreadableLoaderClientWrapper>, WorkerLoaderProxy&, const String& taskMode, const ResourceRequest&, const ThreadableLoaderOptions&);
+ void cancel();
+ void destroy();
+
+ private:
+ // Executed on the worker context's thread.
+ void clearClientWrapper();
+
+ // All executed on the main thread.
+ static void mainThreadDestroy(ScriptExecutionContext*, MainThreadBridge*);
+ ~MainThreadBridge();
+
+ static void mainThreadCreateLoader(ScriptExecutionContext*, MainThreadBridge*, PassOwnPtr<CrossThreadResourceRequestData>, ThreadableLoaderOptions);
+ static void mainThreadCancel(ScriptExecutionContext*, MainThreadBridge*);
+ virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
+ virtual void didReceiveResponse(const ResourceResponse&);
+ virtual void didReceiveData(const char*, int lengthReceived);
+ virtual void didFinishLoading(unsigned long identifier);
+ virtual void didFail(const ResourceError&);
+ virtual void didFailRedirectCheck();
+ virtual void didReceiveAuthenticationCancellation(const ResourceResponse&);
+
+ // Only to be used on the main thread.
+ RefPtr<ThreadableLoader> m_mainThreadLoader;
+
+ // ThreadableLoaderClientWrapper is to be used on the worker context thread.
+ // The ref counting is done on either thread.
+ RefPtr<ThreadableLoaderClientWrapper> m_workerClientWrapper;
+
+ // May be used on either thread.
+ WorkerLoaderProxy& m_loaderProxy;
+
+ // For use on the main thread.
+ String m_taskMode;
+ };
+
+ WorkerThreadableLoader(WorkerContext*, ThreadableLoaderClient*, const String& taskMode, const ResourceRequest&, const ThreadableLoaderOptions&);
+
+ RefPtr<WorkerContext> m_workerContext;
+ RefPtr<ThreadableLoaderClientWrapper> m_workerClientWrapper;
+ MainThreadBridge& m_bridge;
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(WORKERS)
+
+#endif // WorkerThreadableLoader_h
diff --git a/Source/WebCore/loader/appcache/ApplicationCache.cpp b/Source/WebCore/loader/appcache/ApplicationCache.cpp
new file mode 100644
index 0000000..2a93765
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCache.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ApplicationCache.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCacheGroup.h"
+#include "ApplicationCacheResource.h"
+#include "ApplicationCacheStorage.h"
+#include "ResourceRequest.h"
+#include <wtf/text/CString.h>
+#include <stdio.h>
+
+namespace WebCore {
+
+ApplicationCache::ApplicationCache()
+ : m_group(0)
+ , m_manifest(0)
+ , m_estimatedSizeInStorage(0)
+ , m_storageID(0)
+{
+}
+
+ApplicationCache::~ApplicationCache()
+{
+ if (m_group && !m_group->isCopy())
+ m_group->cacheDestroyed(this);
+}
+
+void ApplicationCache::setGroup(ApplicationCacheGroup* group)
+{
+ ASSERT(!m_group || group == m_group);
+ m_group = group;
+}
+
+bool ApplicationCache::isComplete() const
+{
+ return !m_group->cacheIsBeingUpdated(this);
+}
+
+void ApplicationCache::setManifestResource(PassRefPtr<ApplicationCacheResource> manifest)
+{
+ ASSERT(manifest);
+ ASSERT(!m_manifest);
+ ASSERT(manifest->type() & ApplicationCacheResource::Manifest);
+
+ m_manifest = manifest.get();
+
+ addResource(manifest);
+}
+
+void ApplicationCache::addResource(PassRefPtr<ApplicationCacheResource> resource)
+{
+ ASSERT(resource);
+
+ const String& url = resource->url();
+
+ ASSERT(!m_resources.contains(url));
+
+ if (m_storageID) {
+ ASSERT(!resource->storageID());
+ ASSERT(resource->type() & ApplicationCacheResource::Master);
+
+ // Add the resource to the storage.
+ cacheStorage().store(resource.get(), this);
+ }
+
+ m_estimatedSizeInStorage += resource->estimatedSizeInStorage();
+
+ m_resources.set(url, resource);
+}
+
+unsigned ApplicationCache::removeResource(const String& url)
+{
+ HashMap<String, RefPtr<ApplicationCacheResource> >::iterator it = m_resources.find(url);
+ if (it == m_resources.end())
+ return 0;
+
+ // The resource exists, get its type so we can return it.
+ unsigned type = it->second->type();
+
+ m_resources.remove(it);
+
+ m_estimatedSizeInStorage -= it->second->estimatedSizeInStorage();
+
+ return type;
+}
+
+ApplicationCacheResource* ApplicationCache::resourceForURL(const String& url)
+{
+ ASSERT(!KURL(ParsedURLString, url).hasFragmentIdentifier());
+ return m_resources.get(url).get();
+}
+
+bool ApplicationCache::requestIsHTTPOrHTTPSGet(const ResourceRequest& request)
+{
+ if (!request.url().protocolInHTTPFamily())
+ return false;
+
+ if (!equalIgnoringCase(request.httpMethod(), "GET"))
+ return false;
+
+ return true;
+}
+
+ApplicationCacheResource* ApplicationCache::resourceForRequest(const ResourceRequest& request)
+{
+ // We only care about HTTP/HTTPS GET requests.
+ if (!requestIsHTTPOrHTTPSGet(request))
+ return 0;
+
+ KURL url(request.url());
+ if (url.hasFragmentIdentifier())
+ url.removeFragmentIdentifier();
+
+ return resourceForURL(url);
+}
+
+void ApplicationCache::setOnlineWhitelist(const Vector<KURL>& onlineWhitelist)
+{
+ ASSERT(m_onlineWhitelist.isEmpty());
+ m_onlineWhitelist = onlineWhitelist;
+}
+
+bool ApplicationCache::isURLInOnlineWhitelist(const KURL& url)
+{
+ if (m_allowAllNetworkRequests)
+ return true;
+
+ size_t whitelistSize = m_onlineWhitelist.size();
+ for (size_t i = 0; i < whitelistSize; ++i) {
+ if (protocolHostAndPortAreEqual(url, m_onlineWhitelist[i]) && url.string().startsWith(m_onlineWhitelist[i].string()))
+ return true;
+ }
+ return false;
+}
+
+void ApplicationCache::setFallbackURLs(const FallbackURLVector& fallbackURLs)
+{
+ ASSERT(m_fallbackURLs.isEmpty());
+ m_fallbackURLs = fallbackURLs;
+}
+
+bool ApplicationCache::urlMatchesFallbackNamespace(const KURL& url, KURL* fallbackURL)
+{
+ size_t fallbackCount = m_fallbackURLs.size();
+ for (size_t i = 0; i < fallbackCount; ++i) {
+ if (protocolHostAndPortAreEqual(url, m_fallbackURLs[i].first) && url.string().startsWith(m_fallbackURLs[i].first.string())) {
+ if (fallbackURL)
+ *fallbackURL = m_fallbackURLs[i].second;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ApplicationCache::clearStorageID()
+{
+ m_storageID = 0;
+
+ ResourceMap::const_iterator end = m_resources.end();
+ for (ResourceMap::const_iterator it = m_resources.begin(); it != end; ++it)
+ it->second->clearStorageID();
+}
+
+#ifndef NDEBUG
+void ApplicationCache::dump()
+{
+ HashMap<String, RefPtr<ApplicationCacheResource> >::const_iterator end = m_resources.end();
+
+ for (HashMap<String, RefPtr<ApplicationCacheResource> >::const_iterator it = m_resources.begin(); it != end; ++it) {
+ printf("%s ", it->first.ascii().data());
+ ApplicationCacheResource::dumpType(it->second->type());
+ }
+}
+#endif
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/Source/WebCore/loader/appcache/ApplicationCache.h b/Source/WebCore/loader/appcache/ApplicationCache.h
new file mode 100644
index 0000000..f073499
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCache.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ApplicationCache_h
+#define ApplicationCache_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "PlatformString.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+class ApplicationCacheGroup;
+class ApplicationCacheResource;
+class DocumentLoader;
+class KURL;
+class ResourceRequest;
+
+typedef Vector<std::pair<KURL, KURL> > FallbackURLVector;
+
+class ApplicationCache : public RefCounted<ApplicationCache> {
+public:
+ static PassRefPtr<ApplicationCache> create() { return adoptRef(new ApplicationCache); }
+ ~ApplicationCache();
+
+ void addResource(PassRefPtr<ApplicationCacheResource> resource);
+ unsigned removeResource(const String& url);
+
+ void setManifestResource(PassRefPtr<ApplicationCacheResource> manifest);
+ ApplicationCacheResource* manifestResource() const { return m_manifest; }
+
+ void setGroup(ApplicationCacheGroup*);
+ ApplicationCacheGroup* group() const { return m_group; }
+
+ bool isComplete() const;
+
+ ApplicationCacheResource* resourceForRequest(const ResourceRequest&);
+ ApplicationCacheResource* resourceForURL(const String& url);
+
+ void setAllowsAllNetworkRequests(bool value) { m_allowAllNetworkRequests = value; }
+ bool allowsAllNetworkRequests() const { return m_allowAllNetworkRequests; }
+ void setOnlineWhitelist(const Vector<KURL>& onlineWhitelist);
+ const Vector<KURL>& onlineWhitelist() const { return m_onlineWhitelist; }
+ bool isURLInOnlineWhitelist(const KURL&); // There is an entry in online whitelist that has the same origin as the resource's URL and that is a prefix match for the resource's URL.
+
+ void setFallbackURLs(const FallbackURLVector&);
+ const FallbackURLVector& fallbackURLs() const { return m_fallbackURLs; }
+ bool urlMatchesFallbackNamespace(const KURL&, KURL* fallbackURL = 0);
+
+#ifndef NDEBUG
+ void dump();
+#endif
+
+ typedef HashMap<String, RefPtr<ApplicationCacheResource> > ResourceMap;
+ ResourceMap::const_iterator begin() const { return m_resources.begin(); }
+ ResourceMap::const_iterator end() const { return m_resources.end(); }
+
+ void setStorageID(unsigned storageID) { m_storageID = storageID; }
+ unsigned storageID() const { return m_storageID; }
+ void clearStorageID();
+
+ static bool requestIsHTTPOrHTTPSGet(const ResourceRequest&);
+
+ int64_t estimatedSizeInStorage() const { return m_estimatedSizeInStorage; }
+
+private:
+ ApplicationCache();
+
+ ApplicationCacheGroup* m_group;
+ ResourceMap m_resources;
+ ApplicationCacheResource* m_manifest;
+
+ bool m_allowAllNetworkRequests;
+ Vector<KURL> m_onlineWhitelist;
+ FallbackURLVector m_fallbackURLs;
+
+ // The total size of the resources belonging to this Application Cache instance.
+ // This is an estimation of the size this Application Cache occupies in the
+ // database file.
+ int64_t m_estimatedSizeInStorage;
+
+ unsigned m_storageID;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCache_h
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp
new file mode 100644
index 0000000..6454b90
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCacheGroup.cpp
@@ -0,0 +1,1181 @@
+/*
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ApplicationCacheGroup.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCache.h"
+#include "ApplicationCacheHost.h"
+#include "ApplicationCacheResource.h"
+#include "ApplicationCacheStorage.h"
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "DocumentLoader.h"
+#include "DOMApplicationCache.h"
+#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)
+{
+}
+
+ApplicationCacheGroup::~ApplicationCacheGroup()
+{
+ if (m_isCopy) {
+ ASSERT(m_newestCache);
+ ASSERT(m_caches.size() == 1);
+ ASSERT(m_caches.contains(m_newestCache.get()));
+ ASSERT(!m_cacheBeingUpdated);
+ ASSERT(m_associatedDocumentLoaders.isEmpty());
+ ASSERT(m_pendingMasterResourceLoaders.isEmpty());
+ ASSERT(m_newestCache->group() == this);
+
+ return;
+ }
+
+ ASSERT(!m_newestCache);
+ ASSERT(m_caches.isEmpty());
+
+ stopLoading();
+
+ cacheStorage().cacheGroupDestroyed(this);
+}
+
+ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceRequest& request, DocumentLoader*)
+{
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
+ return 0;
+
+ KURL url(request.url());
+ if (url.hasFragmentIdentifier())
+ url.removeFragmentIdentifier();
+
+ if (ApplicationCacheGroup* group = cacheStorage().cacheGroupForURL(url)) {
+ ASSERT(group->newestCache());
+ ASSERT(!group->isObsolete());
+
+ return group->newestCache();
+ }
+
+ return 0;
+}
+
+ApplicationCache* ApplicationCacheGroup::fallbackCacheForMainRequest(const ResourceRequest& request, DocumentLoader*)
+{
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
+ return 0;
+
+ KURL url(request.url());
+ if (url.hasFragmentIdentifier())
+ url.removeFragmentIdentifier();
+
+ if (ApplicationCacheGroup* group = cacheStorage().fallbackCacheGroupForURL(url)) {
+ ASSERT(group->newestCache());
+ ASSERT(!group->isObsolete());
+
+ return group->newestCache();
+ }
+
+ return 0;
+}
+
+void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& passedManifestURL)
+{
+ ASSERT(frame && frame->page());
+
+ if (!frame->settings()->offlineWebApplicationCacheEnabled())
+ return;
+
+ DocumentLoader* documentLoader = frame->loader()->documentLoader();
+ ASSERT(!documentLoader->applicationCacheHost()->applicationCache());
+
+ if (passedManifestURL.isNull()) {
+ selectCacheWithoutManifestURL(frame);
+ return;
+ }
+
+ KURL manifestURL(passedManifestURL);
+ if (manifestURL.hasFragmentIdentifier())
+ manifestURL.removeFragmentIdentifier();
+
+ ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache();
+
+ 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 resourceURL(documentLoader->responseURL());
+ if (resourceURL.hasFragmentIdentifier())
+ resourceURL.removeFragmentIdentifier();
+ ApplicationCacheResource* resource = mainResourceCache->resourceForURL(resourceURL);
+ bool inStorage = resource->storageID();
+ resource->addType(ApplicationCacheResource::Foreign);
+ if (inStorage)
+ cacheStorage().storeUpdatedType(resource, mainResourceCache);
+
+ // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made
+ // as part of the initial load.
+ // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation.
+ frame->navigationScheduler()->scheduleLocationChange(frame->document()->securityOrigin(), documentLoader->url(), frame->loader()->referrer(), true);
+ }
+
+ return;
+ }
+
+ // The resource was loaded from the network, check if it is a HTTP/HTTPS GET.
+ const ResourceRequest& request = frame->loader()->activeDocumentLoader()->request();
+
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
+ return;
+
+ // Check that the resource URL has the same scheme/host/port as the manifest URL.
+ if (!protocolHostAndPortAreEqual(manifestURL, request.url()))
+ return;
+
+ // Don't change anything on disk if private browsing is enabled.
+ if (!frame->settings() || frame->settings()->privateBrowsingEnabled()) {
+ postListenerTask(ApplicationCacheHost::CHECKING_EVENT, documentLoader);
+ postListenerTask(ApplicationCacheHost::ERROR_EVENT, documentLoader);
+ return;
+ }
+
+ ApplicationCacheGroup* group = cacheStorage().findOrCreateCacheGroup(manifestURL);
+
+ documentLoader->applicationCacheHost()->setCandidateApplicationCacheGroup(group);
+ group->m_pendingMasterResourceLoaders.add(documentLoader);
+ group->m_downloadingPendingMasterResourceLoadersCount++;
+
+ ASSERT(!group->m_cacheBeingUpdated || group->m_updateStatus != Idle);
+ group->update(frame, ApplicationCacheUpdateWithBrowsingContext);
+}
+
+void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame)
+{
+ if (!frame->settings()->offlineWebApplicationCacheEnabled())
+ return;
+
+ DocumentLoader* documentLoader = frame->loader()->documentLoader();
+ ASSERT(!documentLoader->applicationCacheHost()->applicationCache());
+
+ ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache();
+
+ if (mainResourceCache) {
+ mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache);
+ mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext);
+ }
+}
+
+void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader)
+{
+ ASSERT(m_pendingMasterResourceLoaders.contains(loader));
+ ASSERT(m_completionType == None || m_pendingEntries.isEmpty());
+ KURL url = loader->url();
+ if (url.hasFragmentIdentifier())
+ url.removeFragmentIdentifier();
+
+ switch (m_completionType) {
+ case None:
+ // The main resource finished loading before the manifest was ready. It will be handled via dispatchMainResources() later.
+ return;
+ case NoUpdate:
+ ASSERT(!m_cacheBeingUpdated);
+ associateDocumentLoaderWithCache(loader, m_newestCache.get());
+
+ if (ApplicationCacheResource* resource = m_newestCache->resourceForURL(url)) {
+ if (!(resource->type() & ApplicationCacheResource::Master)) {
+ resource->addType(ApplicationCacheResource::Master);
+ ASSERT(!resource->storageID());
+ }
+ } else
+ m_newestCache->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, loader->mainResourceData()));
+
+ break;
+ case Failure:
+ // Cache update has been a failure, so there is no reason to keep the document associated with the incomplete cache
+ // (its main resource was not cached yet, so it is likely that the application changed significantly server-side).
+ ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading().
+ loader->applicationCacheHost()->setApplicationCache(0); // Will unset candidate, too.
+ m_associatedDocumentLoaders.remove(loader);
+ postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
+ break;
+ case Completed:
+ ASSERT(m_associatedDocumentLoaders.contains(loader));
+
+ if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) {
+ if (!(resource->type() & ApplicationCacheResource::Master)) {
+ resource->addType(ApplicationCacheResource::Master);
+ ASSERT(!resource->storageID());
+ }
+ } else
+ m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, loader->mainResourceData()));
+ // The "cached" event will be posted to all associated documents once update is complete.
+ break;
+ }
+
+ m_downloadingPendingMasterResourceLoadersCount--;
+ checkIfLoadIsComplete();
+}
+
+void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader)
+{
+ ASSERT(m_pendingMasterResourceLoaders.contains(loader));
+ ASSERT(m_completionType == None || m_pendingEntries.isEmpty());
+
+ switch (m_completionType) {
+ case None:
+ // The main resource finished loading before the manifest was ready. It will be handled via dispatchMainResources() later.
+ return;
+ case NoUpdate:
+ ASSERT(!m_cacheBeingUpdated);
+
+ // The manifest didn't change, and we have a relevant cache - but the main resource download failed mid-way, so it cannot be stored to the cache,
+ // and the loader does not get associated to it. If there are other main resources being downloaded for this cache group, they may still succeed.
+ postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
+
+ break;
+ case Failure:
+ // Cache update failed, too.
+ ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading().
+ ASSERT(!loader->applicationCacheHost()->applicationCache() || loader->applicationCacheHost()->applicationCache() == m_cacheBeingUpdated);
+
+ loader->applicationCacheHost()->setApplicationCache(0); // Will unset candidate, too.
+ m_associatedDocumentLoaders.remove(loader);
+ postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
+ break;
+ case Completed:
+ // The cache manifest didn't list this main resource, and all cache entries were already updated successfully - but the main resource failed to load,
+ // so it cannot be stored to the cache. If there are other main resources being downloaded for this cache group, they may still succeed.
+ ASSERT(m_associatedDocumentLoaders.contains(loader));
+ ASSERT(loader->applicationCacheHost()->applicationCache() == m_cacheBeingUpdated);
+ ASSERT(!loader->applicationCacheHost()->candidateApplicationCacheGroup());
+ m_associatedDocumentLoaders.remove(loader);
+ loader->applicationCacheHost()->setApplicationCache(0);
+
+ postListenerTask(ApplicationCacheHost::ERROR_EVENT, loader);
+
+ break;
+ }
+
+ m_downloadingPendingMasterResourceLoadersCount--;
+ checkIfLoadIsComplete();
+}
+
+void ApplicationCacheGroup::stopLoading()
+{
+ if (m_manifestHandle) {
+ ASSERT(!m_currentHandle);
+
+ m_manifestHandle->setClient(0);
+ m_manifestHandle->cancel();
+ m_manifestHandle = 0;
+ }
+
+ if (m_currentHandle) {
+ ASSERT(!m_manifestHandle);
+ ASSERT(m_cacheBeingUpdated);
+
+ m_currentHandle->setClient(0);
+ m_currentHandle->cancel();
+ m_currentHandle = 0;
+ }
+
+ m_cacheBeingUpdated = 0;
+ m_pendingEntries.clear();
+}
+
+void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader* loader)
+{
+ HashSet<DocumentLoader*>::iterator it = m_associatedDocumentLoaders.find(loader);
+ if (it != m_associatedDocumentLoaders.end())
+ m_associatedDocumentLoaders.remove(it);
+
+ m_pendingMasterResourceLoaders.remove(loader);
+
+ loader->applicationCacheHost()->setApplicationCache(0); // Will set candidate to 0, too.
+
+ if (!m_associatedDocumentLoaders.isEmpty() || !m_pendingMasterResourceLoaders.isEmpty())
+ return;
+
+ if (m_caches.isEmpty()) {
+ // There is an initial cache attempt in progress.
+ ASSERT(!m_newestCache);
+ // Delete ourselves, causing the cache attempt to be stopped.
+ delete this;
+ return;
+ }
+
+ ASSERT(m_caches.contains(m_newestCache.get()));
+
+ // Release our reference to the newest cache. This could cause us to be deleted.
+ // Any ongoing updates will be stopped from destructor.
+ m_newestCache.release();
+}
+
+void ApplicationCacheGroup::cacheDestroyed(ApplicationCache* cache)
+{
+ if (!m_caches.contains(cache))
+ return;
+
+ m_caches.remove(cache);
+
+ if (m_caches.isEmpty()) {
+ ASSERT(m_associatedDocumentLoaders.isEmpty());
+ ASSERT(m_pendingMasterResourceLoaders.isEmpty());
+ delete this;
+ }
+}
+
+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()
+{
+ if (isObsolete())
+ return;
+
+ m_isObsolete = true;
+ cacheStorage().cacheGroupMadeObsolete(this);
+ ASSERT(!m_storageID);
+#if ENABLE(INSPECTOR)
+ inspectorUpdateApplicationCacheStatus(m_frame);
+#endif
+}
+
+void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption)
+{
+ if (m_updateStatus == Checking || m_updateStatus == Downloading) {
+ if (updateOption == ApplicationCacheUpdateWithBrowsingContext) {
+ postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
+ if (m_updateStatus == Downloading)
+ postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, frame->loader()->documentLoader());
+ }
+ return;
+ }
+
+ // Don't change anything on disk if private browsing is enabled.
+ if (!frame->settings() || frame->settings()->privateBrowsingEnabled()) {
+ ASSERT(m_pendingMasterResourceLoaders.isEmpty());
+ ASSERT(m_pendingEntries.isEmpty());
+ ASSERT(!m_cacheBeingUpdated);
+ postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
+ postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, frame->loader()->documentLoader());
+ return;
+ }
+
+ ASSERT(!m_frame);
+ m_frame = frame;
+
+ setUpdateStatus(Checking);
+
+ postListenerTask(ApplicationCacheHost::CHECKING_EVENT, m_associatedDocumentLoaders);
+ if (!m_newestCache) {
+ ASSERT(updateOption == ApplicationCacheUpdateWithBrowsingContext);
+ postListenerTask(ApplicationCacheHost::CHECKING_EVENT, frame->loader()->documentLoader());
+ }
+
+ ASSERT(!m_manifestHandle);
+ ASSERT(!m_manifestResource);
+ ASSERT(m_completionType == None);
+
+ // FIXME: Handle defer loading
+ m_manifestHandle = createResourceHandle(m_manifestURL, m_newestCache ? m_newestCache->manifestResource() : 0);
+}
+
+PassRefPtr<ResourceHandle> ApplicationCacheGroup::createResourceHandle(const KURL& url, ApplicationCacheResource* newestCachedResource)
+{
+ ResourceRequest request(url);
+ m_frame->loader()->applyUserAgent(request);
+ request.setHTTPHeaderField("Cache-Control", "max-age=0");
+
+ if (newestCachedResource) {
+ const String& lastModified = newestCachedResource->response().httpHeaderField("Last-Modified");
+ const String& eTag = newestCachedResource->response().httpHeaderField("ETag");
+ if (!lastModified.isEmpty() || !eTag.isEmpty()) {
+ if (!lastModified.isEmpty())
+ request.setHTTPHeaderField("If-Modified-Since", lastModified);
+ if (!eTag.isEmpty())
+ request.setHTTPHeaderField("If-None-Match", eTag);
+ }
+ }
+
+ 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;
+ }
+
+ ASSERT(handle == m_currentHandle);
+
+ KURL url(handle->firstRequest().url());
+ if (url.hasFragmentIdentifier())
+ url.removeFragmentIdentifier();
+
+ ASSERT(!m_currentResource);
+ ASSERT(m_pendingEntries.contains(url));
+
+ unsigned type = m_pendingEntries.get(url);
+
+ // If this is an initial cache attempt, we should not get master resources delivered here.
+ if (!m_newestCache)
+ ASSERT(!(type & ApplicationCacheResource::Master));
+
+ if (m_newestCache && response.httpStatusCode() == 304) { // Not modified.
+ ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url);
+ if (newestCachedResource) {
+ m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data()));
+ m_pendingEntries.remove(m_currentHandle->firstRequest().url());
+ m_currentHandle->cancel();
+ m_currentHandle = 0;
+ // Load the next resource, if any.
+ startLoadingEntry();
+ return;
+ }
+ // The server could return 304 for an unconditional request - in this case, we handle the response as a normal error.
+ }
+
+ if (response.httpStatusCode() / 100 != 2 || response.url() != m_currentHandle->firstRequest().url()) {
+ if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) {
+ // Note that cacheUpdateFailed() can cause the cache group to be deleted.
+ cacheUpdateFailed();
+ } else if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
+ // Skip this resource. It is dropped from the cache.
+ m_currentHandle->cancel();
+ m_currentHandle = 0;
+ m_pendingEntries.remove(url);
+ // Load the next resource, if any.
+ startLoadingEntry();
+ } else {
+ // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act
+ // as if that was the fetched resource, ignoring the resource obtained from the network.
+ ASSERT(m_newestCache);
+ ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->firstRequest().url());
+ ASSERT(newestCachedResource);
+ m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data()));
+ m_pendingEntries.remove(m_currentHandle->firstRequest().url());
+ m_currentHandle->cancel();
+ m_currentHandle = 0;
+ // Load the next resource, if any.
+ startLoadingEntry();
+ }
+ return;
+ }
+
+ m_currentResource = ApplicationCacheResource::create(url, response, type);
+}
+
+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;
+ }
+
+ ASSERT(handle == m_currentHandle);
+
+ ASSERT(m_currentResource);
+ m_currentResource->data()->append(data, length);
+
+ m_loadedSize += length;
+}
+
+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->firstRequest().url()));
+
+ m_pendingEntries.remove(handle->firstRequest().url());
+
+ ASSERT(m_cacheBeingUpdated);
+
+ m_cacheBeingUpdated->addResource(m_currentResource.release());
+ m_currentHandle = 0;
+
+ // Load the next resource, if any.
+ startLoadingEntry();
+}
+
+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->firstRequest().url());
+ KURL url(handle->firstRequest().url());
+ if (url.hasFragmentIdentifier())
+ url.removeFragmentIdentifier();
+
+ ASSERT(!m_currentResource || !m_pendingEntries.contains(url));
+ m_currentResource = 0;
+ m_pendingEntries.remove(url);
+
+ if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) {
+ // Note that cacheUpdateFailed() can cause the cache group to be deleted.
+ cacheUpdateFailed();
+ } else {
+ // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act
+ // as if that was the fetched resource, ignoring the resource obtained from the network.
+ ASSERT(m_newestCache);
+ ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url);
+ ASSERT(newestCachedResource);
+ m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data()));
+ // Load the next resource, if any.
+ startLoadingEntry();
+ }
+}
+
+void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response)
+{
+ ASSERT(!m_manifestResource);
+ ASSERT(m_manifestHandle);
+
+ if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) {
+ manifestNotFound();
+ return;
+ }
+
+ if (response.httpStatusCode() == 304)
+ return;
+
+ 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->firstRequest().url(), response, ApplicationCacheResource::Manifest);
+}
+
+void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length)
+{
+ if (m_manifestResource)
+ m_manifestResource->data()->append(data, length);
+}
+
+void ApplicationCacheGroup::didFinishLoadingManifest()
+{
+ bool isUpgradeAttempt = m_newestCache;
+
+ if (!isUpgradeAttempt && !m_manifestResource) {
+ // The server returned 304 Not Modified even though we didn't send a conditional request.
+ cacheUpdateFailed();
+ return;
+ }
+
+ m_manifestHandle = 0;
+
+ // Check if the manifest was not modified.
+ if (isUpgradeAttempt) {
+ ApplicationCacheResource* newestManifest = m_newestCache->manifestResource();
+ ASSERT(newestManifest);
+
+ if (!m_manifestResource || // The resource will be null if HTTP response was 304 Not Modified.
+ (newestManifest->data()->size() == m_manifestResource->data()->size() && !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size()))) {
+
+ m_completionType = NoUpdate;
+ m_manifestResource = 0;
+ deliverDelayedMainResources();
+
+ return;
+ }
+ }
+
+ Manifest manifest;
+ if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) {
+ cacheUpdateFailed();
+ return;
+ }
+
+ ASSERT(!m_cacheBeingUpdated);
+ m_cacheBeingUpdated = ApplicationCache::create();
+ m_cacheBeingUpdated->setGroup(this);
+
+ HashSet<DocumentLoader*>::const_iterator masterEnd = m_pendingMasterResourceLoaders.end();
+ for (HashSet<DocumentLoader*>::const_iterator iter = m_pendingMasterResourceLoaders.begin(); iter != masterEnd; ++iter)
+ associateDocumentLoaderWithCache(*iter, m_cacheBeingUpdated.get());
+
+ // We have the manifest, now download the resources.
+ setUpdateStatus(Downloading);
+
+ postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, m_associatedDocumentLoaders);
+
+ ASSERT(m_pendingEntries.isEmpty());
+
+ if (isUpgradeAttempt) {
+ ApplicationCache::ResourceMap::const_iterator end = m_newestCache->end();
+ for (ApplicationCache::ResourceMap::const_iterator it = m_newestCache->begin(); it != end; ++it) {
+ unsigned type = it->second->type();
+ if (type & ApplicationCacheResource::Master)
+ addEntry(it->first, type);
+ }
+ }
+
+ HashSet<String>::const_iterator end = manifest.explicitURLs.end();
+ for (HashSet<String>::const_iterator it = manifest.explicitURLs.begin(); it != end; ++it)
+ addEntry(*it, ApplicationCacheResource::Explicit);
+
+ size_t fallbackCount = manifest.fallbackURLs.size();
+ for (size_t i = 0; i < fallbackCount; ++i)
+ addEntry(manifest.fallbackURLs[i].second, ApplicationCacheResource::Fallback);
+
+ m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs);
+ m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs);
+ m_cacheBeingUpdated->setAllowsAllNetworkRequests(manifest.allowAllNetworkRequests);
+
+ m_progressTotal = m_pendingEntries.size();
+ m_progressDone = 0;
+
+ startLoadingEntry();
+}
+
+void ApplicationCacheGroup::didReachMaxAppCacheSize()
+{
+ ASSERT(m_frame);
+ ASSERT(m_cacheBeingUpdated);
+ m_frame->page()->chrome()->client()->reachedMaxAppCacheSize(cacheStorage().spaceNeeded(m_cacheBeingUpdated->estimatedSizeInStorage()));
+ m_calledReachedMaxAppCacheSize = true;
+ 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();
+ m_manifestResource = 0;
+
+ // Wait for master resource loads to finish.
+ m_completionType = Failure;
+ deliverDelayedMainResources();
+}
+
+void ApplicationCacheGroup::cacheUpdateFailedDueToOriginQuota()
+{
+ if (!m_originQuotaReached) {
+ m_originQuotaReached = true;
+ scheduleReachedOriginQuotaCallback();
+ }
+
+ cacheUpdateFailed();
+}
+
+void ApplicationCacheGroup::manifestNotFound()
+{
+ makeObsolete();
+
+ postListenerTask(ApplicationCacheHost::OBSOLETE_EVENT, m_associatedDocumentLoaders);
+ postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_pendingMasterResourceLoaders);
+
+ stopLoading();
+
+ ASSERT(m_pendingEntries.isEmpty());
+ m_manifestResource = 0;
+
+ while (!m_pendingMasterResourceLoaders.isEmpty()) {
+ HashSet<DocumentLoader*>::iterator it = m_pendingMasterResourceLoaders.begin();
+
+ ASSERT((*it)->applicationCacheHost()->candidateApplicationCacheGroup() == this);
+ ASSERT(!(*it)->applicationCacheHost()->applicationCache());
+ (*it)->applicationCacheHost()->setCandidateApplicationCacheGroup(0);
+ m_pendingMasterResourceLoaders.remove(it);
+ }
+
+ m_downloadingPendingMasterResourceLoadersCount = 0;
+ setUpdateStatus(Idle);
+ m_frame = 0;
+
+ if (m_caches.isEmpty()) {
+ ASSERT(m_associatedDocumentLoaders.isEmpty());
+ ASSERT(!m_cacheBeingUpdated);
+ delete this;
+ }
+}
+
+void ApplicationCacheGroup::checkIfLoadIsComplete()
+{
+ if (m_manifestHandle || !m_pendingEntries.isEmpty() || m_downloadingPendingMasterResourceLoadersCount)
+ return;
+
+ // We're done, all resources have finished downloading (successfully or not).
+
+ bool isUpgradeAttempt = m_newestCache;
+
+ switch (m_completionType) {
+ case None:
+ ASSERT_NOT_REACHED();
+ return;
+ case NoUpdate:
+ ASSERT(isUpgradeAttempt);
+ ASSERT(!m_cacheBeingUpdated);
+
+ // The storage could have been manually emptied by the user.
+ if (!m_storageID)
+ cacheStorage().storeNewestCache(this);
+
+ postListenerTask(ApplicationCacheHost::NOUPDATE_EVENT, m_associatedDocumentLoaders);
+ break;
+ case Failure:
+ ASSERT(!m_cacheBeingUpdated);
+ postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders);
+ if (m_caches.isEmpty()) {
+ ASSERT(m_associatedDocumentLoaders.isEmpty());
+ delete this;
+ return;
+ }
+ break;
+ case Completed: {
+ // FIXME: Fetch the resource from manifest URL again, and check whether it is identical to the one used for update (in case the application was upgraded server-side in the meanwhile). (<rdar://problem/6467625>)
+
+ ASSERT(m_cacheBeingUpdated);
+ if (m_manifestResource)
+ m_cacheBeingUpdated->setManifestResource(m_manifestResource.release());
+ else {
+ // We can get here as a result of retrying the Complete step, following
+ // a failure of the cache storage to save the newest cache due to hitting
+ // the maximum size. In such a case, m_manifestResource may be 0, as
+ // the manifest was already set on the newest cache object.
+ 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, oldNewestCache.get(), failureReason)) {
+ // New cache stored, now remove the old cache.
+ if (oldNewestCache)
+ cacheStorage().remove(oldNewestCache.get());
+
+ // 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 (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
+ // save the new cache.
+
+ // Save a reference to the new cache.
+ m_cacheBeingUpdated = m_newestCache.release();
+ if (oldNewestCache) {
+ // Reinstate the oldNewestCache.
+ setNewestCache(oldNewestCache.release());
+ }
+ 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 {
+ // We must have been deleted by the last call to disassociateDocumentLoader().
+ return;
+ }
+ }
+ break;
+ }
+ }
+
+ // Empty cache group's list of pending master entries.
+ m_pendingMasterResourceLoaders.clear();
+ m_completionType = None;
+ setUpdateStatus(Idle);
+ m_frame = 0;
+ m_loadedSize = 0;
+ m_availableSpaceInQuota = ApplicationCacheStorage::unknownQuota();
+ m_calledReachedMaxAppCacheSize = false;
+}
+
+void ApplicationCacheGroup::startLoadingEntry()
+{
+ ASSERT(m_cacheBeingUpdated);
+
+ if (m_pendingEntries.isEmpty()) {
+ m_completionType = Completed;
+ deliverDelayedMainResources();
+ return;
+ }
+
+ EntryMap::const_iterator it = m_pendingEntries.begin();
+
+ postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders);
+ m_progressDone++;
+
+ ASSERT(!m_currentHandle);
+
+ m_currentHandle = createResourceHandle(KURL(ParsedURLString, it->first), m_newestCache ? m_newestCache->resourceForURL(it->first) : 0);
+}
+
+void ApplicationCacheGroup::deliverDelayedMainResources()
+{
+ // Need to copy loaders, because the cache group may be destroyed at the end of iteration.
+ Vector<DocumentLoader*> loaders;
+ copyToVector(m_pendingMasterResourceLoaders, loaders);
+ size_t count = loaders.size();
+ for (size_t i = 0; i != count; ++i) {
+ DocumentLoader* loader = loaders[i];
+ if (loader->isLoadingMainResource())
+ continue;
+
+ const ResourceError& error = loader->mainDocumentError();
+ if (error.isNull())
+ finishedLoadingMainResource(loader);
+ else
+ failedLoadingMainResource(loader);
+ }
+ if (!count)
+ checkIfLoadIsComplete();
+}
+
+void ApplicationCacheGroup::addEntry(const String& url, unsigned type)
+{
+ ASSERT(m_cacheBeingUpdated);
+ ASSERT(!KURL(ParsedURLString, url).hasFragmentIdentifier());
+
+ // Don't add the URL if we already have an master resource in the cache
+ // (i.e., the main resource finished loading before the manifest).
+ if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) {
+ ASSERT(resource->type() & ApplicationCacheResource::Master);
+ ASSERT(!m_frame->loader()->documentLoader()->isLoadingMainResource());
+
+ resource->addType(type);
+ return;
+ }
+
+ // Don't add the URL if it's the same as the manifest URL.
+ ASSERT(m_manifestResource);
+ if (m_manifestResource->url() == url) {
+ m_manifestResource->addType(type);
+ return;
+ }
+
+ pair<EntryMap::iterator, bool> result = m_pendingEntries.add(url, type);
+
+ if (!result.second)
+ result.first->second |= type;
+}
+
+void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loader, ApplicationCache* cache)
+{
+ // If teardown started already, revive the group.
+ if (!m_newestCache && !m_cacheBeingUpdated)
+ m_newestCache = cache;
+
+ ASSERT(!m_isObsolete);
+
+ loader->applicationCacheHost()->setApplicationCache(cache);
+
+ ASSERT(!m_associatedDocumentLoaders.contains(loader));
+ m_associatedDocumentLoaders.add(loader);
+}
+
+class ChromeClientCallbackTimer: public TimerBase {
+public:
+ ChromeClientCallbackTimer(ApplicationCacheGroup* cacheGroup)
+ : m_cacheGroup(cacheGroup)
+ {
+ }
+
+private:
+ virtual void fired()
+ {
+ m_cacheGroup->didReachMaxAppCacheSize();
+ delete this;
+ }
+ // Note that there is no need to use a RefPtr here. The ApplicationCacheGroup instance is guaranteed
+ // to be alive when the timer fires since invoking the ChromeClient callback is part of its normal
+ // update machinery and nothing can yet cause it to get deleted.
+ ApplicationCacheGroup* m_cacheGroup;
+};
+
+void ApplicationCacheGroup::scheduleReachedMaxAppCacheSizeCallback()
+{
+ ASSERT(isMainThread());
+ ChromeClientCallbackTimer* timer = new ChromeClientCallbackTimer(this);
+ timer->startOneShot(0);
+ // 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, int progressTotal, int progressDone)
+ {
+ return adoptPtr(new CallCacheListenerTask(loader, eventID, progressTotal, progressDone));
+ }
+
+ virtual void performTask(ScriptExecutionContext* context)
+ {
+
+ ASSERT_UNUSED(context, context->isDocument());
+ Frame* frame = m_documentLoader->frame();
+ if (!frame)
+ return;
+
+ ASSERT(frame->loader()->documentLoader() == m_documentLoader.get());
+
+ m_documentLoader->applicationCacheHost()->notifyDOMApplicationCache(m_eventID, m_progressTotal, m_progressDone);
+ }
+
+private:
+ 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, 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, progressTotal, progressDone, *iter);
+}
+
+void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, DocumentLoader* loader)
+{
+ Frame* frame = loader->frame();
+ if (!frame)
+ return;
+
+ ASSERT(frame->loader()->documentLoader() == loader);
+
+ 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()
+{
+ m_storageID = 0;
+
+ HashSet<ApplicationCache*>::const_iterator end = m_caches.end();
+ for (HashSet<ApplicationCache*>::const_iterator it = m_caches.begin(); it != end; ++it)
+ (*it)->clearStorageID();
+}
+
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheGroup.h b/Source/WebCore/loader/appcache/ApplicationCacheGroup.h
new file mode 100644
index 0000000..29d0749
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCacheGroup.h
@@ -0,0 +1,217 @@
+/*
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ApplicationCacheGroup_h
+#define ApplicationCacheGroup_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "DOMApplicationCache.h"
+#include "KURL.h"
+#include "PlatformString.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "SharedBuffer.h"
+
+#include <wtf/Noncopyable.h>
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+
+class ApplicationCache;
+class ApplicationCacheResource;
+class Document;
+class DocumentLoader;
+class Frame;
+class SecurityOrigin;
+
+enum ApplicationCacheUpdateOption {
+ ApplicationCacheUpdateWithBrowsingContext,
+ ApplicationCacheUpdateWithoutBrowsingContext
+};
+
+class ApplicationCacheGroup : public Noncopyable, ResourceHandleClient {
+public:
+ ApplicationCacheGroup(const KURL& manifestURL, bool isCopy = false);
+ ~ApplicationCacheGroup();
+
+ enum UpdateStatus { Idle, Checking, Downloading };
+
+ static ApplicationCache* cacheForMainRequest(const ResourceRequest&, DocumentLoader*);
+ static ApplicationCache* fallbackCacheForMainRequest(const ResourceRequest&, DocumentLoader*);
+
+ static void selectCache(Frame*, const KURL& manifestURL);
+ static void selectCacheWithoutManifestURL(Frame*);
+
+ const KURL& manifestURL() const { return m_manifestURL; }
+ 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 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>);
+
+ void makeObsolete();
+ bool isObsolete() const { return m_isObsolete; }
+
+ void finishedLoadingMainResource(DocumentLoader*);
+ void failedLoadingMainResource(DocumentLoader*);
+
+ void disassociateDocumentLoader(DocumentLoader*);
+
+ bool isCopy() const { return m_isCopy; }
+
+private:
+ 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);
+
+ // For normal resource loading, WebKit client is asked about each resource individually. Since application cache does not belong to any particular document,
+ // 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 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);
+
+ void associateDocumentLoaderWithCache(DocumentLoader*, ApplicationCache*);
+
+ void stopLoading();
+
+ KURL m_manifestURL;
+ RefPtr<SecurityOrigin> m_origin;
+ UpdateStatus m_updateStatus;
+
+ // This is the newest complete cache in the group.
+ RefPtr<ApplicationCache> m_newestCache;
+
+ // All complete caches in this cache group.
+ HashSet<ApplicationCache*> m_caches;
+
+ // The cache being updated (if any). Note that cache updating does not immediately create a new
+ // ApplicationCache object, so this may be null even when update status is not Idle.
+ RefPtr<ApplicationCache> m_cacheBeingUpdated;
+
+ // List of pending master entries, used during the update process to ensure that new master entries are cached.
+ HashSet<DocumentLoader*> m_pendingMasterResourceLoaders;
+ // How many of the above pending master entries have not yet finished downloading.
+ int m_downloadingPendingMasterResourceLoadersCount;
+
+ // These are all the document loaders that are associated with a cache in this group.
+ HashSet<DocumentLoader*> m_associatedDocumentLoaders;
+
+ // The URLs and types of pending cache entries.
+ typedef HashMap<String, unsigned> EntryMap;
+ EntryMap m_pendingEntries;
+
+ // 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.
+ Frame* m_frame;
+
+ // An obsolete cache group is never stored, but the opposite is not true - storing may fail for multiple reasons, such as exceeding disk quota.
+ unsigned m_storageID;
+ bool m_isObsolete;
+
+ // During update, this is used to handle asynchronously arriving results.
+ enum CompletionType {
+ None,
+ NoUpdate,
+ Failure,
+ Completed
+ };
+ CompletionType m_completionType;
+
+ // Whether this cache group is a copy that's only used for transferring the cache to another file.
+ bool m_isCopy;
+
+ // This flag is set immediately after the ChromeClient::reachedMaxAppCacheSize() callback is invoked as a result of the storage layer failing to save a cache
+ // due to reaching the maximum size of the application cache database file. This flag is used by ApplicationCacheGroup::checkIfLoadIsComplete() to decide
+ // the course of action in case of this failure (i.e. call the ChromeClient callback or run the failure steps).
+ bool m_calledReachedMaxAppCacheSize;
+
+ 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
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCacheGroup_h
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheHost.cpp b/Source/WebCore/loader/appcache/ApplicationCacheHost.cpp
new file mode 100644
index 0000000..d5707cf
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCacheHost.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 "ApplicationCacheHost.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCache.h"
+#include "ApplicationCacheGroup.h"
+#include "ApplicationCacheResource.h"
+#include "DocumentLoader.h"
+#include "DOMApplicationCache.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "MainResourceLoader.h"
+#include "ProgressEvent.h"
+#include "ResourceLoader.h"
+#include "ResourceRequest.h"
+#include "Settings.h"
+
+namespace WebCore {
+
+ApplicationCacheHost::ApplicationCacheHost(DocumentLoader* documentLoader)
+ : m_domApplicationCache(0)
+ , m_documentLoader(documentLoader)
+ , m_defersEvents(true)
+ , m_candidateApplicationCacheGroup(0)
+{
+ ASSERT(m_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)
+ m_candidateApplicationCacheGroup->disassociateDocumentLoader(m_documentLoader);
+}
+
+void ApplicationCacheHost::selectCacheWithoutManifest()
+{
+ ApplicationCacheGroup::selectCacheWithoutManifestURL(m_documentLoader->frame());
+}
+
+void ApplicationCacheHost::selectCacheWithManifest(const KURL& manifestURL)
+{
+ ApplicationCacheGroup::selectCache(m_documentLoader->frame(), manifestURL);
+}
+
+void ApplicationCacheHost::maybeLoadMainResource(ResourceRequest& request, SubstituteData& substituteData)
+{
+ // Check if this request should be loaded from the application cache
+ if (!substituteData.isValid() && isApplicationCacheEnabled()) {
+ ASSERT(!m_mainResourceApplicationCache);
+
+ m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, m_documentLoader);
+
+ if (m_mainResourceApplicationCache) {
+ // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource.
+ ApplicationCacheResource* resource = m_mainResourceApplicationCache->resourceForRequest(request);
+ substituteData = SubstituteData(resource->data(),
+ resource->response().mimeType(),
+ resource->response().textEncodingName(), KURL());
+ }
+ }
+}
+
+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) {
+ ASSERT(!m_mainResourceApplicationCache);
+ if (isApplicationCacheEnabled()) {
+ m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, documentLoader());
+
+ if (scheduleLoadFallbackResourceFromApplicationCache(documentLoader()->mainResourceLoader(), m_mainResourceApplicationCache.get()))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ApplicationCacheHost::maybeLoadFallbackForMainError(const ResourceRequest& request, const ResourceError& error)
+{
+ if (!error.isCancellation()) {
+ ASSERT(!m_mainResourceApplicationCache);
+ if (isApplicationCacheEnabled()) {
+ m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, m_documentLoader);
+
+ if (scheduleLoadFallbackResourceFromApplicationCache(documentLoader()->mainResourceLoader(), m_mainResourceApplicationCache.get()))
+ return true;
+ }
+ }
+ return false;
+}
+
+void ApplicationCacheHost::mainResourceDataReceived(const char*, int, long long, bool)
+{
+ // This method is here to facilitate alternate implemetations of this interface by the host browser.
+}
+
+void ApplicationCacheHost::failedLoadingMainResource()
+{
+ ApplicationCacheGroup* group = m_candidateApplicationCacheGroup;
+ if (!group && m_applicationCache) {
+ if (mainResourceApplicationCache()) {
+ // Even when the main resource is being loaded from an application cache, loading can fail if aborted.
+ return;
+ }
+ group = m_applicationCache->group();
+ }
+
+ if (group)
+ group->failedLoadingMainResource(m_documentLoader);
+}
+
+void ApplicationCacheHost::finishedLoadingMainResource()
+{
+ ApplicationCacheGroup* group = candidateApplicationCacheGroup();
+ if (!group && applicationCache() && !mainResourceApplicationCache())
+ group = applicationCache()->group();
+
+ if (group)
+ group->finishedLoadingMainResource(m_documentLoader);
+}
+
+bool ApplicationCacheHost::maybeLoadResource(ResourceLoader* loader, ResourceRequest& request, const KURL& originalURL)
+{
+ if (!isApplicationCacheEnabled())
+ return false;
+
+ if (request.url() != originalURL)
+ return false;
+
+ ApplicationCacheResource* resource;
+ if (!shouldLoadResourceFromApplicationCache(request, resource))
+ return false;
+
+ m_documentLoader->m_pendingSubstituteResources.set(loader, resource);
+ m_documentLoader->deliverSubstituteResourcesAfterDelay();
+
+ return true;
+}
+
+bool ApplicationCacheHost::maybeLoadFallbackForRedirect(ResourceLoader* resourceLoader, ResourceRequest& request, const ResourceResponse& redirectResponse)
+{
+ if (!redirectResponse.isNull() && !protocolHostAndPortAreEqual(request.url(), redirectResponse.url()))
+ if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
+ return true;
+ return false;
+}
+
+bool ApplicationCacheHost::maybeLoadFallbackForResponse(ResourceLoader* resourceLoader, const ResourceResponse& response)
+{
+ if (response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5)
+ if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
+ return true;
+ return false;
+}
+
+bool ApplicationCacheHost::maybeLoadFallbackForError(ResourceLoader* resourceLoader, const ResourceError& error)
+{
+ if (!error.isCancellation())
+ if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
+ return true;
+ return false;
+}
+
+bool ApplicationCacheHost::maybeLoadSynchronously(ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
+{
+ ApplicationCacheResource* resource;
+ if (shouldLoadResourceFromApplicationCache(request, resource)) {
+ if (resource) {
+ response = resource->response();
+ data.append(resource->data()->data(), resource->data()->size());
+ } else {
+ error = documentLoader()->frameLoader()->client()->cannotShowURLError(request);
+ }
+ return true;
+ }
+ return false;
+}
+
+void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
+{
+ // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent,
+ // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry
+ // corresponding to the matched namespace.
+ if ((!error.isNull() && !error.isCancellation())
+ || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5
+ || !protocolHostAndPortAreEqual(request.url(), response.url())) {
+ ApplicationCacheResource* resource;
+ if (getApplicationCacheFallbackResource(request, resource)) {
+ response = resource->response();
+ data.clear();
+ data.append(resource->data()->data(), resource->data()->size());
+ }
+ }
+}
+
+bool ApplicationCacheHost::canCacheInPageCache() const
+{
+ return !applicationCache() && !candidateApplicationCacheGroup();
+}
+
+void ApplicationCacheHost::setDOMApplicationCache(DOMApplicationCache* domApplicationCache)
+{
+ ASSERT(!m_domApplicationCache || !domApplicationCache);
+ m_domApplicationCache = domApplicationCache;
+}
+
+void ApplicationCacheHost::notifyDOMApplicationCache(EventID id, int total, int done)
+{
+ if (m_defersEvents) {
+ // Event dispatching is deferred until document.onload has fired.
+ m_deferredEvents.append(DeferredEvent(id, total, done));
+ return;
+ }
+ 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) {
+ 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);
+ m_candidateApplicationCacheGroup = group;
+}
+
+void ApplicationCacheHost::setApplicationCache(PassRefPtr<ApplicationCache> applicationCache)
+{
+ if (m_candidateApplicationCacheGroup) {
+ ASSERT(!m_applicationCache);
+ m_candidateApplicationCacheGroup = 0;
+ }
+
+ m_applicationCache = applicationCache;
+}
+
+bool ApplicationCacheHost::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource)
+{
+ ApplicationCache* cache = applicationCache();
+ if (!cache || !cache->isComplete())
+ return false;
+
+ // If the resource is not to be fetched using the HTTP GET mechanism or equivalent, or if its URL has a different
+ // <scheme> component than the application cache's manifest, then fetch the resource normally.
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request) || !equalIgnoringCase(request.url().protocol(), cache->manifestResource()->url().protocol()))
+ return false;
+
+ // If the resource's URL is an master entry, the manifest, an explicit entry, or a fallback entry
+ // in the application cache, then get the resource from the cache (instead of fetching it).
+ resource = cache->resourceForURL(request.url());
+
+ // Resources that match fallback namespaces or online whitelist entries are fetched from the network,
+ // unless they are also cached.
+ if (!resource && (cache->urlMatchesFallbackNamespace(request.url()) || cache->isURLInOnlineWhitelist(request.url())))
+ return false;
+
+ // Resources that are not present in the manifest will always fail to load (at least, after the
+ // cache has been primed the first time), making the testing of offline applications simpler.
+ return true;
+}
+
+bool ApplicationCacheHost::getApplicationCacheFallbackResource(const ResourceRequest& request, ApplicationCacheResource*& resource, ApplicationCache* cache)
+{
+ if (!cache) {
+ cache = applicationCache();
+ if (!cache)
+ return false;
+ }
+ if (!cache->isComplete())
+ return false;
+
+ // If the resource is not a HTTP/HTTPS GET, then abort
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
+ return false;
+
+ KURL fallbackURL;
+ if (!cache->urlMatchesFallbackNamespace(request.url(), &fallbackURL))
+ return false;
+
+ resource = cache->resourceForURL(fallbackURL);
+ ASSERT(resource);
+
+ return true;
+}
+
+bool ApplicationCacheHost::scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader* loader, ApplicationCache* cache)
+{
+ if (!isApplicationCacheEnabled())
+ return false;
+
+ ApplicationCacheResource* resource;
+ if (!getApplicationCacheFallbackResource(loader->request(), resource, cache))
+ return false;
+
+ m_documentLoader->m_pendingSubstituteResources.set(loader, resource);
+ m_documentLoader->deliverSubstituteResourcesAfterDelay();
+
+ loader->handle()->cancel();
+
+ return true;
+}
+
+ApplicationCacheHost::Status ApplicationCacheHost::status() const
+{
+ ApplicationCache* cache = applicationCache();
+ if (!cache)
+ return UNCACHED;
+
+ switch (cache->group()->updateStatus()) {
+ case ApplicationCacheGroup::Checking:
+ return CHECKING;
+ case ApplicationCacheGroup::Downloading:
+ return DOWNLOADING;
+ case ApplicationCacheGroup::Idle: {
+ if (cache->group()->isObsolete())
+ return OBSOLETE;
+ if (cache != cache->group()->newestCache())
+ return UPDATEREADY;
+ return IDLE;
+ }
+ }
+
+ ASSERT_NOT_REACHED();
+ return UNCACHED;
+}
+
+bool ApplicationCacheHost::update()
+{
+ ApplicationCache* cache = applicationCache();
+ if (!cache)
+ return false;
+ cache->group()->update(m_documentLoader->frame(), ApplicationCacheUpdateWithoutBrowsingContext);
+ return true;
+}
+
+bool ApplicationCacheHost::swapCache()
+{
+ ApplicationCache* cache = applicationCache();
+ if (!cache)
+ return false;
+
+ // If the group of application caches to which cache belongs has the lifecycle status obsolete, unassociate document from cache.
+ if (cache->group()->isObsolete()) {
+ cache->group()->disassociateDocumentLoader(m_documentLoader);
+ return true;
+ }
+
+ // If there is no newer cache, raise an INVALID_STATE_ERR exception.
+ ApplicationCache* newestCache = cache->group()->newestCache();
+ if (cache == newestCache)
+ return false;
+
+ ASSERT(cache->group() == newestCache->group());
+ setApplicationCache(newestCache);
+
+ return true;
+}
+
+bool ApplicationCacheHost::isApplicationCacheEnabled()
+{
+ return m_documentLoader->frame()->settings()
+ && m_documentLoader->frame()->settings()->offlineWebApplicationCacheEnabled();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheHost.h b/Source/WebCore/loader/appcache/ApplicationCacheHost.h
new file mode 100644
index 0000000..8ac5357
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCacheHost.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ApplicationCacheHost_h
+#define ApplicationCacheHost_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "KURL.h"
+#include <wtf/Deque.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+ class DOMApplicationCache;
+ class DocumentLoader;
+ class Frame;
+ class ResourceLoader;
+ class ResourceError;
+ class ResourceRequest;
+ class ResourceResponse;
+ class SubstituteData;
+#if PLATFORM(CHROMIUM)
+ class ApplicationCacheHostInternal;
+#else
+ class ApplicationCache;
+ class ApplicationCacheGroup;
+ class ApplicationCacheResource;
+ class ApplicationCacheStorage;
+#endif
+
+ class ApplicationCacheHost : public Noncopyable {
+ public:
+ // The Status numeric values are specified in the HTML5 spec.
+ enum Status {
+ UNCACHED = 0,
+ IDLE = 1,
+ CHECKING = 2,
+ DOWNLOADING = 3,
+ UPDATEREADY = 4,
+ OBSOLETE = 5
+ };
+
+ enum EventID {
+ CHECKING_EVENT = 0,
+ ERROR_EVENT,
+ NOUPDATE_EVENT,
+ DOWNLOADING_EVENT,
+ PROGRESS_EVENT,
+ UPDATEREADY_EVENT,
+ CACHED_EVENT,
+ 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();
+
+ void selectCacheWithoutManifest();
+ 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);
+ void finishedLoadingMainResource();
+ void failedLoadingMainResource();
+
+ bool maybeLoadResource(ResourceLoader*, ResourceRequest&, const KURL& originalURL);
+ bool maybeLoadFallbackForRedirect(ResourceLoader*, ResourceRequest&, const ResourceResponse&);
+ bool maybeLoadFallbackForResponse(ResourceLoader*, const ResourceResponse&);
+ bool maybeLoadFallbackForError(ResourceLoader*, const ResourceError&);
+
+ bool maybeLoadSynchronously(ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data);
+ void maybeLoadFallbackSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data);
+
+ bool canCacheInPageCache() const;
+
+ Status status() const;
+ bool update();
+ bool swapCache();
+
+ 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() 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<DeferredEvent> m_deferredEvents;
+
+ void dispatchDOMEvent(EventID, int progressTotal, int progressDone);
+
+#if PLATFORM(CHROMIUM)
+ friend class ApplicationCacheHostInternal;
+ OwnPtr<ApplicationCacheHostInternal> m_internal;
+#else
+ friend class ApplicationCacheGroup;
+ friend class ApplicationCacheStorage;
+
+ bool scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader*, ApplicationCache* = 0);
+ bool shouldLoadResourceFromApplicationCache(const ResourceRequest&, ApplicationCacheResource*&);
+ bool getApplicationCacheFallbackResource(const ResourceRequest&, ApplicationCacheResource*&, ApplicationCache* = 0);
+ void setCandidateApplicationCacheGroup(ApplicationCacheGroup* group);
+ ApplicationCacheGroup* candidateApplicationCacheGroup() const { return m_candidateApplicationCacheGroup; }
+ void setApplicationCache(PassRefPtr<ApplicationCache> applicationCache);
+ ApplicationCache* applicationCache() const { return m_applicationCache.get(); }
+ ApplicationCache* mainResourceApplicationCache() const { return m_mainResourceApplicationCache.get(); }
+
+
+ // The application cache that the document loader is associated with (if any).
+ RefPtr<ApplicationCache> m_applicationCache;
+
+ // Before an application cache has finished loading, this will be the candidate application
+ // group that the document loader is associated with.
+ ApplicationCacheGroup* m_candidateApplicationCacheGroup;
+
+ // This is the application cache the main resource was loaded from (if any).
+ RefPtr<ApplicationCache> m_mainResourceApplicationCache;
+#endif
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+#endif // ApplicationCacheHost_h
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp b/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp
new file mode 100644
index 0000000..03c5c83
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCacheResource.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ApplicationCacheResource.h"
+#include <stdio.h>
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+namespace WebCore {
+
+ApplicationCacheResource::ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> data)
+ : SubstituteResource(url, response, data)
+ , m_type(type)
+ , m_storageID(0)
+ , m_estimatedSizeInStorage(0)
+{
+}
+
+void ApplicationCacheResource::addType(unsigned type)
+{
+ // Caller should take care of storing the new type in database.
+ m_type |= type;
+}
+
+int64_t ApplicationCacheResource::estimatedSizeInStorage()
+{
+ if (m_estimatedSizeInStorage)
+ return m_estimatedSizeInStorage;
+
+ if (data())
+ m_estimatedSizeInStorage = data()->size();
+
+ HTTPHeaderMap::const_iterator end = response().httpHeaderFields().end();
+ for (HTTPHeaderMap::const_iterator it = response().httpHeaderFields().begin(); it != end; ++it)
+ m_estimatedSizeInStorage += (it->first.length() + it->second.length() + 2) * sizeof(UChar);
+
+ m_estimatedSizeInStorage += url().string().length() * sizeof(UChar);
+ m_estimatedSizeInStorage += sizeof(int); // response().m_httpStatusCode
+ m_estimatedSizeInStorage += response().url().string().length() * sizeof(UChar);
+ m_estimatedSizeInStorage += sizeof(unsigned); // dataId
+ m_estimatedSizeInStorage += response().mimeType().length() * sizeof(UChar);
+ m_estimatedSizeInStorage += response().textEncodingName().length() * sizeof(UChar);
+
+ return m_estimatedSizeInStorage;
+}
+
+#ifndef NDEBUG
+void ApplicationCacheResource::dumpType(unsigned type)
+{
+ if (type & Master)
+ printf("master ");
+ if (type & Manifest)
+ printf("manifest ");
+ if (type & Explicit)
+ printf("explicit ");
+ if (type & Foreign)
+ printf("foreign ");
+ if (type & Fallback)
+ printf("fallback ");
+
+ printf("\n");
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheResource.h b/Source/WebCore/loader/appcache/ApplicationCacheResource.h
new file mode 100644
index 0000000..2ca7846
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCacheResource.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ApplicationCacheResource_h
+#define ApplicationCacheResource_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "SubstituteResource.h"
+
+namespace WebCore {
+
+class ApplicationCacheResource : public SubstituteResource {
+public:
+ enum Type {
+ Master = 1 << 0,
+ Manifest = 1 << 1,
+ Explicit = 1 << 2,
+ Foreign = 1 << 3,
+ Fallback = 1 << 4
+ };
+
+ static PassRefPtr<ApplicationCacheResource> create(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer = SharedBuffer::create())
+ {
+ ASSERT(!url.hasFragmentIdentifier());
+ return adoptRef(new ApplicationCacheResource(url, response, type, buffer));
+ }
+
+ unsigned type() const { return m_type; }
+ void addType(unsigned type);
+
+ void setStorageID(unsigned storageID) { m_storageID = storageID; }
+ unsigned storageID() const { return m_storageID; }
+ void clearStorageID() { m_storageID = 0; }
+ int64_t estimatedSizeInStorage();
+
+#ifndef NDEBUG
+ static void dumpType(unsigned type);
+#endif
+
+private:
+ ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer);
+
+ unsigned m_type;
+ unsigned m_storageID;
+ int64_t m_estimatedSizeInStorage;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCacheResource_h
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp
new file mode 100644
index 0000000..7b20775
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp
@@ -0,0 +1,1306 @@
+/*
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ApplicationCacheStorage.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCache.h"
+#include "ApplicationCacheGroup.h"
+#include "ApplicationCacheHost.h"
+#include "ApplicationCacheResource.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>
+
+using namespace std;
+
+namespace WebCore {
+
+template <class T>
+class StorageIDJournal {
+public:
+ ~StorageIDJournal()
+ {
+ size_t size = m_records.size();
+ for (size_t i = 0; i < size; ++i)
+ m_records[i].restore();
+ }
+
+ void add(T* resource, unsigned storageID)
+ {
+ m_records.append(Record(resource, storageID));
+ }
+
+ void commit()
+ {
+ m_records.clear();
+ }
+
+private:
+ class Record {
+ public:
+ Record() : m_resource(0), m_storageID(0) { }
+ Record(T* resource, unsigned storageID) : m_resource(resource), m_storageID(storageID) { }
+
+ void restore()
+ {
+ m_resource->setStorageID(m_storageID);
+ }
+
+ private:
+ T* m_resource;
+ unsigned m_storageID;
+ };
+
+ Vector<Record> m_records;
+};
+
+static unsigned urlHostHash(const KURL& url)
+{
+ unsigned hostStart = url.hostStart();
+ unsigned hostEnd = url.hostEnd();
+
+ return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(url.string().characters() + hostStart, hostEnd - hostStart));
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manifestURL)
+{
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return 0;
+
+ SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL AND manifestURL=?");
+ if (statement.prepare() != SQLResultOk)
+ return 0;
+
+ statement.bindText(1, manifestURL);
+
+ int result = statement.step();
+ if (result == SQLResultDone)
+ return 0;
+
+ if (result != SQLResultRow) {
+ LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
+ return 0;
+ }
+
+ unsigned newestCacheStorageID = static_cast<unsigned>(statement.getColumnInt64(2));
+
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID);
+ if (!cache)
+ return 0;
+
+ ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
+
+ group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
+ group->setNewestCache(cache.release());
+
+ return group;
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const KURL& manifestURL)
+{
+ ASSERT(!manifestURL.hasFragmentIdentifier());
+
+ std::pair<CacheGroupMap::iterator, bool> result = m_cachesInMemory.add(manifestURL, 0);
+
+ if (!result.second) {
+ ASSERT(result.first->second);
+ return result.first->second;
+ }
+
+ // Look up the group in the database
+ ApplicationCacheGroup* group = loadCacheGroup(manifestURL);
+
+ // If the group was not found we need to create it
+ if (!group) {
+ group = new ApplicationCacheGroup(manifestURL);
+ m_cacheHostSet.add(urlHostHash(manifestURL));
+ }
+
+ result.first->second = group;
+
+ return group;
+}
+
+void ApplicationCacheStorage::loadManifestHostHashes()
+{
+ static bool hasLoadedHashes = false;
+
+ if (hasLoadedHashes)
+ return;
+
+ // We set this flag to true before the database has been opened
+ // to avoid trying to open the database over and over if it doesn't exist.
+ hasLoadedHashes = true;
+
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return;
+
+ // Fetch the host hashes.
+ SQLiteStatement statement(m_database, "SELECT manifestHostHash FROM CacheGroups");
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ while (statement.step() == SQLResultRow)
+ m_cacheHostSet.add(static_cast<unsigned>(statement.getColumnInt64(0)));
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url)
+{
+ ASSERT(!url.hasFragmentIdentifier());
+
+ loadManifestHostHashes();
+
+ // Hash the host name and see if there's a manifest with the same host.
+ if (!m_cacheHostSet.contains(urlHostHash(url)))
+ return 0;
+
+ // Check if a cache already exists in memory.
+ CacheGroupMap::const_iterator end = m_cachesInMemory.end();
+ for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) {
+ ApplicationCacheGroup* group = it->second;
+
+ ASSERT(!group->isObsolete());
+
+ if (!protocolHostAndPortAreEqual(url, group->manifestURL()))
+ continue;
+
+ if (ApplicationCache* cache = group->newestCache()) {
+ ApplicationCacheResource* resource = cache->resourceForURL(url);
+ if (!resource)
+ continue;
+ if (resource->type() & ApplicationCacheResource::Foreign)
+ continue;
+ return group;
+ }
+ }
+
+ if (!m_database.isOpen())
+ return 0;
+
+ // Check the database. Look for all cache groups with a newest cache.
+ SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL");
+ if (statement.prepare() != SQLResultOk)
+ return 0;
+
+ int result;
+ while ((result = statement.step()) == SQLResultRow) {
+ KURL manifestURL = KURL(ParsedURLString, statement.getColumnText(1));
+
+ if (m_cachesInMemory.contains(manifestURL))
+ continue;
+
+ if (!protocolHostAndPortAreEqual(url, manifestURL))
+ continue;
+
+ // We found a cache group that matches. Now check if the newest cache has a resource with
+ // a matching URL.
+ unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+ if (!cache)
+ continue;
+
+ ApplicationCacheResource* resource = cache->resourceForURL(url);
+ if (!resource)
+ continue;
+ if (resource->type() & ApplicationCacheResource::Foreign)
+ continue;
+
+ ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
+
+ group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
+ group->setNewestCache(cache.release());
+
+ m_cachesInMemory.set(group->manifestURL(), group);
+
+ return group;
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
+
+ return 0;
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const KURL& url)
+{
+ ASSERT(!url.hasFragmentIdentifier());
+
+ // Check if an appropriate cache already exists in memory.
+ CacheGroupMap::const_iterator end = m_cachesInMemory.end();
+ for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) {
+ ApplicationCacheGroup* group = it->second;
+
+ ASSERT(!group->isObsolete());
+
+ if (ApplicationCache* cache = group->newestCache()) {
+ KURL fallbackURL;
+ if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
+ continue;
+ if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
+ continue;
+ return group;
+ }
+ }
+
+ if (!m_database.isOpen())
+ return 0;
+
+ // Check the database. Look for all cache groups with a newest cache.
+ SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL");
+ if (statement.prepare() != SQLResultOk)
+ return 0;
+
+ int result;
+ while ((result = statement.step()) == SQLResultRow) {
+ KURL manifestURL = KURL(ParsedURLString, statement.getColumnText(1));
+
+ if (m_cachesInMemory.contains(manifestURL))
+ continue;
+
+ // Fallback namespaces always have the same origin as manifest URL, so we can avoid loading caches that cannot match.
+ if (!protocolHostAndPortAreEqual(url, manifestURL))
+ continue;
+
+ // We found a cache group that matches. Now check if the newest cache has a resource with
+ // a matching fallback namespace.
+ unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+
+ KURL fallbackURL;
+ if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
+ continue;
+ if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
+ continue;
+
+ ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
+
+ group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
+ group->setNewestCache(cache.release());
+
+ m_cachesInMemory.set(group->manifestURL(), group);
+
+ return group;
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
+
+ return 0;
+}
+
+void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup* group)
+{
+ if (group->isObsolete()) {
+ ASSERT(!group->storageID());
+ ASSERT(m_cachesInMemory.get(group->manifestURL()) != group);
+ return;
+ }
+
+ ASSERT(m_cachesInMemory.get(group->manifestURL()) == group);
+
+ m_cachesInMemory.remove(group->manifestURL());
+
+ // If the cache group is half-created, we don't want it in the saved set (as it is not stored in database).
+ if (!group->storageID())
+ m_cacheHostSet.remove(urlHostHash(group->manifestURL()));
+}
+
+void ApplicationCacheStorage::cacheGroupMadeObsolete(ApplicationCacheGroup* group)
+{
+ ASSERT(m_cachesInMemory.get(group->manifestURL()) == group);
+ ASSERT(m_cacheHostSet.contains(urlHostHash(group->manifestURL())));
+
+ if (ApplicationCache* newestCache = group->newestCache())
+ remove(newestCache);
+
+ m_cachesInMemory.remove(group->manifestURL());
+ m_cacheHostSet.remove(urlHostHash(group->manifestURL()));
+}
+
+void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory)
+{
+ ASSERT(m_cacheDirectory.isNull());
+ ASSERT(!cacheDirectory.isNull());
+
+ m_cacheDirectory = cacheDirectory;
+}
+
+const String& ApplicationCacheStorage::cacheDirectory() const
+{
+ return m_cacheDirectory;
+}
+
+void ApplicationCacheStorage::setMaximumSize(int64_t size)
+{
+ m_maximumSize = size;
+}
+
+int64_t ApplicationCacheStorage::maximumSize() const
+{
+ return m_maximumSize;
+}
+
+bool ApplicationCacheStorage::isMaximumSizeReached() const
+{
+ return m_isMaximumSizeReached;
+}
+
+int64_t ApplicationCacheStorage::spaceNeeded(int64_t cacheToSave)
+{
+ int64_t spaceNeeded = 0;
+ long long fileSize = 0;
+ if (!getFileSize(m_cacheFile, fileSize))
+ return 0;
+
+ int64_t currentSize = fileSize;
+
+ // Determine the amount of free space we have available.
+ int64_t totalAvailableSize = 0;
+ if (m_maximumSize < currentSize) {
+ // The max size is smaller than the actual size of the app cache file.
+ // This can happen if the client previously imposed a larger max size
+ // value and the app cache file has already grown beyond the current
+ // max size value.
+ // The amount of free space is just the amount of free space inside
+ // the database file. Note that this is always 0 if SQLite is compiled
+ // with AUTO_VACUUM = 1.
+ totalAvailableSize = m_database.freeSpaceSize();
+ } else {
+ // The max size is the same or larger than the current size.
+ // The amount of free space available is the amount of free space
+ // inside the database file plus the amount we can grow until we hit
+ // the max size.
+ totalAvailableSize = (m_maximumSize - currentSize) + m_database.freeSpaceSize();
+ }
+
+ // The space needed to be freed in order to accommodate the failed cache is
+ // the size of the failed cache minus any already available free space.
+ spaceNeeded = cacheToSave - totalAvailableSize;
+ // The space needed value must be positive (or else the total already
+ // available free space would be larger than the size of the failed cache and
+ // saving of the cache should have never failed).
+ ASSERT(spaceNeeded);
+ 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());
+
+ bool result = m_database.executeCommand(sql);
+ if (!result)
+ LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"",
+ sql.utf8().data(), m_database.lastErrorMsg());
+
+ return result;
+}
+
+// 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()
+{
+ int version = SQLiteStatement(m_database, "PRAGMA user_version").getColumnInt(0);
+ if (version == schemaVersion)
+ return;
+
+ m_database.clearAllTables();
+
+ // Update user version.
+ SQLiteTransaction setDatabaseVersion(m_database);
+ setDatabaseVersion.begin();
+
+ char userVersionSQL[32];
+ int unusedNumBytes = snprintf(userVersionSQL, sizeof(userVersionSQL), "PRAGMA user_version=%d", schemaVersion);
+ ASSERT_UNUSED(unusedNumBytes, static_cast<int>(sizeof(userVersionSQL)) >= unusedNumBytes);
+
+ SQLiteStatement statement(m_database, userVersionSQL);
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ executeStatement(statement);
+ setDatabaseVersion.commit();
+}
+
+void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
+{
+ if (m_database.isOpen())
+ return;
+
+ // The cache directory should never be null, but if it for some weird reason is we bail out.
+ if (m_cacheDirectory.isNull())
+ return;
+
+ m_cacheFile = pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db");
+ if (!createIfDoesNotExist && !fileExists(m_cacheFile))
+ return;
+
+ makeAllDirectories(m_cacheDirectory);
+ m_database.open(m_cacheFile);
+
+ if (!m_database.isOpen())
+ return;
+
+ verifySchemaVersion();
+
+ // Create tables
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER, 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)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS FallbackURLs (namespace TEXT NOT NULL ON CONFLICT FAIL, fallbackURL TEXT NOT NULL ON CONFLICT FAIL, "
+ "cache INTEGER NOT NULL ON CONFLICT FAIL)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheEntries (cache INTEGER NOT NULL ON CONFLICT FAIL, type INTEGER, resource INTEGER NOT NULL)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL ON CONFLICT FAIL, "
+ "statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB)");
+ 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"
+ " FOR EACH ROW BEGIN"
+ " DELETE FROM CacheEntries WHERE cache = OLD.id;"
+ " DELETE FROM CacheWhitelistURLs WHERE cache = OLD.id;"
+ " DELETE FROM CacheAllowsAllNetworkRequests WHERE cache = OLD.id;"
+ " DELETE FROM FallbackURLs WHERE cache = OLD.id;"
+ " END");
+
+ // When a cache entry is deleted, its resource should also be deleted.
+ executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheEntryDeleted AFTER DELETE ON CacheEntries"
+ " FOR EACH ROW BEGIN"
+ " DELETE FROM CacheResources WHERE id = OLD.resource;"
+ " END");
+
+ // When a cache resource is deleted, its data blob should also be deleted.
+ executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDeleted AFTER DELETE ON CacheResources"
+ " FOR EACH ROW BEGIN"
+ " DELETE FROM CacheResourceData WHERE id = OLD.data;"
+ " END");
+}
+
+bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement)
+{
+ bool result = statement.executeCommand();
+ if (!result)
+ LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"",
+ statement.query().utf8().data(), m_database.lastErrorMsg());
+
+ return result;
+}
+
+bool ApplicationCacheStorage::store(ApplicationCacheGroup* group, GroupStorageIDJournal* journal)
+{
+ ASSERT(group->storageID() == 0);
+ ASSERT(journal);
+
+ 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;
+
+ unsigned groupStorageID = static_cast<unsigned>(m_database.lastInsertRowID());
+
+ if (!ensureOriginRecord(group->origin()))
+ return false;
+
+ group->setStorageID(groupStorageID);
+ journal->add(group, 0);
+ return true;
+}
+
+bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJournal* storageIDJournal)
+{
+ ASSERT(cache->storageID() == 0);
+ ASSERT(cache->group()->storageID() != 0);
+ ASSERT(storageIDJournal);
+
+ SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup, size) VALUES (?, ?)");
+ if (statement.prepare() != SQLResultOk)
+ return false;
+
+ statement.bindInt64(1, cache->group()->storageID());
+ statement.bindInt64(2, cache->estimatedSizeInStorage());
+
+ if (!executeStatement(statement))
+ return false;
+
+ unsigned cacheStorageID = static_cast<unsigned>(m_database.lastInsertRowID());
+
+ // Store all resources
+ {
+ ApplicationCache::ResourceMap::const_iterator end = cache->end();
+ for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) {
+ unsigned oldStorageID = it->second->storageID();
+ if (!store(it->second.get(), cacheStorageID))
+ return false;
+
+ // Storing the resource succeeded. Log its old storageID in case
+ // it needs to be restored later.
+ storageIDJournal->add(it->second.get(), oldStorageID);
+ }
+ }
+
+ // Store the online whitelist
+ const Vector<KURL>& onlineWhitelist = cache->onlineWhitelist();
+ {
+ size_t whitelistSize = onlineWhitelist.size();
+ for (size_t i = 0; i < whitelistSize; ++i) {
+ SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)");
+ statement.prepare();
+
+ statement.bindText(1, onlineWhitelist[i]);
+ statement.bindInt64(2, cacheStorageID);
+
+ if (!executeStatement(statement))
+ return false;
+ }
+ }
+
+ // Store online whitelist wildcard flag.
+ {
+ SQLiteStatement statement(m_database, "INSERT INTO CacheAllowsAllNetworkRequests (wildcard, cache) VALUES (?, ?)");
+ statement.prepare();
+
+ statement.bindInt64(1, cache->allowsAllNetworkRequests());
+ statement.bindInt64(2, cacheStorageID);
+
+ if (!executeStatement(statement))
+ return false;
+ }
+
+ // Store fallback URLs.
+ const FallbackURLVector& fallbackURLs = cache->fallbackURLs();
+ {
+ size_t fallbackCount = fallbackURLs.size();
+ for (size_t i = 0; i < fallbackCount; ++i) {
+ SQLiteStatement statement(m_database, "INSERT INTO FallbackURLs (namespace, fallbackURL, cache) VALUES (?, ?, ?)");
+ statement.prepare();
+
+ statement.bindText(1, fallbackURLs[i].first);
+ statement.bindText(2, fallbackURLs[i].second);
+ statement.bindInt64(3, cacheStorageID);
+
+ if (!executeStatement(statement))
+ return false;
+ }
+ }
+
+ cache->setStorageID(cacheStorageID);
+ return true;
+}
+
+bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned cacheStorageID)
+{
+ ASSERT(cacheStorageID);
+ 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)
+ return false;
+
+ if (resource->data()->size())
+ dataStatement.bindBlob(1, resource->data()->data(), resource->data()->size());
+
+ if (!dataStatement.executeCommand())
+ return false;
+
+ unsigned dataId = static_cast<unsigned>(m_database.lastInsertRowID());
+
+ // Then, insert the resource
+
+ // Serialize the headers
+ Vector<UChar> stringBuilder;
+
+ HTTPHeaderMap::const_iterator end = resource->response().httpHeaderFields().end();
+ for (HTTPHeaderMap::const_iterator it = resource->response().httpHeaderFields().begin(); it!= end; ++it) {
+ stringBuilder.append(it->first.characters(), it->first.length());
+ stringBuilder.append((UChar)':');
+ stringBuilder.append(it->second.characters(), it->second.length());
+ stringBuilder.append((UChar)'\n');
+ }
+
+ String headers = String::adopt(stringBuilder);
+
+ SQLiteStatement resourceStatement(m_database, "INSERT INTO CacheResources (url, statusCode, responseURL, headers, data, mimeType, textEncodingName) VALUES (?, ?, ?, ?, ?, ?, ?)");
+ if (resourceStatement.prepare() != SQLResultOk)
+ return false;
+
+ // The same ApplicationCacheResource are used in ApplicationCacheResource::size()
+ // to calculate the approximate size of an ApplicationCacheResource object. If
+ // you change the code below, please also change ApplicationCacheResource::size().
+ resourceStatement.bindText(1, resource->url());
+ resourceStatement.bindInt64(2, resource->response().httpStatusCode());
+ resourceStatement.bindText(3, resource->response().url());
+ resourceStatement.bindText(4, headers);
+ resourceStatement.bindInt64(5, dataId);
+ resourceStatement.bindText(6, resource->response().mimeType());
+ resourceStatement.bindText(7, resource->response().textEncodingName());
+
+ if (!executeStatement(resourceStatement))
+ return false;
+
+ unsigned resourceId = static_cast<unsigned>(m_database.lastInsertRowID());
+
+ // Finally, insert the cache entry
+ SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)");
+ if (entryStatement.prepare() != SQLResultOk)
+ return false;
+
+ entryStatement.bindInt64(1, cacheStorageID);
+ entryStatement.bindInt64(2, resource->type());
+ entryStatement.bindInt64(3, resourceId);
+
+ if (!executeStatement(entryStatement))
+ return false;
+
+ resource->setStorageID(resourceId);
+ return true;
+}
+
+bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resource, ApplicationCache* cache)
+{
+ ASSERT_UNUSED(cache, cache->storageID());
+ ASSERT(resource->storageID());
+
+ // First, insert the data
+ SQLiteStatement entryStatement(m_database, "UPDATE CacheEntries SET type=? WHERE resource=?");
+ if (entryStatement.prepare() != SQLResultOk)
+ return false;
+
+ entryStatement.bindInt64(1, resource->type());
+ entryStatement.bindInt64(2, resource->storageID());
+
+ return executeStatement(entryStatement);
+}
+
+bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache)
+{
+ ASSERT(cache->storageID());
+
+ openDatabase(true);
+
+ if (!m_database.isOpen())
+ return false;
+
+ m_isMaximumSizeReached = false;
+ m_database.setMaximumSize(m_maximumSize);
+
+ SQLiteTransaction storeResourceTransaction(m_database);
+ storeResourceTransaction.begin();
+
+ if (!store(resource, cache->storageID())) {
+ checkForMaxSizeReached();
+ return false;
+ }
+
+ // A resource was added to the cache. Update the total data size for the cache.
+ SQLiteStatement sizeUpdateStatement(m_database, "UPDATE Caches SET size=size+? WHERE id=?");
+ if (sizeUpdateStatement.prepare() != SQLResultOk)
+ return false;
+
+ sizeUpdateStatement.bindInt64(1, resource->estimatedSizeInStorage());
+ sizeUpdateStatement.bindInt64(2, cache->storageID());
+
+ if (!executeStatement(sizeUpdateStatement))
+ return false;
+
+ storeResourceTransaction.commit();
+ return true;
+}
+
+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);
+
+ SQLiteTransaction storeCacheTransaction(m_database);
+
+ 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;
+ }
+ }
+
+ ASSERT(group->newestCache());
+ ASSERT(!group->isObsolete());
+ ASSERT(!group->newestCache()->storageID());
+
+ // Log the storageID changes to the in-memory resource objects. The journal
+ // object will roll them back automatically in case a database operation
+ // fails and this method returns early.
+ ResourceStorageIDJournal resourceStorageIDJournal;
+
+ // 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) {
+ failureReason = DiskOrOperationFailure;
+ return false;
+ }
+
+ statement.bindInt64(1, group->newestCache()->storageID());
+ statement.bindInt64(2, group->storageID());
+
+ if (!executeStatement(statement)) {
+ failureReason = DiskOrOperationFailure;
+ return false;
+ }
+
+ groupStorageIDJournal.commit();
+ resourceStorageIDJournal.commit();
+ storeCacheTransaction.commit();
+ 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)
+{
+ size_t pos = find(header, headerLength, ':');
+ ASSERT(pos != notFound);
+
+ AtomicString headerName = AtomicString(header, pos);
+ String headerValue = String(header + pos + 1, headerLength - pos - 1);
+
+ response.setHTTPHeaderField(headerName, headerValue);
+}
+
+static inline void parseHeaders(const String& headers, ResourceResponse& response)
+{
+ unsigned startPos = 0;
+ size_t endPos;
+ while ((endPos = headers.find('\n', startPos)) != notFound) {
+ ASSERT(startPos != endPos);
+
+ parseHeader(headers.characters() + startPos, endPos - startPos, response);
+
+ startPos = endPos + 1;
+ }
+
+ if (startPos != headers.length())
+ parseHeader(headers.characters(), headers.length(), response);
+}
+
+PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID)
+{
+ SQLiteStatement cacheStatement(m_database,
+ "SELECT url, type, mimeType, textEncodingName, headers, CacheResourceData.data FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id "
+ "INNER JOIN CacheResourceData ON CacheResourceData.id=CacheResources.data WHERE CacheEntries.cache=?");
+ if (cacheStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg());
+ return 0;
+ }
+
+ cacheStatement.bindInt64(1, storageID);
+
+ RefPtr<ApplicationCache> cache = ApplicationCache::create();
+
+ int result;
+ while ((result = cacheStatement.step()) == SQLResultRow) {
+ KURL url(ParsedURLString, cacheStatement.getColumnText(0));
+
+ unsigned type = static_cast<unsigned>(cacheStatement.getColumnInt64(1));
+
+ Vector<char> blob;
+ cacheStatement.getColumnBlobAsVector(5, blob);
+
+ RefPtr<SharedBuffer> data = SharedBuffer::adoptVector(blob);
+
+ String mimeType = cacheStatement.getColumnText(2);
+ String textEncodingName = cacheStatement.getColumnText(3);
+
+ ResourceResponse response(url, mimeType, data->size(), textEncodingName, "");
+
+ String headers = cacheStatement.getColumnText(4);
+ parseHeaders(headers, response);
+
+ RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release());
+
+ if (type & ApplicationCacheResource::Manifest)
+ cache->setManifestResource(resource.release());
+ else
+ cache->addResource(resource.release());
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache resources, error \"%s\"", m_database.lastErrorMsg());
+
+ // Load the online whitelist
+ SQLiteStatement whitelistStatement(m_database, "SELECT url FROM CacheWhitelistURLs WHERE cache=?");
+ if (whitelistStatement.prepare() != SQLResultOk)
+ return 0;
+ whitelistStatement.bindInt64(1, storageID);
+
+ Vector<KURL> whitelist;
+ while ((result = whitelistStatement.step()) == SQLResultRow)
+ whitelist.append(KURL(ParsedURLString, whitelistStatement.getColumnText(0)));
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg());
+
+ cache->setOnlineWhitelist(whitelist);
+
+ // Load online whitelist wildcard flag.
+ SQLiteStatement whitelistWildcardStatement(m_database, "SELECT wildcard FROM CacheAllowsAllNetworkRequests WHERE cache=?");
+ if (whitelistWildcardStatement.prepare() != SQLResultOk)
+ return 0;
+ whitelistWildcardStatement.bindInt64(1, storageID);
+
+ result = whitelistWildcardStatement.step();
+ if (result != SQLResultRow)
+ LOG_ERROR("Could not load cache online whitelist wildcard flag, error \"%s\"", m_database.lastErrorMsg());
+
+ cache->setAllowsAllNetworkRequests(whitelistWildcardStatement.getColumnInt64(0));
+
+ if (whitelistWildcardStatement.step() != SQLResultDone)
+ LOG_ERROR("Too many rows for online whitelist wildcard flag");
+
+ // Load fallback URLs.
+ SQLiteStatement fallbackStatement(m_database, "SELECT namespace, fallbackURL FROM FallbackURLs WHERE cache=?");
+ if (fallbackStatement.prepare() != SQLResultOk)
+ return 0;
+ fallbackStatement.bindInt64(1, storageID);
+
+ FallbackURLVector fallbackURLs;
+ while ((result = fallbackStatement.step()) == SQLResultRow)
+ fallbackURLs.append(make_pair(KURL(ParsedURLString, fallbackStatement.getColumnText(0)), KURL(ParsedURLString, fallbackStatement.getColumnText(1))));
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load fallback URLs, error \"%s\"", m_database.lastErrorMsg());
+
+ cache->setFallbackURLs(fallbackURLs);
+
+ cache->setStorageID(storageID);
+
+ return cache.release();
+}
+
+void ApplicationCacheStorage::remove(ApplicationCache* cache)
+{
+ if (!cache->storageID())
+ return;
+
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return;
+
+ ASSERT(cache->group());
+ ASSERT(cache->group()->storageID());
+
+ // All associated data will be deleted by database triggers.
+ SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?");
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ statement.bindInt64(1, cache->storageID());
+ executeStatement(statement);
+
+ cache->clearStorageID();
+
+ if (cache->group()->newestCache() == cache) {
+ // Currently, there are no triggers on the cache group, which is why the cache had to be removed separately above.
+ SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?");
+ if (groupStatement.prepare() != SQLResultOk)
+ return;
+
+ groupStatement.bindInt64(1, cache->group()->storageID());
+ executeStatement(groupStatement);
+
+ cache->group()->clearStorageID();
+ }
+}
+
+void ApplicationCacheStorage::empty()
+{
+ openDatabase(false);
+
+ if (!m_database.isOpen())
+ return;
+
+ // 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
+ // until a cache update process has been initiated.
+ CacheGroupMap::const_iterator end = m_cachesInMemory.end();
+ for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it)
+ it->second->clearStorageID();
+}
+
+bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, ApplicationCacheHost* cacheHost)
+{
+ ApplicationCache* cache = cacheHost->applicationCache();
+ if (!cache)
+ return true;
+
+ // Create a new cache.
+ RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create();
+
+ cacheCopy->setOnlineWhitelist(cache->onlineWhitelist());
+ cacheCopy->setFallbackURLs(cache->fallbackURLs());
+
+ // Traverse the cache and add copies of all resources.
+ ApplicationCache::ResourceMap::const_iterator end = cache->end();
+ for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) {
+ ApplicationCacheResource* resource = it->second.get();
+
+ RefPtr<ApplicationCacheResource> resourceCopy = ApplicationCacheResource::create(resource->url(), resource->response(), resource->type(), resource->data());
+
+ cacheCopy->addResource(resourceCopy.release());
+ }
+
+ // Now create a new cache group.
+ OwnPtr<ApplicationCacheGroup> groupCopy(adoptPtr(new ApplicationCacheGroup(cache->group()->manifestURL(), true)));
+
+ groupCopy->setNewestCache(cacheCopy);
+
+ ApplicationCacheStorage copyStorage;
+ copyStorage.setCacheDirectory(cacheDirectory);
+
+ // Empty the cache in case something was there before.
+ copyStorage.empty();
+
+ return copyStorage.storeNewestCache(groupCopy.get());
+}
+
+bool ApplicationCacheStorage::manifestURLs(Vector<KURL>* urls)
+{
+ ASSERT(urls);
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return false;
+
+ SQLiteStatement selectURLs(m_database, "SELECT manifestURL FROM CacheGroups");
+
+ if (selectURLs.prepare() != SQLResultOk)
+ return false;
+
+ while (selectURLs.step() == SQLResultRow)
+ urls->append(KURL(ParsedURLString, selectURLs.getColumnText(0)));
+
+ return true;
+}
+
+bool ApplicationCacheStorage::cacheGroupSize(const String& manifestURL, int64_t* size)
+{
+ ASSERT(size);
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return false;
+
+ SQLiteStatement statement(m_database, "SELECT sum(Caches.size) FROM Caches INNER JOIN CacheGroups ON Caches.cacheGroup=CacheGroups.id WHERE CacheGroups.manifestURL=?");
+ if (statement.prepare() != SQLResultOk)
+ return false;
+
+ statement.bindText(1, manifestURL);
+
+ int result = statement.step();
+ if (result == SQLResultDone)
+ return false;
+
+ if (result != SQLResultRow) {
+ LOG_ERROR("Could not get the size of the cache group, error \"%s\"", m_database.lastErrorMsg());
+ return false;
+ }
+
+ *size = statement.getColumnInt64(0);
+ return true;
+}
+
+bool ApplicationCacheStorage::deleteCacheGroup(const String& manifestURL)
+{
+ SQLiteTransaction deleteTransaction(m_database);
+ // Check to see if the group is in memory.
+ ApplicationCacheGroup* group = m_cachesInMemory.get(manifestURL);
+ if (group)
+ cacheGroupMadeObsolete(group);
+ else {
+ // The cache group is not in memory, so remove it from the disk.
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return false;
+
+ SQLiteStatement idStatement(m_database, "SELECT id FROM CacheGroups WHERE manifestURL=?");
+ if (idStatement.prepare() != SQLResultOk)
+ return false;
+
+ idStatement.bindText(1, manifestURL);
+
+ int result = idStatement.step();
+ if (result == SQLResultDone)
+ return false;
+
+ if (result != SQLResultRow) {
+ LOG_ERROR("Could not load cache group id, error \"%s\"", m_database.lastErrorMsg());
+ return false;
+ }
+
+ int64_t groupId = idStatement.getColumnInt64(0);
+
+ SQLiteStatement cacheStatement(m_database, "DELETE FROM Caches WHERE cacheGroup=?");
+ if (cacheStatement.prepare() != SQLResultOk)
+ return false;
+
+ SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?");
+ if (groupStatement.prepare() != SQLResultOk)
+ return false;
+
+ cacheStatement.bindInt64(1, groupId);
+ executeStatement(cacheStatement);
+ groupStatement.bindInt64(1, groupId);
+ executeStatement(groupStatement);
+ }
+
+ deleteTransaction.commit();
+ return true;
+}
+
+void ApplicationCacheStorage::vacuumDatabaseFile()
+{
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return;
+
+ m_database.runVacuumCommand();
+}
+
+void ApplicationCacheStorage::checkForMaxSizeReached()
+{
+ if (m_database.lastError() == SQLResultFull)
+ m_isMaximumSizeReached = true;
+}
+
+ApplicationCacheStorage::ApplicationCacheStorage()
+ : m_maximumSize(ApplicationCacheStorage::noQuota())
+ , m_isMaximumSizeReached(false)
+ , m_defaultOriginQuota(ApplicationCacheStorage::noQuota())
+{
+}
+
+ApplicationCacheStorage& cacheStorage()
+{
+ DEFINE_STATIC_LOCAL(ApplicationCacheStorage, storage, ());
+
+ return storage;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/Source/WebCore/loader/appcache/ApplicationCacheStorage.h b/Source/WebCore/loader/appcache/ApplicationCacheStorage.h
new file mode 100644
index 0000000..7db34e6
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ApplicationCacheStorage.h
@@ -0,0 +1,147 @@
+/*
+ * 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ApplicationCacheStorage_h
+#define ApplicationCacheStorage_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "PlatformString.h"
+#include "SQLiteDatabase.h"
+#include <wtf/HashCountedSet.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+class ApplicationCache;
+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;
+
+ void setMaximumSize(int64_t size);
+ int64_t maximumSize() const;
+ 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.
+
+ ApplicationCacheGroup* findOrCreateCacheGroup(const KURL& manifestURL);
+ 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*);
+
+ // Removes the group if the cache to be removed is the newest one (so, storeNewestCache() needs to be called beforehand when updating).
+ void remove(ApplicationCache*);
+
+ void empty();
+
+ static bool storeCopyOfCache(const String& cacheDirectory, ApplicationCacheHost*);
+
+ bool manifestURLs(Vector<KURL>* urls);
+ 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);
+ ApplicationCacheGroup* loadCacheGroup(const KURL& manifestURL);
+
+ typedef StorageIDJournal<ApplicationCacheResource> ResourceStorageIDJournal;
+ typedef StorageIDJournal<ApplicationCacheGroup> GroupStorageIDJournal;
+
+ bool store(ApplicationCacheGroup*, GroupStorageIDJournal*);
+ bool store(ApplicationCache*, ResourceStorageIDJournal*);
+ bool store(ApplicationCacheResource*, unsigned cacheStorageID);
+
+ bool ensureOriginRecord(const SecurityOrigin*);
+
+ void loadManifestHostHashes();
+
+ void verifySchemaVersion();
+
+ void openDatabase(bool createIfDoesNotExist);
+
+ bool executeStatement(SQLiteStatement&);
+ bool executeSQLCommand(const String&);
+
+ void checkForMaxSizeReached();
+
+ String m_cacheDirectory;
+ String m_cacheFile;
+
+ 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,
+ // we keep a hash set of the hosts of the manifest URLs of all non-obsolete cache groups.
+ HashCountedSet<unsigned, AlreadyHashed> m_cacheHostSet;
+
+ typedef HashMap<String, ApplicationCacheGroup*> CacheGroupMap;
+ CacheGroupMap m_cachesInMemory; // Excludes obsolete cache groups.
+
+ friend ApplicationCacheStorage& cacheStorage();
+};
+
+ApplicationCacheStorage& cacheStorage();
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCacheStorage_h
diff --git a/Source/WebCore/loader/appcache/DOMApplicationCache.cpp b/Source/WebCore/loader/appcache/DOMApplicationCache.cpp
new file mode 100644
index 0000000..b9297b1
--- /dev/null
+++ b/Source/WebCore/loader/appcache/DOMApplicationCache.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DOMApplicationCache.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCacheHost.h"
+#include "DocumentLoader.h"
+#include "Event.h"
+#include "EventException.h"
+#include "EventListener.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+
+namespace WebCore {
+
+DOMApplicationCache::DOMApplicationCache(Frame* frame)
+ : m_frame(frame)
+{
+ ApplicationCacheHost* cacheHost = applicationCacheHost();
+ if (cacheHost)
+ cacheHost->setDOMApplicationCache(this);
+}
+
+void DOMApplicationCache::disconnectFrame()
+{
+ ApplicationCacheHost* cacheHost = applicationCacheHost();
+ if (cacheHost)
+ cacheHost->setDOMApplicationCache(0);
+ m_frame = 0;
+}
+
+ApplicationCacheHost* DOMApplicationCache::applicationCacheHost() const
+{
+ if (!m_frame || !m_frame->loader()->documentLoader())
+ return 0;
+ return m_frame->loader()->documentLoader()->applicationCacheHost();
+}
+
+unsigned short DOMApplicationCache::status() const
+{
+ ApplicationCacheHost* cacheHost = applicationCacheHost();
+ if (!cacheHost)
+ return ApplicationCacheHost::UNCACHED;
+ return cacheHost->status();
+}
+
+void DOMApplicationCache::update(ExceptionCode& ec)
+{
+ ApplicationCacheHost* cacheHost = applicationCacheHost();
+ if (!cacheHost || !cacheHost->update())
+ ec = INVALID_STATE_ERR;
+}
+
+void DOMApplicationCache::swapCache(ExceptionCode& ec)
+{
+ ApplicationCacheHost* cacheHost = applicationCacheHost();
+ if (!cacheHost || !cacheHost->swapCache())
+ ec = INVALID_STATE_ERR;
+}
+
+ScriptExecutionContext* DOMApplicationCache::scriptExecutionContext() const
+{
+ if (m_frame)
+ return m_frame->document();
+ return 0;
+}
+
+const AtomicString& DOMApplicationCache::toEventType(ApplicationCacheHost::EventID id)
+{
+ switch (id) {
+ case ApplicationCacheHost::CHECKING_EVENT:
+ return eventNames().checkingEvent;
+ case ApplicationCacheHost::ERROR_EVENT:
+ return eventNames().errorEvent;
+ case ApplicationCacheHost::NOUPDATE_EVENT:
+ return eventNames().noupdateEvent;
+ case ApplicationCacheHost::DOWNLOADING_EVENT:
+ return eventNames().downloadingEvent;
+ case ApplicationCacheHost::PROGRESS_EVENT:
+ return eventNames().progressEvent;
+ case ApplicationCacheHost::UPDATEREADY_EVENT:
+ return eventNames().updatereadyEvent;
+ case ApplicationCacheHost::CACHED_EVENT:
+ return eventNames().cachedEvent;
+ case ApplicationCacheHost::OBSOLETE_EVENT:
+ return eventNames().obsoleteEvent;
+ }
+ ASSERT_NOT_REACHED();
+ return eventNames().errorEvent;
+}
+
+EventTargetData* DOMApplicationCache::eventTargetData()
+{
+ return &m_eventTargetData;
+}
+
+EventTargetData* DOMApplicationCache::ensureEventTargetData()
+{
+ return &m_eventTargetData;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/Source/WebCore/loader/appcache/DOMApplicationCache.h b/Source/WebCore/loader/appcache/DOMApplicationCache.h
new file mode 100644
index 0000000..2a806fa
--- /dev/null
+++ b/Source/WebCore/loader/appcache/DOMApplicationCache.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DOMApplicationCache_h
+#define DOMApplicationCache_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCacheHost.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 Frame;
+class KURL;
+
+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;
+ void update(ExceptionCode&);
+ void swapCache(ExceptionCode&);
+
+ // EventTarget impl
+
+ using RefCounted<DOMApplicationCache>::ref;
+ using RefCounted<DOMApplicationCache>::deref;
+
+ // Explicitly named attribute event listener helpers
+
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(checking);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(noupdate);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(downloading);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(progress);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(updateready);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(cached);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(obsolete);
+
+ virtual ScriptExecutionContext* scriptExecutionContext() const;
+ DOMApplicationCache* toDOMApplicationCache() { return this; }
+
+ static const AtomicString& toEventType(ApplicationCacheHost::EventID id);
+
+private:
+ DOMApplicationCache(Frame*);
+
+ virtual void refEventTarget() { ref(); }
+ virtual void derefEventTarget() { deref(); }
+ virtual EventTargetData* eventTargetData();
+ virtual EventTargetData* ensureEventTargetData();
+
+ ApplicationCacheHost* applicationCacheHost() const;
+
+ Frame* m_frame;
+ EventTargetData m_eventTargetData;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // DOMApplicationCache_h
diff --git a/Source/WebCore/loader/appcache/DOMApplicationCache.idl b/Source/WebCore/loader/appcache/DOMApplicationCache.idl
new file mode 100644
index 0000000..9113ffa
--- /dev/null
+++ b/Source/WebCore/loader/appcache/DOMApplicationCache.idl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module offline {
+
+ interface [
+ Conditional=OFFLINE_WEB_APPLICATIONS,
+ EventTarget,
+ OmitConstructor,
+ DontCheckEnums
+ ] DOMApplicationCache {
+ // update status
+ const unsigned short UNCACHED = 0;
+ const unsigned short IDLE = 1;
+ const unsigned short CHECKING = 2;
+ const unsigned short DOWNLOADING = 3;
+ const unsigned short UPDATEREADY = 4;
+ const unsigned short OBSOLETE = 5;
+ readonly attribute unsigned short status;
+
+ void update()
+ raises(DOMException);
+ void swapCache()
+ raises(DOMException);
+
+ // events
+ attribute EventListener onchecking;
+ attribute EventListener onerror;
+ attribute EventListener onnoupdate;
+ attribute EventListener ondownloading;
+ attribute EventListener onprogress;
+ attribute EventListener onupdateready;
+ attribute EventListener oncached;
+ attribute EventListener onobsolete;
+
+ // EventTarget interface
+ 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/Source/WebCore/loader/appcache/ManifestParser.cpp b/Source/WebCore/loader/appcache/ManifestParser.cpp
new file mode 100644
index 0000000..f58a55d
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ManifestParser.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ManifestParser.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "CharacterNames.h"
+#include "KURL.h"
+#include "TextResourceDecoder.h"
+
+using namespace std;
+
+namespace WebCore {
+
+enum Mode { Explicit, Fallback, OnlineWhitelist, Unknown };
+
+bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest& manifest)
+{
+ ASSERT(manifest.explicitURLs.isEmpty());
+ ASSERT(manifest.onlineWhitelistedURLs.isEmpty());
+ ASSERT(manifest.fallbackURLs.isEmpty());
+ manifest.allowAllNetworkRequests = false;
+
+ Mode mode = Explicit;
+
+ RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/cache-manifest", "UTF-8");
+ String s = decoder->decode(data, length);
+ s += decoder->flush();
+
+ // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?" (the BOM is removed by TextResourceDecoder).
+ // Example: "CACHE MANIFEST #comment" is a valid signature.
+ // Example: "CACHE MANIFEST;V2" is not.
+ if (!s.startsWith("CACHE MANIFEST"))
+ return false;
+
+ const UChar* end = s.characters() + s.length();
+ const UChar* p = s.characters() + 14; // "CACHE MANIFEST" is 14 characters.
+
+ if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r')
+ return false;
+
+ // Skip to the end of the line.
+ while (p < end && *p != '\r' && *p != '\n')
+ p++;
+
+ while (1) {
+ // Skip whitespace
+ while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
+ p++;
+
+ if (p == end)
+ break;
+
+ const UChar* lineStart = p;
+
+ // Find the end of the line
+ while (p < end && *p != '\r' && *p != '\n')
+ p++;
+
+ // Check if we have a comment
+ if (*lineStart == '#')
+ continue;
+
+ // Get rid of trailing whitespace
+ const UChar* tmp = p - 1;
+ while (tmp > lineStart && (*tmp == ' ' || *tmp == '\t'))
+ tmp--;
+
+ String line(lineStart, tmp - lineStart + 1);
+
+ if (line == "CACHE:")
+ mode = Explicit;
+ else if (line == "FALLBACK:")
+ mode = Fallback;
+ else if (line == "NETWORK:")
+ mode = OnlineWhitelist;
+ else if (line.endsWith(":"))
+ mode = Unknown;
+ else if (mode == Unknown)
+ continue;
+ else if (mode == Explicit || mode == OnlineWhitelist) {
+ const UChar* p = line.characters();
+ const UChar* lineEnd = p + line.length();
+
+ // Look for whitespace separating the URL from subsequent ignored tokens.
+ while (p < lineEnd && *p != '\t' && *p != ' ')
+ p++;
+
+ if (mode == OnlineWhitelist && p - line.characters() == 1 && *line.characters() == '*') {
+ // Wildcard was found.
+ manifest.allowAllNetworkRequests = true;
+ continue;
+ }
+
+ KURL url(manifestURL, String(line.characters(), p - line.characters()));
+
+ if (!url.isValid())
+ continue;
+
+ if (url.hasFragmentIdentifier())
+ url.removeFragmentIdentifier();
+
+ 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
+ manifest.onlineWhitelistedURLs.append(url);
+
+ } else if (mode == Fallback) {
+ const UChar* p = line.characters();
+ const UChar* lineEnd = p + line.length();
+
+ // Look for whitespace separating the two URLs
+ while (p < lineEnd && *p != '\t' && *p != ' ')
+ p++;
+
+ if (p == lineEnd) {
+ // There was no whitespace separating the URLs.
+ continue;
+ }
+
+ KURL namespaceURL(manifestURL, String(line.characters(), p - line.characters()));
+ if (!namespaceURL.isValid())
+ continue;
+ if (namespaceURL.hasFragmentIdentifier())
+ namespaceURL.removeFragmentIdentifier();
+
+ if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL))
+ continue;
+
+ // Skip whitespace separating fallback namespace from URL.
+ while (p < lineEnd && (*p == '\t' || *p == ' '))
+ p++;
+
+ // Look for whitespace separating the URL from subsequent ignored tokens.
+ const UChar* fallbackStart = p;
+ while (p < lineEnd && *p != '\t' && *p != ' ')
+ p++;
+
+ KURL fallbackURL(manifestURL, String(fallbackStart, p - fallbackStart));
+ if (!fallbackURL.isValid())
+ continue;
+ if (fallbackURL.hasFragmentIdentifier())
+ fallbackURL.removeFragmentIdentifier();
+
+ if (!protocolHostAndPortAreEqual(manifestURL, fallbackURL))
+ continue;
+
+ manifest.fallbackURLs.append(make_pair(namespaceURL, fallbackURL));
+ } else
+ ASSERT_NOT_REACHED();
+ }
+
+ return true;
+}
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/Source/WebCore/loader/appcache/ManifestParser.h b/Source/WebCore/loader/appcache/ManifestParser.h
new file mode 100644
index 0000000..f0369ee
--- /dev/null
+++ b/Source/WebCore/loader/appcache/ManifestParser.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 ManifestParser_h
+#define ManifestParser_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCache.h"
+
+namespace WebCore {
+
+ class KURL;
+
+ struct Manifest {
+ Vector<KURL> onlineWhitelistedURLs;
+ HashSet<String> explicitURLs;
+ FallbackURLVector fallbackURLs;
+ bool allowAllNetworkRequests; // Wildcard found in NETWORK section.
+ };
+
+ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest&);
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ManifestParser_h
diff --git a/Source/WebCore/loader/archive/Archive.h b/Source/WebCore/loader/archive/Archive.h
new file mode 100644
index 0000000..af3d8b1
--- /dev/null
+++ b/Source/WebCore/loader/archive/Archive.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Archive_h
+#define Archive_h
+
+#include "ArchiveResource.h"
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class Archive : public RefCounted<Archive> {
+public:
+ ArchiveResource* mainResource() { return m_mainResource.get(); }
+ const Vector<RefPtr<ArchiveResource> >& subresources() const { return m_subresources; }
+ const Vector<RefPtr<Archive> >& subframeArchives() const { return m_subframeArchives; }
+
+protected:
+ // These methods are meant for subclasses for different archive types to add resources in to the archive,
+ // and should not be exposed as archives should be immutable to clients
+ void setMainResource(PassRefPtr<ArchiveResource> mainResource) { m_mainResource = mainResource; }
+ void addSubresource(PassRefPtr<ArchiveResource> subResource) { m_subresources.append(subResource); }
+ void addSubframeArchive(PassRefPtr<Archive> subframeArchive) { m_subframeArchives.append(subframeArchive); }
+
+private:
+ RefPtr<ArchiveResource> m_mainResource;
+ Vector<RefPtr<ArchiveResource> > m_subresources;
+ Vector<RefPtr<Archive> > m_subframeArchives;
+};
+
+}
+
+#endif // Archive
diff --git a/Source/WebCore/loader/archive/ArchiveFactory.cpp b/Source/WebCore/loader/archive/ArchiveFactory.cpp
new file mode 100644
index 0000000..5926690
--- /dev/null
+++ b/Source/WebCore/loader/archive/ArchiveFactory.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ArchiveFactory.h"
+
+#include "MIMETypeRegistry.h"
+#include "PlatformString.h"
+
+#if PLATFORM(CF) && !PLATFORM(QT)
+#include "LegacyWebArchive.h"
+#elif PLATFORM(ANDROID)
+#include "WebArchiveAndroid.h"
+#endif
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+typedef PassRefPtr<Archive> RawDataCreationFunction(SharedBuffer*);
+typedef HashMap<String, RawDataCreationFunction*, CaseFoldingHash> ArchiveMIMETypesMap;
+
+// The create functions in the archive classes return PassRefPtr to concrete subclasses
+// of Archive. This adaptor makes the functions have a uniform return type.
+template <typename ArchiveClass> static PassRefPtr<Archive> archiveFactoryCreate(SharedBuffer* buffer)
+{
+ return ArchiveClass::create(buffer);
+}
+
+static ArchiveMIMETypesMap& archiveMIMETypes()
+{
+ DEFINE_STATIC_LOCAL(ArchiveMIMETypesMap, mimeTypes, ());
+ static bool initialized = false;
+
+ if (initialized)
+ return mimeTypes;
+
+#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;
+ return mimeTypes;
+}
+
+bool ArchiveFactory::isArchiveMimeType(const String& mimeType)
+{
+ return !mimeType.isEmpty() && archiveMIMETypes().contains(mimeType);
+}
+
+PassRefPtr<Archive> ArchiveFactory::create(SharedBuffer* data, const String& mimeType)
+{
+ RawDataCreationFunction* function = mimeType.isEmpty() ? 0 : archiveMIMETypes().get(mimeType);
+ return function ? function(data) : 0;
+}
+
+void ArchiveFactory::registerKnownArchiveMIMETypes()
+{
+ HashSet<String>& mimeTypes = MIMETypeRegistry::getSupportedNonImageMIMETypes();
+ ArchiveMIMETypesMap::iterator i = archiveMIMETypes().begin();
+ ArchiveMIMETypesMap::iterator end = archiveMIMETypes().end();
+
+ for (; i != end; ++i)
+ mimeTypes.add(i->first);
+}
+
+}
diff --git a/Source/WebCore/loader/archive/ArchiveFactory.h b/Source/WebCore/loader/archive/ArchiveFactory.h
new file mode 100644
index 0000000..c3b9464
--- /dev/null
+++ b/Source/WebCore/loader/archive/ArchiveFactory.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ArchiveFactory_h
+#define ArchiveFactory_h
+
+#include "Archive.h"
+
+#include <wtf/Forward.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class SharedBuffer;
+
+class ArchiveFactory {
+public:
+ static bool isArchiveMimeType(const String&);
+ static PassRefPtr<Archive> create(SharedBuffer* data, const String& mimeType);
+ static void registerKnownArchiveMIMETypes();
+};
+
+}
+
+#endif // ArchiveFactory_h
diff --git a/Source/WebCore/loader/archive/ArchiveResource.cpp b/Source/WebCore/loader/archive/ArchiveResource.cpp
new file mode 100644
index 0000000..7dedc93
--- /dev/null
+++ b/Source/WebCore/loader/archive/ArchiveResource.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ArchiveResource.h"
+
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+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(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/Source/WebCore/loader/archive/ArchiveResource.h b/Source/WebCore/loader/archive/ArchiveResource.h
new file mode 100644
index 0000000..97d6e32
--- /dev/null
+++ b/Source/WebCore/loader/archive/ArchiveResource.h
@@ -0,0 +1,62 @@
+/*
+ * 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
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ArchiveResource_h
+#define ArchiveResource_h
+
+#include "SubstituteResource.h"
+
+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,
+ 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 String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&);
+
+ String m_mimeType;
+ String m_textEncoding;
+ String m_frameName;
+
+ bool m_shouldIgnoreWhenUnarchiving;
+};
+
+}
+
+#endif // ArchiveResource_h
diff --git a/Source/WebCore/loader/archive/ArchiveResourceCollection.cpp b/Source/WebCore/loader/archive/ArchiveResourceCollection.cpp
new file mode 100644
index 0000000..6eb1237
--- /dev/null
+++ b/Source/WebCore/loader/archive/ArchiveResourceCollection.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ArchiveResourceCollection.h"
+
+namespace WebCore {
+
+ArchiveResourceCollection::ArchiveResourceCollection()
+{
+}
+
+void ArchiveResourceCollection::addAllResources(Archive* archive)
+{
+ ASSERT(archive);
+ if (!archive)
+ return;
+
+ const Vector<RefPtr<ArchiveResource> >& subresources = archive->subresources();
+ Vector<RefPtr<ArchiveResource> >::const_iterator iRes = subresources.begin();
+ Vector<RefPtr<ArchiveResource> >::const_iterator endRes = subresources.end();
+
+ for (; iRes != endRes; ++iRes)
+ m_subresources.set((*iRes)->url(), iRes->get());
+
+ const Vector<RefPtr<Archive> >& subframes = archive->subframeArchives();
+ Vector<RefPtr<Archive> >::const_iterator iFrame = subframes.begin();
+ Vector<RefPtr<Archive> >::const_iterator endFrame = subframes.end();
+
+ for (; iFrame != endFrame; ++iFrame) {
+ ASSERT((*iFrame)->mainResource());
+ const String& frameName = (*iFrame)->mainResource()->frameName();
+ if (!frameName.isNull())
+ m_subframes.set(frameName, iFrame->get());
+ }
+}
+
+// FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on.
+// Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps?
+void ArchiveResourceCollection::addResource(PassRefPtr<ArchiveResource> resource)
+{
+ ASSERT(resource);
+ if (!resource)
+ return;
+
+ const KURL& url = resource->url(); // get before passing PassRefPtr (which sets it to 0)
+ m_subresources.set(url, resource);
+}
+
+ArchiveResource* ArchiveResourceCollection::archiveResourceForURL(const KURL& url)
+{
+ ArchiveResource* resource = m_subresources.get(url).get();
+ if (!resource)
+ return 0;
+
+ return resource;
+}
+
+PassRefPtr<Archive> ArchiveResourceCollection::popSubframeArchive(const String& frameName)
+{
+ return m_subframes.take(frameName);
+}
+
+}
diff --git a/Source/WebCore/loader/archive/ArchiveResourceCollection.h b/Source/WebCore/loader/archive/ArchiveResourceCollection.h
new file mode 100644
index 0000000..9d630d1
--- /dev/null
+++ b/Source/WebCore/loader/archive/ArchiveResourceCollection.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ArchiveResourceCollection_h
+#define ArchiveResourceCollection_h
+
+#include "Archive.h"
+#include "ArchiveResource.h"
+#include "KURL.h"
+#include "PlatformString.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class ArchiveResourceCollection : public Noncopyable {
+public:
+ ArchiveResourceCollection();
+
+ void addResource(PassRefPtr<ArchiveResource>);
+ void addAllResources(Archive*);
+
+ ArchiveResource* archiveResourceForURL(const KURL&);
+ PassRefPtr<Archive> popSubframeArchive(const String& frameName);
+
+private:
+ HashMap<String, RefPtr<ArchiveResource> > m_subresources;
+ HashMap<String, RefPtr<Archive> > m_subframes;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/archive/cf/LegacyWebArchive.cpp b/Source/WebCore/loader/archive/cf/LegacyWebArchive.cpp
new file mode 100644
index 0000000..ddd564e
--- /dev/null
+++ b/Source/WebCore/loader/archive/cf/LegacyWebArchive.cpp
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LegacyWebArchive.h"
+
+#include "MemoryCache.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "HTMLFrameOwnerElement.h"
+#include "HTMLNames.h"
+#include "IconDatabase.h"
+#include "Image.h"
+#include "KURLHash.h"
+#include "Logging.h"
+#include "markup.h"
+#include "Node.h"
+#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>
+
+namespace WebCore {
+
+static const CFStringRef LegacyWebArchiveMainResourceKey = CFSTR("WebMainResource");
+static const CFStringRef LegacyWebArchiveSubresourcesKey = CFSTR("WebSubresources");
+static const CFStringRef LegacyWebArchiveSubframeArchivesKey = CFSTR("WebSubframeArchives");
+static const CFStringRef LegacyWebArchiveResourceDataKey = CFSTR("WebResourceData");
+static const CFStringRef LegacyWebArchiveResourceFrameNameKey = CFSTR("WebResourceFrameName");
+static const CFStringRef LegacyWebArchiveResourceMIMETypeKey = CFSTR("WebResourceMIMEType");
+static const CFStringRef LegacyWebArchiveResourceURLKey = CFSTR("WebResourceURL");
+static const CFStringRef LegacyWebArchiveResourceTextEncodingNameKey = CFSTR("WebResourceTextEncodingName");
+static const CFStringRef LegacyWebArchiveResourceResponseKey = CFSTR("WebResourceResponse");
+static const CFStringRef LegacyWebArchiveResourceResponseVersionKey = CFSTR("WebResourceResponseVersion");
+
+RetainPtr<CFDictionaryRef> LegacyWebArchive::createPropertyListRepresentation(ArchiveResource* resource, MainResourceStatus isMainResource)
+{
+ if (!resource) {
+ // The property list representation of a null/empty WebResource has the following 3 objects stored as nil.
+ // FIXME: 0 is not serializable. Presumably we need to use kCFNull here instead for compatibility.
+ // FIXME: But why do we need to support a resource of 0? Who relies on that?
+ RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, 0));
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, 0);
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, 0);
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, 0);
+ return propertyList;
+ }
+
+ RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 6, 0, &kCFTypeDictionaryValueCallBacks));
+
+ // Resource data can be empty, but must be represented by an empty CFDataRef
+ SharedBuffer* data = resource->data();
+ RetainPtr<CFDataRef> cfData;
+ if (data)
+ cfData.adoptCF(data->createCFData());
+ else
+ cfData.adoptCF(CFDataCreate(0, 0, 0));
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, cfData.get());
+
+ // Resource URL cannot be null
+ RetainPtr<CFStringRef> cfURL(AdoptCF, resource->url().string().createCFString());
+ if (cfURL)
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, cfURL.get());
+ else {
+ LOG(Archives, "LegacyWebArchive - NULL resource URL is invalid - returning null property list");
+ return 0;
+ }
+
+ // FrameName should be left out if empty for subresources, but always included for main resources
+ const String& frameName(resource->frameName());
+ if (!frameName.isEmpty() || isMainResource) {
+ RetainPtr<CFStringRef> cfFrameName(AdoptCF, frameName.createCFString());
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceFrameNameKey, cfFrameName.get());
+ }
+
+ // Set MIMEType, TextEncodingName, and ResourceResponse only if they actually exist
+ const String& mimeType(resource->mimeType());
+ if (!mimeType.isEmpty()) {
+ RetainPtr<CFStringRef> cfMIMEType(AdoptCF, mimeType.createCFString());
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, cfMIMEType.get());
+ }
+
+ const String& textEncoding(resource->textEncoding());
+ if (!textEncoding.isEmpty()) {
+ RetainPtr<CFStringRef> cfTextEncoding(AdoptCF, textEncoding.createCFString());
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceTextEncodingNameKey, cfTextEncoding.get());
+ }
+
+ // Don't include the resource response for the main resource
+ if (!isMainResource) {
+ RetainPtr<CFDataRef> resourceResponseData = createPropertyListRepresentation(resource->response());
+ if (resourceResponseData)
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceResponseKey, resourceResponseData.get());
+ }
+
+ return propertyList;
+}
+
+RetainPtr<CFDictionaryRef> LegacyWebArchive::createPropertyListRepresentation(Archive* archive)
+{
+ RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, &kCFTypeDictionaryValueCallBacks));
+
+ RetainPtr<CFDictionaryRef> mainResourceDict = createPropertyListRepresentation(archive->mainResource(), MainResource);
+ ASSERT(mainResourceDict);
+ if (!mainResourceDict)
+ return 0;
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveMainResourceKey, mainResourceDict.get());
+
+ RetainPtr<CFMutableArrayRef> subresourcesArray(AdoptCF, CFArrayCreateMutable(0, archive->subresources().size(), &kCFTypeArrayCallBacks));
+ const Vector<RefPtr<ArchiveResource> >& subresources(archive->subresources());
+ for (unsigned i = 0; i < subresources.size(); ++i) {
+ RetainPtr<CFDictionaryRef> subresource = createPropertyListRepresentation(subresources[i].get(), Subresource);
+ if (subresource)
+ CFArrayAppendValue(subresourcesArray.get(), subresource.get());
+ else
+ LOG(Archives, "LegacyWebArchive - Failed to create property list for subresource");
+ }
+ if (CFArrayGetCount(subresourcesArray.get()))
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubresourcesKey, subresourcesArray.get());
+
+ RetainPtr<CFMutableArrayRef> subframesArray(AdoptCF, CFArrayCreateMutable(0, archive->subframeArchives().size(), &kCFTypeArrayCallBacks));
+ const Vector<RefPtr<Archive> >& subframeArchives(archive->subframeArchives());
+ for (unsigned i = 0; i < subframeArchives.size(); ++i) {
+ RetainPtr<CFDictionaryRef> subframeArchive = createPropertyListRepresentation(subframeArchives[i].get());
+ if (subframeArchive)
+ CFArrayAppendValue(subframesArray.get(), subframeArchive.get());
+ else
+ LOG(Archives, "LegacyWebArchive - Failed to create property list for subframe archive");
+ }
+ if (CFArrayGetCount(subframesArray.get()))
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubframeArchivesKey, subframesArray.get());
+
+ return propertyList;
+}
+
+ResourceResponse LegacyWebArchive::createResourceResponseFromPropertyListData(CFDataRef data, CFStringRef responseDataType)
+{
+ ASSERT(data);
+ if (!data)
+ return ResourceResponse();
+
+ // If the ResourceResponseVersion (passed in as responseDataType) exists at all, this is a "new" web archive that we
+ // can parse well in a cross platform manner If it doesn't exist, we will assume this is an "old" web archive with,
+ // NSURLResponse objects in it and parse the ResourceResponse as such.
+ if (!responseDataType)
+ return createResourceResponseFromMacArchivedData(data);
+
+ // FIXME: Parse the "new" format that the above comment references here. This format doesn't exist yet.
+ return ResourceResponse();
+}
+
+PassRefPtr<ArchiveResource> LegacyWebArchive::createResource(CFDictionaryRef dictionary)
+{
+ ASSERT(dictionary);
+ if (!dictionary)
+ return 0;
+
+ CFDataRef resourceData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceDataKey));
+ if (resourceData && CFGetTypeID(resourceData) != CFDataGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Resource data is not of type CFData, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef frameName = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceFrameNameKey));
+ if (frameName && CFGetTypeID(frameName) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Frame name is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef mimeType = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceMIMETypeKey));
+ if (!mimeType || CFGetTypeID(mimeType) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - MIME type is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef url = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceURLKey));
+ if (url && CFGetTypeID(url) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - URL is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef textEncoding = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceTextEncodingNameKey));
+ if (textEncoding && CFGetTypeID(textEncoding) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Text encoding is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ ResourceResponse response;
+
+ CFDataRef resourceResponseData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseKey));
+ if (resourceResponseData) {
+ if (CFGetTypeID(resourceResponseData) != CFDataGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Resource response data is not of type CFData, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef resourceResponseVersion = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseVersionKey));
+ if (resourceResponseVersion && CFGetTypeID(resourceResponseVersion) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Resource response version is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ response = createResourceResponseFromPropertyListData(resourceResponseData, resourceResponseVersion);
+ }
+
+ return ArchiveResource::create(SharedBuffer::wrapCFData(resourceData), KURL(KURL(), url), mimeType, textEncoding, frameName, response);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create()
+{
+ return adoptRef(new LegacyWebArchive);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives)
+{
+ ASSERT(mainResource);
+ if (!mainResource)
+ return 0;
+
+ RefPtr<LegacyWebArchive> archive = create();
+ archive->setMainResource(mainResource);
+
+ for (unsigned i = 0; i < subresources.size(); ++i)
+ archive->addSubresource(subresources[i]);
+
+ for (unsigned i = 0; i < subframeArchives.size(); ++i)
+ archive->addSubframeArchive(subframeArchives[i]);
+
+ return archive.release();
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(SharedBuffer* data)
+{
+ LOG(Archives, "LegacyWebArchive - Creating from raw data");
+
+ RefPtr<LegacyWebArchive> archive = create();
+
+ ASSERT(data);
+ if (!data)
+ return 0;
+
+ RetainPtr<CFDataRef> cfData(AdoptCF, data->createCFData());
+ if (!cfData)
+ return 0;
+
+ CFStringRef errorString = 0;
+
+ RetainPtr<CFDictionaryRef> plist(AdoptCF, static_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(0, cfData.get(), kCFPropertyListImmutable, &errorString)));
+ if (!plist) {
+#ifndef NDEBUG
+ const char* cError = errorString ? CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8) : "unknown error";
+ LOG(Archives, "LegacyWebArchive - Error parsing PropertyList from archive data - %s", cError);
+#endif
+ if (errorString)
+ CFRelease(errorString);
+ return 0;
+ }
+
+ if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Archive property list is not the expected CFDictionary, aborting invalid WebArchive");
+ return 0;
+ }
+
+ if (!archive->extract(plist.get()))
+ return 0;
+
+ return archive.release();
+}
+
+bool LegacyWebArchive::extract(CFDictionaryRef dictionary)
+{
+ ASSERT(dictionary);
+ if (!dictionary) {
+ LOG(Archives, "LegacyWebArchive - Null root CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+
+ CFDictionaryRef mainResourceDict = static_cast<CFDictionaryRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveMainResourceKey));
+ if (!mainResourceDict) {
+ LOG(Archives, "LegacyWebArchive - No main resource in archive, aborting invalid WebArchive");
+ return false;
+ }
+ if (CFGetTypeID(mainResourceDict) != CFDictionaryGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Main resource is not the expected CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+
+ setMainResource(createResource(mainResourceDict));
+ if (!mainResource()) {
+ LOG(Archives, "LegacyWebArchive - Failed to parse main resource from CFDictionary or main resource does not exist, aborting invalid WebArchive");
+ return false;
+ }
+
+ CFArrayRef subresourceArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubresourcesKey));
+ if (subresourceArray && CFGetTypeID(subresourceArray) != CFArrayGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Subresources is not the expected Array, aborting invalid WebArchive");
+ return false;
+ }
+
+ if (subresourceArray) {
+ CFIndex count = CFArrayGetCount(subresourceArray);
+ for (CFIndex i = 0; i < count; ++i) {
+ CFDictionaryRef subresourceDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subresourceArray, i));
+ if (CFGetTypeID(subresourceDict) != CFDictionaryGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Subresource is not expected CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+ addSubresource(createResource(subresourceDict));
+ }
+ }
+
+ CFArrayRef subframeArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubframeArchivesKey));
+ if (subframeArray && CFGetTypeID(subframeArray) != CFArrayGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Subframe archives is not the expected Array, aborting invalid WebArchive");
+ return false;
+ }
+
+ if (subframeArray) {
+ CFIndex count = CFArrayGetCount(subframeArray);
+ for (CFIndex i = 0; i < count; ++i) {
+ CFDictionaryRef subframeDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subframeArray, i));
+ if (CFGetTypeID(subframeDict) != CFDictionaryGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Subframe array is not expected CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+
+ RefPtr<LegacyWebArchive> subframeArchive = create();
+ if (subframeArchive->extract(subframeDict))
+ addSubframeArchive(subframeArchive.release());
+ else
+ LOG(Archives, "LegacyWebArchive - Invalid subframe archive skipped");
+ }
+ }
+
+ return true;
+}
+
+RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation()
+{
+ RetainPtr<CFDictionaryRef> propertyList = createPropertyListRepresentation(this);
+ ASSERT(propertyList);
+ if (!propertyList) {
+ LOG(Archives, "LegacyWebArchive - Failed to create property list for archive, returning no data");
+ return 0;
+ }
+
+ RetainPtr<CFWriteStreamRef> stream(AdoptCF, CFWriteStreamCreateWithAllocatedBuffers(0, 0));
+
+ CFWriteStreamOpen(stream.get());
+ CFPropertyListWriteToStream(propertyList.get(), stream.get(), kCFPropertyListBinaryFormat_v1_0, 0);
+
+ RetainPtr<CFDataRef> plistData(AdoptCF, static_cast<CFDataRef>(CFWriteStreamCopyProperty(stream.get(), kCFStreamPropertyDataWritten)));
+ ASSERT(plistData);
+
+ CFWriteStreamClose(stream.get());
+
+ if (!plistData) {
+ LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data");
+ return 0;
+ }
+
+ return plistData;
+}
+
+#if !PLATFORM(MAC)
+
+ResourceResponse LegacyWebArchive::createResourceResponseFromMacArchivedData(CFDataRef responseData)
+{
+ // FIXME: If is is possible to parse in a serialized NSURLResponse manually, without using
+ // NSKeyedUnarchiver, manipulating plists directly, then we want to do that here.
+ // Until then, this can be done on Mac only.
+ return ResourceResponse();
+}
+
+RetainPtr<CFDataRef> LegacyWebArchive::createPropertyListRepresentation(const ResourceResponse& response)
+{
+ // FIXME: Write out the "new" format described in createResourceResponseFromPropertyListData once we invent it.
+ return 0;
+}
+
+#endif
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Node* node)
+{
+ ASSERT(node);
+ if (!node)
+ return create();
+
+ Document* document = node->document();
+ Frame* frame = document ? document->frame() : 0;
+ if (!frame)
+ return create();
+
+ Vector<Node*> nodeList;
+ String markupString = createMarkup(node, IncludeNode, &nodeList);
+ Node::NodeType nodeType = node->nodeType();
+ if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
+ markupString = frame->documentTypeString() + markupString;
+
+ return create(markupString, frame, nodeList);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Frame* frame)
+{
+ ASSERT(frame);
+
+ DocumentLoader* documentLoader = frame->loader()->documentLoader();
+
+ if (!documentLoader)
+ return 0;
+
+ Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
+
+ unsigned children = frame->tree()->childCount();
+ for (unsigned i = 0; i < children; ++i) {
+ RefPtr<LegacyWebArchive> childFrameArchive = create(frame->tree()->child(i));
+ if (childFrameArchive)
+ subframeArchives.append(childFrameArchive.release());
+ }
+
+ Vector<PassRefPtr<ArchiveResource> > subresources;
+ documentLoader->getSubresources(subresources);
+
+ return create(documentLoader->mainResource(), subresources, subframeArchives);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range)
+{
+ if (!range)
+ return 0;
+
+ Node* startContainer = range->startContainer();
+ if (!startContainer)
+ return 0;
+
+ Document* document = startContainer->document();
+ if (!document)
+ return 0;
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return 0;
+
+ Vector<Node*> nodeList;
+
+ // FIXME: This is always "for interchange". Is that right? See the previous method.
+ String markupString = frame->documentTypeString() + createMarkup(range, &nodeList, AnnotateForInterchange);
+
+ return create(markupString, frame, nodeList);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, const Vector<Node*>& nodes)
+{
+ ASSERT(frame);
+
+ const ResourceResponse& response = frame->loader()->documentLoader()->response();
+ KURL responseURL = response.url();
+
+ // it's possible to have a response without a URL here
+ // <rdar://problem/5454935>
+ if (responseURL.isNull())
+ responseURL = KURL(ParsedURLString, "");
+
+ PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->uniqueName());
+
+ Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
+ Vector<PassRefPtr<ArchiveResource> > subresources;
+ HashSet<KURL> uniqueSubresources;
+
+ size_t nodesSize = nodes.size();
+ for (size_t i = 0; i < nodesSize; ++i) {
+ Node* node = nodes[i];
+ Frame* childFrame;
+ if ((node->hasTagName(HTMLNames::frameTag) || node->hasTagName(HTMLNames::iframeTag) || node->hasTagName(HTMLNames::objectTag)) &&
+ (childFrame = static_cast<HTMLFrameOwnerElement*>(node)->contentFrame())) {
+ RefPtr<LegacyWebArchive> subframeArchive = create(childFrame->document());
+
+ if (subframeArchive)
+ subframeArchives.append(subframeArchive);
+ else
+ LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->uniqueName().string().utf8().data());
+ } else {
+ ListHashSet<KURL> subresourceURLs;
+ node->getSubresourceURLs(subresourceURLs);
+
+ DocumentLoader* documentLoader = frame->loader()->documentLoader();
+ ListHashSet<KURL>::iterator iterEnd = subresourceURLs.end();
+ for (ListHashSet<KURL>::iterator iter = subresourceURLs.begin(); iter != iterEnd; ++iter) {
+ const KURL& subresourceURL = *iter;
+ if (uniqueSubresources.contains(subresourceURL))
+ continue;
+
+ uniqueSubresources.add(subresourceURL);
+
+ RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURL);
+ if (resource) {
+ subresources.append(resource.release());
+ continue;
+ }
+
+ CachedResource *cachedResource = cache()->resourceForURL(subresourceURL);
+ if (cachedResource) {
+ resource = ArchiveResource::create(cachedResource->data(), subresourceURL, cachedResource->response());
+ if (resource) {
+ subresources.append(resource.release());
+ continue;
+ }
+ }
+
+ // FIXME: should do something better than spew to console here
+ LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data());
+ }
+ }
+ }
+
+ // Add favicon if one exists for this page, if we are archiving the entire page.
+ if (nodesSize && nodes[0]->isDocumentNode() && iconDatabase() && iconDatabase()->isEnabled()) {
+ const String& iconURL = iconDatabase()->iconURLForPageURL(responseURL);
+ if (!iconURL.isEmpty() && iconDatabase()->iconDataKnownForIconURL(iconURL)) {
+ if (Image* iconImage = iconDatabase()->iconForPageURL(responseURL, IntSize(16, 16))) {
+ if (RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), KURL(ParsedURLString, iconURL), "image/x-icon", "", ""))
+ subresources.append(resource.release());
+ }
+ }
+ }
+
+ return create(mainResource, subresources, subframeArchives);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame)
+{
+ if (!frame)
+ return 0;
+
+ RefPtr<Range> selectionRange = frame->selection()->toNormalizedRange();
+ Vector<Node*> nodeList;
+ String markupString = frame->documentTypeString() + createMarkup(selectionRange.get(), &nodeList, AnnotateForInterchange);
+
+ RefPtr<LegacyWebArchive> archive = create(markupString, frame, nodeList);
+
+ if (!frame->document() || !frame->document()->isFrameSet())
+ return archive.release();
+
+ // Wrap the frameset document in an iframe so it can be pasted into
+ // another document (which will have a body or frameset of its own).
+ String iframeMarkup = 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;
+
+ Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
+ subframeArchives.append(archive);
+
+ archive = create(iframeResource.release(), subresources, subframeArchives);
+
+ return archive.release();
+}
+
+}
diff --git a/Source/WebCore/loader/archive/cf/LegacyWebArchive.h b/Source/WebCore/loader/archive/cf/LegacyWebArchive.h
new file mode 100644
index 0000000..8c8f2e4
--- /dev/null
+++ b/Source/WebCore/loader/archive/cf/LegacyWebArchive.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LegacyWebArchive_h
+#define LegacyWebArchive_h
+
+#include "Archive.h"
+
+namespace WebCore {
+
+class Frame;
+class Node;
+class Range;
+
+class LegacyWebArchive : public Archive {
+public:
+ static PassRefPtr<LegacyWebArchive> create();
+ static PassRefPtr<LegacyWebArchive> create(SharedBuffer*);
+ static PassRefPtr<LegacyWebArchive> create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives);
+ static PassRefPtr<LegacyWebArchive> create(Node*);
+ static PassRefPtr<LegacyWebArchive> create(Frame*);
+ static PassRefPtr<LegacyWebArchive> createFromSelection(Frame*);
+ static PassRefPtr<LegacyWebArchive> create(Range*);
+
+ RetainPtr<CFDataRef> rawDataRepresentation();
+
+private:
+ LegacyWebArchive() { }
+
+ enum MainResourceStatus { Subresource, MainResource };
+
+ static PassRefPtr<LegacyWebArchive> create(const String& markupString, Frame*, const Vector<Node*>& nodes);
+ static PassRefPtr<ArchiveResource> createResource(CFDictionaryRef);
+ static ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef);
+ static ResourceResponse createResourceResponseFromPropertyListData(CFDataRef, CFStringRef responseDataType);
+ static RetainPtr<CFDataRef> createPropertyListRepresentation(const ResourceResponse&);
+ static RetainPtr<CFDictionaryRef> createPropertyListRepresentation(Archive*);
+ static RetainPtr<CFDictionaryRef> createPropertyListRepresentation(ArchiveResource*, MainResourceStatus);
+
+ bool extract(CFDictionaryRef);
+};
+
+}
+
+#endif // Archive
diff --git a/Source/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm b/Source/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm
new file mode 100644
index 0000000..6a35753
--- /dev/null
+++ b/Source/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LegacyWebArchive.h"
+
+namespace WebCore {
+
+static 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.
+ResourceResponse LegacyWebArchive::createResourceResponseFromMacArchivedData(CFDataRef responseData)
+{
+ ASSERT(responseData);
+ if (!responseData)
+ return ResourceResponse();
+
+ NSURLResponse *response = nil;
+ NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)responseData];
+ @try {
+ id responseObject = [unarchiver decodeObjectForKey:LegacyWebArchiveResourceResponseKey];
+ if ([responseObject isKindOfClass:[NSURLResponse class]])
+ response = responseObject;
+ [unarchiver finishDecoding];
+ } @catch(id) {
+ response = nil;
+ }
+ [unarchiver release];
+
+ return ResourceResponse(response);
+}
+
+RetainPtr<CFDataRef> LegacyWebArchive::createPropertyListRepresentation(const ResourceResponse& response)
+{
+ NSURLResponse *nsResponse = response.nsURLResponse();
+ ASSERT(nsResponse);
+ if (!nsResponse)
+ return 0;
+
+ CFMutableDataRef responseData = CFDataCreateMutable(0, 0);
+
+ NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:(NSMutableData *)responseData];
+ [archiver encodeObject:nsResponse forKey:LegacyWebArchiveResourceResponseKey];
+ [archiver finishEncoding];
+ [archiver release];
+
+ return RetainPtr<CFDataRef>(AdoptCF, responseData);
+}
+
+}
diff --git a/Source/WebCore/loader/cache/CachePolicy.h b/Source/WebCore/loader/cache/CachePolicy.h
new file mode 100644
index 0000000..0b9010b
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachePolicy.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CachePolicy_h
+#define CachePolicy_h
+
+namespace WebCore {
+
+ enum CachePolicy {
+ CachePolicyCache,
+ CachePolicyVerify,
+ CachePolicyRevalidate,
+ CachePolicyReload,
+ CachePolicyHistoryBuffer
+ };
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp b/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp
new file mode 100644
index 0000000..ae7a03c
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp
@@ -0,0 +1,151 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
+
+ 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"
+#include "CachedCSSStyleSheet.h"
+
+#include "MemoryCache.h"
+#include "CachedResourceClient.h"
+#include "CachedResourceClientWalker.h"
+#include "HTTPParsers.h"
+#include "TextResourceDecoder.h"
+#include "SharedBuffer.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+CachedCSSStyleSheet::CachedCSSStyleSheet(const String& url, const String& charset)
+ : CachedResource(url, CSSStyleSheet)
+ , m_decoder(TextResourceDecoder::create("text/css", charset))
+{
+ // Prefer text/css but accept any type (dell.com serves a stylesheet
+ // as text/html; see <http://bugs.webkit.org/show_bug.cgi?id=11451>).
+ setAccept("text/css,*/*;q=0.1");
+}
+
+CachedCSSStyleSheet::~CachedCSSStyleSheet()
+{
+}
+
+void CachedCSSStyleSheet::didAddClient(CachedResourceClient *c)
+{
+ if (!isLoading())
+ c->setCSSStyleSheet(m_url, m_response.url(), m_decoder->encoding().name(), this);
+}
+
+void CachedCSSStyleSheet::allClientsRemoved()
+{
+ if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable())
+ makePurgeable(true);
+}
+
+void CachedCSSStyleSheet::setEncoding(const String& chs)
+{
+ m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader);
+}
+
+String CachedCSSStyleSheet::encoding() const
+{
+ return m_decoder->encoding().name();
+}
+
+const String CachedCSSStyleSheet::sheetText(bool enforceMIMEType, bool* hasValidMIMEType) const
+{
+ ASSERT(!isPurgeable());
+
+ if (!m_data || m_data->isEmpty() || !canUseSheet(enforceMIMEType, hasValidMIMEType))
+ return String();
+
+ if (!m_decodedSheetText.isNull())
+ return m_decodedSheetText;
+
+ // Don't cache the decoded text, regenerating is cheap and it can use quite a bit of memory
+ String sheetText = m_decoder->decode(m_data->data(), m_data->size());
+ sheetText += m_decoder->flush();
+ return sheetText;
+}
+
+void CachedCSSStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
+{
+ if (!allDataReceived)
+ return;
+
+ m_data = data;
+ setEncodedSize(m_data.get() ? m_data->size() : 0);
+ // Decode the data to find out the encoding and keep the sheet text around during checkNotify()
+ if (m_data) {
+ m_decodedSheetText = m_decoder->decode(m_data->data(), m_data->size());
+ m_decodedSheetText += m_decoder->flush();
+ }
+ setLoading(false);
+ checkNotify();
+ // Clear the decoded text as it is unlikely to be needed immediately again and is cheap to regenerate.
+ m_decodedSheetText = String();
+}
+
+void CachedCSSStyleSheet::checkNotify()
+{
+ if (isLoading())
+ return;
+
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient *c = w.next())
+ c->setCSSStyleSheet(m_url, m_response.url(), m_decoder->encoding().name(), this);
+}
+
+void CachedCSSStyleSheet::error(CachedResource::Status status)
+{
+ setStatus(status);
+ ASSERT(errorOccurred());
+ setLoading(false);
+ checkNotify();
+}
+
+bool CachedCSSStyleSheet::canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const
+{
+ if (errorOccurred())
+ return false;
+
+ if (!enforceMIMEType && !hasValidMIMEType)
+ return true;
+
+ // This check exactly matches Firefox. Note that we grab the Content-Type
+ // header directly because we want to see what the value is BEFORE content
+ // sniffing. Firefox does this by setting a "type hint" on the channel.
+ // This implementation should be observationally equivalent.
+ //
+ // This code defaults to allowing the stylesheet for non-HTTP protocols so
+ // folks can use standards mode for local HTML documents.
+ String mimeType = extractMIMETypeFromMediaType(response().httpHeaderField("Content-Type"));
+ bool typeOK = mimeType.isEmpty() || equalIgnoringCase(mimeType, "text/css") || equalIgnoringCase(mimeType, "application/x-unknown-content-type");
+ if (hasValidMIMEType)
+ *hasValidMIMEType = typeOK;
+ if (!enforceMIMEType)
+ return true;
+ return typeOK;
+}
+
+}
diff --git a/Source/WebCore/loader/cache/CachedCSSStyleSheet.h b/Source/WebCore/loader/cache/CachedCSSStyleSheet.h
new file mode 100644
index 0000000..a982e03
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedCSSStyleSheet.h
@@ -0,0 +1,67 @@
+/*
+ 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 CachedCSSStyleSheet_h
+#define CachedCSSStyleSheet_h
+
+#include "CachedResource.h"
+#include "TextEncoding.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+ class CachedResourceLoader;
+ class TextResourceDecoder;
+
+ class CachedCSSStyleSheet : public CachedResource {
+ public:
+ CachedCSSStyleSheet(const String& URL, const String& charset);
+ virtual ~CachedCSSStyleSheet();
+
+ const String sheetText(bool enforceMIMEType = true, bool* hasValidMIMEType = 0) const;
+
+ virtual void didAddClient(CachedResourceClient*);
+
+ virtual void allClientsRemoved();
+
+ virtual void setEncoding(const String&);
+ virtual String encoding() const;
+ virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived);
+ virtual void error(CachedResource::Status);
+
+ void checkNotify();
+
+ private:
+ bool canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const;
+ virtual PurgePriority purgePriority() const { return PurgeLast; }
+
+ protected:
+ RefPtr<TextResourceDecoder> m_decoder;
+ String m_decodedSheetText;
+ };
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedFont.cpp b/Source/WebCore/loader/cache/CachedFont.cpp
new file mode 100644
index 0000000..d6967bf
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedFont.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CachedFont.h"
+
+#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 "CachedResourceClient.h"
+#include "CachedResourceClientWalker.h"
+#include "CachedResourceLoader.h"
+#include "FontPlatformData.h"
+#include "MemoryCache.h"
+#include "SharedBuffer.h"
+#include "TextResourceDecoder.h"
+#include <wtf/Vector.h>
+
+#ifdef STORE_FONT_CUSTOM_PLATFORM_DATA
+#include "FontCustomPlatformData.h"
+#endif
+
+#if ENABLE(SVG_FONTS)
+#include "NodeList.h"
+#include "SVGElement.h"
+#include "SVGFontElement.h"
+#include "SVGGElement.h"
+#include "SVGNames.h"
+#endif
+
+namespace WebCore {
+
+CachedFont::CachedFont(const String &url)
+ : CachedResource(url, FontResource)
+ , m_fontData(0)
+ , m_loadInitiated(false)
+#if ENABLE(SVG_FONTS)
+ , m_isSVGFont(false)
+#endif
+{
+}
+
+CachedFont::~CachedFont()
+{
+#ifdef STORE_FONT_CUSTOM_PLATFORM_DATA
+ delete m_fontData;
+#endif
+}
+
+void CachedFont::load(CachedResourceLoader*)
+{
+ // Don't load the file yet. Wait for an access before triggering the load.
+ setLoading(true);
+}
+
+void CachedFont::didAddClient(CachedResourceClient* c)
+{
+ if (!isLoading())
+ c->fontLoaded(this);
+}
+
+void CachedFont::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
+{
+ if (!allDataReceived)
+ return;
+
+ m_data = data;
+ setEncodedSize(m_data.get() ? m_data->size() : 0);
+ setLoading(false);
+ checkNotify();
+}
+
+void CachedFont::beginLoadIfNeeded(CachedResourceLoader* dl)
+{
+ if (!m_loadInitiated) {
+ m_loadInitiated = true;
+ dl->load(this, false);
+ }
+}
+
+bool CachedFont::ensureCustomFontData()
+{
+#ifdef STORE_FONT_CUSTOM_PLATFORM_DATA
+#if ENABLE(SVG_FONTS)
+ ASSERT(!m_isSVGFont);
+#endif
+ if (!m_fontData && !errorOccurred() && !isLoading() && m_data) {
+ m_fontData = createFontCustomPlatformData(m_data.get());
+ if (!m_fontData)
+ setStatus(DecodeError);
+ }
+#endif
+ return m_fontData;
+}
+
+FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation orientation, FontRenderingMode renderingMode)
+{
+#if ENABLE(SVG_FONTS)
+ if (m_externalSVGDocument)
+ return FontPlatformData(size, bold, italic);
+#endif
+#ifdef STORE_FONT_CUSTOM_PLATFORM_DATA
+ ASSERT(m_fontData);
+ return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic, orientation, renderingMode);
+#else
+ return FontPlatformData();
+#endif
+}
+
+#if ENABLE(SVG_FONTS)
+bool CachedFont::ensureSVGFontData()
+{
+ ASSERT(m_isSVGFont);
+ if (!m_externalSVGDocument && !errorOccurred() && !isLoading() && m_data) {
+ m_externalSVGDocument = SVGDocument::create(0, KURL());
+ m_externalSVGDocument->open();
+
+ RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml");
+ m_externalSVGDocument->write(decoder->decode(m_data->data(), m_data->size()));
+ m_externalSVGDocument->write(decoder->flush());
+ if (decoder->sawError()) {
+ m_externalSVGDocument.clear();
+ return 0;
+ }
+
+ m_externalSVGDocument->finishParsing();
+ m_externalSVGDocument->close();
+ }
+
+ return m_externalSVGDocument;
+}
+
+SVGFontElement* CachedFont::getSVGFontById(const String& fontName) const
+{
+ ASSERT(m_isSVGFont);
+ RefPtr<NodeList> list = m_externalSVGDocument->getElementsByTagNameNS(SVGNames::fontTag.namespaceURI(), SVGNames::fontTag.localName());
+ if (!list)
+ return 0;
+
+ 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 (fontName.isEmpty())
+ return static_cast<SVGFontElement*>(list->item(0));
+
+ for (unsigned i = 0; i < listLength; ++i) {
+ SVGFontElement* element = static_cast<SVGFontElement*>(list->item(i));
+ if (element->getIdAttribute() == fontName)
+ return element;
+ }
+
+ return 0;
+}
+#endif
+
+void CachedFont::allClientsRemoved()
+{
+#ifdef STORE_FONT_CUSTOM_PLATFORM_DATA
+ if (m_fontData) {
+ delete m_fontData;
+ m_fontData = 0;
+ }
+#endif
+}
+
+void CachedFont::checkNotify()
+{
+ if (isLoading())
+ return;
+
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient *c = w.next())
+ c->fontLoaded(this);
+}
+
+
+void CachedFont::error(CachedResource::Status status)
+{
+ setStatus(status);
+ ASSERT(errorOccurred());
+ setLoading(false);
+ checkNotify();
+}
+
+}
diff --git a/Source/WebCore/loader/cache/CachedFont.h b/Source/WebCore/loader/cache/CachedFont.h
new file mode 100644
index 0000000..5814087
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedFont.h
@@ -0,0 +1,89 @@
+/*
+ * 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,
+ * 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 CachedFont_h
+#define CachedFont_h
+
+#include "CachedResource.h"
+#include "FontOrientation.h"
+#include "FontRenderingMode.h"
+#include <wtf/Vector.h>
+
+#if ENABLE(SVG_FONTS)
+#include "SVGElement.h"
+#include "SVGDocument.h"
+#endif
+
+namespace WebCore {
+
+class CachedResourceLoader;
+class MemoryCache;
+class FontPlatformData;
+class SVGFontElement;
+
+struct FontCustomPlatformData;
+
+class CachedFont : public CachedResource {
+public:
+ CachedFont(const String& url);
+ virtual ~CachedFont();
+
+ virtual void load(CachedResourceLoader* cachedResourceLoader);
+
+ virtual void didAddClient(CachedResourceClient*);
+ virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived);
+ virtual void error(CachedResource::Status);
+
+ virtual void allClientsRemoved();
+
+ void checkNotify();
+
+ void beginLoadIfNeeded(CachedResourceLoader* dl);
+
+ bool ensureCustomFontData();
+ FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode);
+
+#if ENABLE(SVG_FONTS)
+ bool isSVGFont() const { return m_isSVGFont; }
+ void setSVGFont(bool isSVG) { m_isSVGFont = isSVG; }
+ bool ensureSVGFontData();
+ SVGFontElement* getSVGFontById(const String&) const;
+#endif
+
+private:
+ FontCustomPlatformData* m_fontData;
+ bool m_loadInitiated;
+
+#if ENABLE(SVG_FONTS)
+ bool m_isSVGFont;
+ RefPtr<SVGDocument> m_externalSVGDocument;
+#endif
+
+ friend class MemoryCache;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedImage.cpp b/Source/WebCore/loader/cache/CachedImage.cpp
new file mode 100644
index 0000000..c550eec
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedImage.cpp
@@ -0,0 +1,386 @@
+/*
+ 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 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "CachedImage.h"
+
+#include "BitmapImage.h"
+#include "MemoryCache.h"
+#include "CachedResourceClient.h"
+#include "CachedResourceClientWalker.h"
+#include "CachedResourceLoader.h"
+#include "CachedResourceRequest.h"
+#include "Frame.h"
+#include "FrameLoaderTypes.h"
+#include "FrameView.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+#if PLATFORM(CG)
+#include "PDFDocumentImage.h"
+#endif
+
+#if ENABLE(SVG_AS_IMAGE)
+#include "SVGImage.h"
+#endif
+
+using std::max;
+
+namespace WebCore {
+
+CachedImage::CachedImage(const String& url)
+ : CachedResource(url, ImageResource)
+ , m_image(0)
+ , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
+{
+ setStatus(Unknown);
+}
+
+CachedImage::CachedImage(Image* image)
+ : CachedResource(String(), ImageResource)
+ , m_image(image)
+ , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
+{
+ setStatus(Cached);
+ setLoading(false);
+}
+
+CachedImage::~CachedImage()
+{
+}
+
+void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
+{
+ ASSERT(!hasClients());
+ destroyDecodedData();
+}
+
+void CachedImage::load(CachedResourceLoader* cachedResourceLoader)
+{
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ if (!cachedResourceLoader || (cachedResourceLoader->autoLoadImages() && !cachedResourceLoader->shouldBlockNetworkImage(m_url)))
+#else
+ if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages())
+#endif
+ CachedResource::load(cachedResourceLoader, true, DoSecurityCheck, true);
+ else
+ setLoading(false);
+}
+
+void CachedImage::didAddClient(CachedResourceClient* c)
+{
+ if (m_decodedDataDeletionTimer.isActive())
+ m_decodedDataDeletionTimer.stop();
+
+ if (m_data && !m_image && !errorOccurred()) {
+ createImage();
+ m_image->setData(m_data, true);
+ }
+
+ if (m_image && !m_image->isNull())
+ c->imageChanged(this);
+
+ if (!isLoading())
+ c->notifyFinished(this);
+}
+
+void CachedImage::allClientsRemoved()
+{
+ if (m_image && !errorOccurred())
+ m_image->resetAnimation();
+ if (double interval = cache()->deadDecodedDataDeletionInterval())
+ m_decodedDataDeletionTimer.startOneShot(interval);
+}
+
+static Image* brokenImage()
+{
+ DEFINE_STATIC_LOCAL(RefPtr<Image>, brokenImage, (Image::loadPlatformResource("missingImage")));
+ return brokenImage.get();
+}
+
+Image* CachedImage::image() const
+{
+ ASSERT(!isPurgeable());
+
+ if (errorOccurred())
+ return brokenImage();
+
+ if (m_image)
+ return m_image.get();
+
+ return Image::nullImage();
+}
+
+void CachedImage::setImageContainerSize(const IntSize& containerSize)
+{
+ if (m_image)
+ m_image->setContainerSize(containerSize);
+}
+
+bool CachedImage::usesImageContainerSize() const
+{
+ if (m_image)
+ return m_image->usesContainerSize();
+
+ return false;
+}
+
+bool CachedImage::imageHasRelativeWidth() const
+{
+ if (m_image)
+ return m_image->hasRelativeWidth();
+
+ return false;
+}
+
+bool CachedImage::imageHasRelativeHeight() const
+{
+ if (m_image)
+ return m_image->hasRelativeHeight();
+
+ return false;
+}
+
+IntSize CachedImage::imageSize(float multiplier) const
+{
+ ASSERT(!isPurgeable());
+
+ if (!m_image)
+ return IntSize();
+ if (multiplier == 1.0f)
+ return m_image->size();
+
+ // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
+ bool hasWidth = m_image->size().width() > 0;
+ bool hasHeight = m_image->size().height() > 0;
+ int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier);
+ int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier);
+ if (hasWidth)
+ width = max(1, width);
+ if (hasHeight)
+ height = max(1, height);
+ return IntSize(width, height);
+}
+
+IntRect CachedImage::imageRect(float multiplier) const
+{
+ ASSERT(!isPurgeable());
+
+ if (!m_image)
+ return IntRect();
+ if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight()))
+ return m_image->rect();
+
+ float widthMultiplier = (m_image->hasRelativeWidth() ? 1.0f : multiplier);
+ float heightMultiplier = (m_image->hasRelativeHeight() ? 1.0f : multiplier);
+
+ // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
+ bool hasWidth = m_image->rect().width() > 0;
+ bool hasHeight = m_image->rect().height() > 0;
+
+ int width = static_cast<int>(m_image->rect().width() * widthMultiplier);
+ int height = static_cast<int>(m_image->rect().height() * heightMultiplier);
+ if (hasWidth)
+ width = max(1, width);
+ if (hasHeight)
+ height = max(1, height);
+
+ int x = static_cast<int>(m_image->rect().x() * widthMultiplier);
+ int y = static_cast<int>(m_image->rect().y() * heightMultiplier);
+
+ return IntRect(x, y, width, height);
+}
+
+void CachedImage::notifyObservers(const IntRect* changeRect)
+{
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient* c = w.next())
+ c->imageChanged(this, changeRect);
+}
+
+void CachedImage::clear()
+{
+ destroyDecodedData();
+ m_image = 0;
+ setEncodedSize(0);
+}
+
+inline void CachedImage::createImage()
+{
+ // Create the image if it doesn't yet exist.
+ if (m_image)
+ return;
+#if PLATFORM(CG) && !USE(WEBKIT_IMAGE_DECODERS)
+ if (m_response.mimeType() == "application/pdf") {
+ m_image = PDFDocumentImage::create();
+ return;
+ }
+#endif
+#if ENABLE(SVG_AS_IMAGE)
+ if (m_response.mimeType() == "image/svg+xml") {
+ m_image = SVGImage::create(this);
+ return;
+ }
+#endif
+ m_image = BitmapImage::create(this);
+#if PLATFORM(ANDROID)
+ m_image->setURL(url());
+#endif
+}
+
+size_t CachedImage::maximumDecodedImageSize()
+{
+ Frame* frame = m_request ? m_request->cachedResourceLoader()->frame() : 0;
+ if (!frame)
+ return 0;
+ Settings* settings = frame->settings();
+ return settings ? settings->maximumDecodedImageSize() : 0;
+}
+
+void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
+{
+ m_data = data;
+
+ createImage();
+
+ bool sizeAvailable = false;
+
+ // Have the image update its data from its internal buffer.
+ // It will not do anything now, but will delay decoding until
+ // queried for info (like size or specific image frames).
+ sizeAvailable = m_image->setData(m_data, allDataReceived);
+
+ // Go ahead and tell our observers to try to draw if we have either
+ // received all the data or the size is known. Each chunk from the
+ // network causes observers to repaint, which will force that chunk
+ // to decode.
+ if (sizeAvailable || allDataReceived) {
+ size_t maxDecodedImageSize = maximumDecodedImageSize();
+ IntSize s = imageSize(1.0f);
+ size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
+ if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
+ error(errorOccurred() ? status() : DecodeError);
+ if (inCache())
+ cache()->remove(this);
+ return;
+ }
+
+ // It would be nice to only redraw the decoded band of the image, but with the current design
+ // (decoding delayed until painting) that seems hard.
+ notifyObservers();
+
+ if (m_image)
+ setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
+ }
+
+ if (allDataReceived) {
+ setLoading(false);
+ checkNotify();
+ }
+}
+
+void CachedImage::error(CachedResource::Status status)
+{
+ clear();
+ setStatus(status);
+ ASSERT(errorOccurred());
+ m_data.clear();
+ notifyObservers();
+ setLoading(false);
+ checkNotify();
+}
+
+void CachedImage::checkNotify()
+{
+ if (isLoading())
+ return;
+
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient* c = w.next())
+ c->notifyFinished(this);
+}
+
+void CachedImage::destroyDecodedData()
+{
+ bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
+ 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);
+ if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
+ makePurgeable(true);
+ } else if (m_image && !errorOccurred())
+ m_image->destroyDecodedData();
+}
+
+void CachedImage::decodedSizeChanged(const Image* image, int delta)
+{
+ if (image != m_image)
+ return;
+
+ setDecodedSize(decodedSize() + delta);
+}
+
+void CachedImage::didDraw(const Image* image)
+{
+ if (image != m_image)
+ return;
+
+ double timeStamp = FrameView::currentPaintTimeStamp();
+ if (!timeStamp) // If didDraw is called outside of a Frame paint.
+ timeStamp = currentTime();
+
+ CachedResource::didAccessDecodedData(timeStamp);
+}
+
+bool CachedImage::shouldPauseAnimation(const Image* image)
+{
+ if (image != m_image)
+ return false;
+
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient* c = w.next()) {
+ if (c->willRenderImage(this))
+ return false;
+ }
+
+ return true;
+}
+
+void CachedImage::animationAdvanced(const Image* image)
+{
+ if (image == m_image)
+ notifyObservers();
+}
+
+void CachedImage::changedInRect(const Image* image, const IntRect& rect)
+{
+ if (image == m_image)
+ notifyObservers(&rect);
+}
+
+} //namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedImage.h b/Source/WebCore/loader/cache/CachedImage.h
new file mode 100644
index 0000000..345d1e7
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedImage.h
@@ -0,0 +1,105 @@
+/*
+ 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 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.
+*/
+
+#ifndef CachedImage_h
+#define CachedImage_h
+
+#include "CachedResource.h"
+#include "ImageObserver.h"
+#include "IntRect.h"
+#include "Timer.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CachedResourceLoader;
+class MemoryCache;
+
+class CachedImage : public CachedResource, public ImageObserver {
+ friend class MemoryCache;
+
+public:
+ CachedImage(const String& url);
+ CachedImage(Image*);
+ virtual ~CachedImage();
+
+ virtual void load(CachedResourceLoader* cachedResourceLoader);
+
+ Image* image() const; // Returns the nullImage() if the image is not available yet.
+ bool hasImage() const { return m_image.get(); }
+
+ bool canRender(float multiplier) const { return !errorOccurred() && !imageSize(multiplier).isEmpty(); }
+
+ // These are only used for SVGImage right now
+ void setImageContainerSize(const IntSize&);
+ bool usesImageContainerSize() const;
+ bool imageHasRelativeWidth() const;
+ bool imageHasRelativeHeight() const;
+
+ // Both of these methods take a zoom multiplier that can be used to increase the natural size of the image by the
+ // zoom.
+ IntSize imageSize(float multiplier) const; // returns the size of the complete image.
+ IntRect imageRect(float multiplier) const; // The size of the currently decoded portion of the image.
+
+ virtual void didAddClient(CachedResourceClient*);
+
+ virtual void allClientsRemoved();
+ virtual void destroyDecodedData();
+
+ virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived);
+ virtual void error(CachedResource::Status);
+
+ // For compatibility, images keep loading even if there are HTTP errors.
+ virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return true; }
+
+ void checkNotify();
+
+ virtual bool isImage() const { return true; }
+
+ void clear();
+
+ bool stillNeedsLoad() const { return !errorOccurred() && status() == Unknown && !isLoading(); }
+ void load();
+
+ // ImageObserver
+ virtual void decodedSizeChanged(const Image* image, int delta);
+ virtual void didDraw(const Image*);
+
+ virtual bool shouldPauseAnimation(const Image*);
+ virtual void animationAdvanced(const Image*);
+ virtual void changedInRect(const Image*, const IntRect&);
+
+private:
+ void createImage();
+ size_t maximumDecodedImageSize();
+ // 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;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedResource.cpp b/Source/WebCore/loader/cache/CachedResource.cpp
new file mode 100644
index 0000000..a9d9b0a
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResource.cpp
@@ -0,0 +1,596 @@
+/*
+ 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.
+*/
+
+#include "config.h"
+#include "CachedResource.h"
+
+#include "MemoryCache.h"
+#include "CachedMetadata.h"
+#include "CachedResourceClient.h"
+#include "CachedResourceClientWalker.h"
+#include "CachedResourceHandle.h"
+#include "CachedResourceLoader.h"
+#include "CachedResourceRequest.h"
+#include "Frame.h"
+#include "FrameLoaderClient.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "PurgeableBuffer.h"
+#include "ResourceHandle.h"
+#include "SharedBuffer.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
+#include <wtf/RefCountedLeakCounter.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+using namespace WTF;
+
+namespace WebCore {
+
+static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type)
+{
+ switch (type) {
+ case CachedResource::CSSStyleSheet:
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+#endif
+ return ResourceLoadPriorityHigh;
+ case CachedResource::Script:
+ case CachedResource::FontResource:
+ return ResourceLoadPriorityMedium;
+ case CachedResource::ImageResource:
+ return ResourceLoadPriorityLow;
+#if ENABLE(LINK_PREFETCH)
+ case CachedResource::LinkPrefetch:
+ return ResourceLoadPriorityVeryLow;
+#endif
+ }
+ ASSERT_NOT_REACHED();
+ return ResourceLoadPriorityLow;
+}
+
+#ifndef NDEBUG
+static RefCountedLeakCounter cachedResourceLeakCounter("CachedResource");
+#endif
+
+CachedResource::CachedResource(const String& url, Type type)
+ : m_url(url)
+ , m_request(0)
+ , m_loadPriority(defaultPriorityForResourceType(type))
+ , m_responseTimestamp(currentTime())
+ , m_lastDecodedAccessTime(0)
+ , 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_inCache(false)
+ , m_loading(false)
+ , 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_owningCachedResourceLoader(0)
+ , m_resourceToRevalidate(0)
+ , m_proxyResource(0)
+{
+#ifndef NDEBUG
+ cachedResourceLeakCounter.increment();
+#endif
+}
+
+CachedResource::~CachedResource()
+{
+ ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this.
+ ASSERT(canDelete());
+ ASSERT(!inCache());
+ ASSERT(!m_deleted);
+ ASSERT(url().isNull() || cache()->resourceForURL(KURL(ParsedURLString, url())) != this);
+#ifndef NDEBUG
+ m_deleted = true;
+ cachedResourceLeakCounter.decrement();
+#endif
+
+ if (m_owningCachedResourceLoader)
+ m_owningCachedResourceLoader->removeCachedResource(this);
+}
+
+void CachedResource::load(CachedResourceLoader* cachedResourceLoader, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
+{
+ m_sendResourceLoadCallbacks = sendResourceLoadCallbacks;
+ cachedResourceLoader->load(this, incremental, securityCheck, sendResourceLoadCallbacks);
+ m_loading = true;
+}
+
+void CachedResource::data(PassRefPtr<SharedBuffer>, bool allDataReceived)
+{
+ if (!allDataReceived)
+ return;
+
+ setLoading(false);
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient* c = w.next())
+ c->notifyFinished(this);
+}
+
+void CachedResource::finish()
+{
+ m_status = Cached;
+}
+
+bool CachedResource::isExpired() const
+{
+ if (m_response.isNull())
+ return false;
+
+ return currentAge() > freshnessLifetime();
+}
+
+double CachedResource::currentAge() const
+{
+ // RFC2616 13.2.3
+ // No compensation for latency as that is not terribly important in practice
+ double dateValue = m_response.date();
+ double apparentAge = isfinite(dateValue) ? max(0., m_responseTimestamp - dateValue) : 0;
+ double ageValue = m_response.age();
+ double correctedReceivedAge = isfinite(ageValue) ? max(apparentAge, ageValue) : apparentAge;
+ double residentTime = currentTime() - m_responseTimestamp;
+ return correctedReceivedAge + residentTime;
+}
+
+double CachedResource::freshnessLifetime() const
+{
+ // Cache non-http resources liberally
+ if (!m_response.url().protocolInHTTPFamily())
+ return std::numeric_limits<double>::max();
+
+ // RFC2616 13.2.4
+ double maxAgeValue = m_response.cacheControlMaxAge();
+ if (isfinite(maxAgeValue))
+ return maxAgeValue;
+ double expiresValue = m_response.expires();
+ double dateValue = m_response.date();
+ double creationTime = isfinite(dateValue) ? dateValue : m_responseTimestamp;
+ if (isfinite(expiresValue))
+ return expiresValue - creationTime;
+ double lastModifiedValue = m_response.lastModified();
+ if (isfinite(lastModifiedValue))
+ return (creationTime - lastModifiedValue) * 0.1;
+ // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0.
+ return 0;
+}
+
+void CachedResource::setResponse(const ResourceResponse& response)
+{
+ m_response = response;
+ m_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(CachedResourceRequest* request)
+{
+ if (request && !m_request)
+ m_status = Pending;
+ m_request = request;
+ if (canDelete() && !inCache())
+ delete this;
+}
+
+void CachedResource::addClient(CachedResourceClient* client)
+{
+ addClientToSet(client);
+ didAddClient(client);
+}
+
+void CachedResource::didAddClient(CachedResourceClient* c)
+{
+ if (!isLoading())
+ c->notifyFinished(this);
+}
+
+void CachedResource::addClientToSet(CachedResourceClient* client)
+{
+ ASSERT(!isPurgeable());
+
+ if (m_preloadResult == PreloadNotReferenced) {
+ if (isLoaded())
+ m_preloadResult = PreloadReferencedWhileComplete;
+ else if (m_requestedFromNetworkingLayer)
+ m_preloadResult = PreloadReferencedWhileLoading;
+ else
+ m_preloadResult = PreloadReferenced;
+ }
+ if (!hasClients() && inCache())
+ cache()->addToLiveResourcesSize(this);
+ m_clients.add(client);
+}
+
+void CachedResource::removeClient(CachedResourceClient* client)
+{
+ ASSERT(m_clients.contains(client));
+ m_clients.remove(client);
+
+ if (canDelete() && !inCache())
+ delete this;
+ else if (!hasClients() && inCache()) {
+ cache()->removeFromLiveResourcesSize(this);
+ cache()->removeFromLiveDecodedResourcesList(this);
+ allClientsRemoved();
+ if (response().cacheControlContainsNoStore()) {
+ // RFC2616 14.9.2:
+ // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
+ // "... History buffers MAY store such responses as part of their normal operation."
+ // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
+ if (protocolIs(url(), "https"))
+ cache()->remove(this);
+ } else
+ cache()->prune();
+ }
+ // This object may be dead here.
+}
+
+void CachedResource::deleteIfPossible()
+{
+ if (canDelete() && !inCache())
+ delete this;
+}
+
+void CachedResource::setDecodedSize(unsigned size)
+{
+ if (size == m_decodedSize)
+ return;
+
+ int delta = size - m_decodedSize;
+
+ // The object must now be moved to a different queue, since its size has been changed.
+ // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous
+ // queue.
+ if (inCache())
+ cache()->removeFromLRUList(this);
+
+ m_decodedSize = size;
+
+ if (inCache()) {
+ // Now insert into the new LRU list.
+ cache()->insertInLRUList(this);
+
+ // Insert into or remove from the live decoded list if necessary.
+ // When inserting into the LiveDecodedResourcesList it is possible
+ // that the m_lastDecodedAccessTime is still zero or smaller than
+ // the m_lastDecodedAccessTime of the current list head. This is a
+ // violation of the invariant that the list is to be kept sorted
+ // by access time. The weakening of the invariant does not pose
+ // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209
+ if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients())
+ cache()->insertInLiveDecodedResourcesList(this);
+ else if (!m_decodedSize && m_inLiveDecodedResourcesList)
+ cache()->removeFromLiveDecodedResourcesList(this);
+
+ // Update the cache's size totals.
+ cache()->adjustSize(hasClients(), delta);
+ }
+}
+
+void CachedResource::setEncodedSize(unsigned size)
+{
+ if (size == m_encodedSize)
+ return;
+
+ // The size cannot ever shrink (unless it is being nulled out because of an error). If it ever does, assert.
+ ASSERT(size == 0 || size >= m_encodedSize);
+
+ int delta = size - m_encodedSize;
+
+ // The object must now be moved to a different queue, since its size has been changed.
+ // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous
+ // queue.
+ if (inCache())
+ cache()->removeFromLRUList(this);
+
+ m_encodedSize = size;
+
+ if (inCache()) {
+ // Now insert into the new LRU list.
+ cache()->insertInLRUList(this);
+
+ // Update the cache's size totals.
+ cache()->adjustSize(hasClients(), delta);
+ }
+}
+
+void CachedResource::didAccessDecodedData(double timeStamp)
+{
+ m_lastDecodedAccessTime = timeStamp;
+
+ if (inCache()) {
+ if (m_inLiveDecodedResourcesList) {
+ cache()->removeFromLiveDecodedResourcesList(this);
+ cache()->insertInLiveDecodedResourcesList(this);
+ }
+ cache()->prune();
+ }
+}
+
+void CachedResource::setResourceToRevalidate(CachedResource* resource)
+{
+ ASSERT(resource);
+ ASSERT(!m_resourceToRevalidate);
+ ASSERT(resource != this);
+ 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.
+ ASSERT(!resource->m_proxyResource);
+
+ resource->m_proxyResource = this;
+ m_resourceToRevalidate = resource;
+}
+
+void CachedResource::clearResourceToRevalidate()
+{
+ ASSERT(m_resourceToRevalidate);
+ // A resource may start revalidation before this method has been called, so check that this resource is still the proxy resource before clearing it out.
+ if (m_resourceToRevalidate->m_proxyResource == this) {
+ m_resourceToRevalidate->m_proxyResource = 0;
+ m_resourceToRevalidate->deleteIfPossible();
+ }
+ m_handlesToRevalidate.clear();
+ m_resourceToRevalidate = 0;
+ deleteIfPossible();
+}
+
+void CachedResource::switchClientsToRevalidatedResource()
+{
+ ASSERT(m_resourceToRevalidate);
+ 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;
+ handle->m_resource = m_resourceToRevalidate;
+ m_resourceToRevalidate->registerHandle(handle);
+ --m_handleCount;
+ }
+ ASSERT(!m_handleCount);
+ m_handlesToRevalidate.clear();
+
+ Vector<CachedResourceClient*> clientsToMove;
+ HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end();
+ for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) {
+ CachedResourceClient* client = it->first;
+ unsigned count = it->second;
+ while (count) {
+ clientsToMove.append(client);
+ --count;
+ }
+ }
+ // Equivalent of calling removeClient() for all clients
+ m_clients.clear();
+
+ unsigned moveCount = clientsToMove.size();
+ for (unsigned n = 0; n < moveCount; ++n)
+ m_resourceToRevalidate->addClientToSet(clientsToMove[n]);
+ for (unsigned n = 0; n < moveCount; ++n) {
+ // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore.
+ if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n]))
+ m_resourceToRevalidate->didAddClient(clientsToMove[n]);
+ }
+}
+
+void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse)
+{
+ m_responseTimestamp = currentTime();
+
+ DEFINE_STATIC_LOCAL(const AtomicString, contentHeaderPrefix, ("content-"));
+ // RFC2616 10.3.5
+ // Update cached headers from the 304 response
+ const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields();
+ HTTPHeaderMap::const_iterator end = newHeaders.end();
+ for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) {
+ // Don't allow 304 response to update content headers, these can't change but some servers send wrong values.
+ if (it->first.startsWith(contentHeaderPrefix, false))
+ continue;
+ m_response.setHTTPHeaderField(it->first, it->second);
+ }
+}
+
+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 || errorOccurred())
+ return false;
+
+ if (m_response.cacheControlContainsNoStore())
+ return false;
+
+ DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader, ("last-modified"));
+ DEFINE_STATIC_LOCAL(const AtomicString, eTagHeader, ("etag"));
+ return !m_response.httpHeaderField(lastModifiedHeader).isEmpty() || !m_response.httpHeaderField(eTagHeader).isEmpty();
+}
+
+bool CachedResource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const
+{
+ ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify);
+
+ if (cachePolicy == CachePolicyRevalidate)
+ return true;
+
+ 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;
+ }
+
+ // CachePolicyVerify
+ if (isExpired()) {
+ LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this);
+ return true;
+ }
+
+ return false;
+}
+
+bool CachedResource::isSafeToMakePurgeable() const
+{
+ return !hasClients() && !m_proxyResource && !m_resourceToRevalidate;
+}
+
+bool CachedResource::makePurgeable(bool purgeable)
+{
+ if (purgeable) {
+ ASSERT(isSafeToMakePurgeable());
+
+ if (m_purgeableData) {
+ ASSERT(!m_data);
+ return true;
+ }
+ if (!m_data)
+ return false;
+
+ // Should not make buffer purgeable if it has refs other than this since we don't want two copies.
+ if (!m_data->hasOneRef())
+ return false;
+
+ if (m_data->hasPurgeableBuffer()) {
+ m_purgeableData = m_data->releasePurgeableBuffer();
+ } else {
+ m_purgeableData = PurgeableBuffer::create(m_data->data(), m_data->size());
+ if (!m_purgeableData)
+ return false;
+ m_purgeableData->setPurgePriority(purgePriority());
+ }
+
+ m_purgeableData->makePurgeable(true);
+ m_data.clear();
+ return true;
+ }
+
+ if (!m_purgeableData)
+ return true;
+ ASSERT(!m_data);
+ ASSERT(!hasClients());
+
+ if (!m_purgeableData->makePurgeable(false))
+ return false;
+
+ m_data = SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release());
+ return true;
+}
+
+bool CachedResource::isPurgeable() const
+{
+ return m_purgeableData && m_purgeableData->isPurgeable();
+}
+
+bool CachedResource::wasPurged() const
+{
+ return m_purgeableData && m_purgeableData->wasPurged();
+}
+
+unsigned CachedResource::overheadSize() const
+{
+ return sizeof(CachedResource) + m_response.memoryUsage() + 576;
+ /*
+ 576 = 192 + // average size of m_url
+ 384; // average size of m_clients hash map
+ */
+}
+
+void CachedResource::setLoadPriority(ResourceLoadPriority loadPriority)
+{
+ if (loadPriority == ResourceLoadPriorityUnresolved)
+ return;
+ m_loadPriority = loadPriority;
+}
+
+}
diff --git a/Source/WebCore/loader/cache/CachedResource.h b/Source/WebCore/loader/cache/CachedResource.h
new file mode 100644
index 0000000..3600a02
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResource.h
@@ -0,0 +1,296 @@
+/*
+ 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 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.
+*/
+
+#ifndef CachedResource_h
+#define CachedResource_h
+
+#include "CachePolicy.h"
+#include "FrameLoaderTypes.h"
+#include "PlatformString.h"
+#include "PurgePriority.h"
+#include "ResourceLoadPriority.h"
+#include "ResourceResponse.h"
+#include <wtf/HashCountedSet.h>
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Vector.h>
+#include <time.h>
+
+namespace WebCore {
+
+class MemoryCache;
+class CachedMetadata;
+class CachedResourceClient;
+class CachedResourceHandleBase;
+class CachedResourceLoader;
+class CachedResourceRequest;
+class Frame;
+class InspectorResource;
+class PurgeableBuffer;
+
+// A resource that is held in the cache. Classes who want to use this object should derive
+// from CachedResourceClient, to get the function calls in case the requested data has arrived.
+// This class also does the actual communication with the loader to obtain the resource from the network.
+class CachedResource : public Noncopyable {
+ friend class MemoryCache;
+ friend class InspectorResource;
+
+public:
+ enum Type {
+ ImageResource,
+ CSSStyleSheet,
+ Script,
+ FontResource
+#if ENABLE(XSLT)
+ , XSLStyleSheet
+#endif
+#if ENABLE(LINK_PREFETCH)
+ , LinkPrefetch
+#endif
+ };
+
+ enum Status {
+ Unknown, // let cache decide what to do with it
+ Pending, // only partially loaded
+ Cached, // regular case
+ LoadError,
+ DecodeError
+ };
+
+ CachedResource(const String& url, Type);
+ virtual ~CachedResource();
+
+ 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);
+ virtual void error(CachedResource::Status) { }
+
+ virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return false; }
+
+ const String &url() const { return m_url; }
+ Type type() const { return static_cast<Type>(m_type); }
+
+ ResourceLoadPriority loadPriority() const { return m_loadPriority; }
+ void setLoadPriority(ResourceLoadPriority);
+
+ void addClient(CachedResourceClient*);
+ void removeClient(CachedResourceClient*);
+ bool hasClients() const { return !m_clients.isEmpty(); }
+ void deleteIfPossible();
+
+ enum PreloadResult {
+ PreloadNotReferenced,
+ PreloadReferenced,
+ PreloadReferencedWhileLoading,
+ PreloadReferencedWhileComplete
+ };
+ PreloadResult preloadResult() const { return static_cast<PreloadResult>(m_preloadResult); }
+ void setRequestedFromNetworkingLayer() { m_requestedFromNetworkingLayer = true; }
+
+ virtual void didAddClient(CachedResourceClient*);
+ virtual void allClientsRemoved() { }
+
+ unsigned count() const { return m_clients.size(); }
+
+ 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; } // 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++; }
+
+ // Computes the status of an object after loading.
+ // Updates the expire date on the cache entry file
+ void finish();
+
+ // Called by the cache if the object has been removed from the cache
+ // while still being referenced. This means the object should delete itself
+ // if the number of clients observing it ever drops to 0.
+ // The resource can be brought back to cache after successful revalidation.
+ void setInCache(bool inCache) { m_inCache = inCache; }
+ bool inCache() const { return m_inCache; }
+
+ void setInLiveDecodedResourcesList(bool b) { m_inLiveDecodedResourcesList = b; }
+ bool inLiveDecodedResourcesList() { return m_inLiveDecodedResourcesList; }
+
+ void setRequest(CachedResourceRequest*);
+
+ SharedBuffer* data() const { ASSERT(!m_purgeableData); return m_data.get(); }
+
+ 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;
+
+ // 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 (status() == LoadError || status() == DecodeError); }
+
+ bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; }
+
+ virtual void destroyDecodedData() { }
+
+ void setOwningCachedResourceLoader(CachedResourceLoader* cachedResourceLoader) { m_owningCachedResourceLoader = cachedResourceLoader; }
+
+ bool isPreloaded() const { return m_preloadCount; }
+ void increasePreloadCount() { ++m_preloadCount; }
+ void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; }
+
+ void registerHandle(CachedResourceHandleBase* h);
+ void unregisterHandle(CachedResourceHandleBase* h);
+
+ bool canUseCacheValidator() const;
+ bool mustRevalidateDueToCacheHeaders(CachePolicy) const;
+ bool isCacheValidator() const { return m_resourceToRevalidate; }
+ CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; }
+
+ bool isPurgeable() const;
+ bool wasPurged() const;
+
+ // This is used by the archive machinery to get at a purged resource without
+ // triggering a load. We should make it protected again if we can find a
+ // better way to handle the archive case.
+ bool makePurgeable(bool purgeable);
+
+ // HTTP revalidation support methods for CachedResourceLoader.
+ void setResourceToRevalidate(CachedResource*);
+ void switchClientsToRevalidatedResource();
+ void clearResourceToRevalidate();
+ void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse);
+
+protected:
+ void setEncodedSize(unsigned);
+ void setDecodedSize(unsigned);
+ void didAccessDecodedData(double timeStamp);
+
+ bool isSafeToMakePurgeable() const;
+
+ HashCountedSet<CachedResourceClient*> m_clients;
+
+ String m_url;
+ String m_accept;
+ CachedResourceRequest* m_request;
+ ResourceLoadPriority m_loadPriority;
+
+ ResourceResponse m_response;
+ double m_responseTimestamp;
+
+ RefPtr<SharedBuffer> m_data;
+ OwnPtr<PurgeableBuffer> m_purgeableData;
+
+private:
+ void addClientToSet(CachedResourceClient*);
+
+ 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_handleCount;
+ unsigned m_preloadCount;
+
+ unsigned m_preloadResult : 2; // PreloadResult
+
+ bool m_inLiveDecodedResourcesList : 1;
+ bool m_requestedFromNetworkingLayer : 1;
+ bool m_sendResourceLoadCallbacks : 1;
+
+ bool m_inCache : 1;
+ bool m_loading : 1;
+
+ unsigned m_type : 3; // Type
+ unsigned m_status : 3; // Status
+
+#ifndef NDEBUG
+ bool m_deleted;
+ unsigned m_lruIndex;
+#endif
+
+ CachedResource* m_nextInAllResourcesList;
+ CachedResource* m_prevInAllResourcesList;
+
+ CachedResource* m_nextInLiveResourcesList;
+ CachedResource* m_prevInLiveResourcesList;
+
+ CachedResourceLoader* m_owningCachedResourceLoader; // only non-0 for resources that are not in the cache
+
+ // If this field is non-null we are using the resource as a proxy for checking whether an existing resource is still up to date
+ // using HTTP If-Modified-Since/If-None-Match headers. If the response is 304 all clients of this resource are moved
+ // to to be clients of m_resourceToRevalidate and the resource is deleted. If not, the field is zeroed and this
+ // resources becomes normal resource load.
+ CachedResource* m_resourceToRevalidate;
+
+ // If this field is non-null, the resource has a proxy for checking whether it is still up to date (see m_resourceToRevalidate).
+ CachedResource* m_proxyResource;
+
+ // These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response.
+ HashSet<CachedResourceHandleBase*> m_handlesToRevalidate;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedResourceClient.h b/Source/WebCore/loader/cache/CachedResourceClient.h
new file mode 100644
index 0000000..275d331
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceClient.h
@@ -0,0 +1,71 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ 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 CachedResourceClient_h
+#define CachedResourceClient_h
+
+#include <wtf/FastAllocBase.h>
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+ class CachedCSSStyleSheet;
+ class CachedFont;
+ class CachedResource;
+ class CachedImage;
+ class Image;
+ class IntRect;
+ class KURL;
+
+ /**
+ * @internal
+ *
+ * a client who wants to load stylesheets, images or scripts from the web has to
+ * inherit from this class and overload one of the 3 functions
+ *
+ */
+ class CachedResourceClient : public FastAllocBase
+ {
+ public:
+ virtual ~CachedResourceClient() { }
+
+ // Called whenever a frame of an image changes, either because we got more data from the network or
+ // because we are animating. If not null, the IntRect is the changed rect of the image.
+ virtual void imageChanged(CachedImage*, const IntRect* = 0) { };
+
+ // Called to find out if this client wants to actually display the image. Used to tell when we
+ // can halt animation. Content nodes that hold image refs for example would not render the image,
+ // but RenderImages would (assuming they have visibility: visible and their render tree isn't hidden
+ // e.g., in the b/f cache or in a background tab).
+ virtual bool willRenderImage(CachedImage*) { return false; }
+
+ 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*) {};
+ virtual void notifyFinished(CachedResource*) { }
+ };
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedResourceClientWalker.cpp b/Source/WebCore/loader/cache/CachedResourceClientWalker.cpp
new file mode 100644
index 0000000..142a2a1
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceClientWalker.cpp
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ 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"
+#include "CachedResourceClientWalker.h"
+
+namespace WebCore {
+
+CachedResourceClientWalker::CachedResourceClientWalker(const HashCountedSet<CachedResourceClient*>& set)
+ : m_clientSet(set), m_clientVector(set.size()), m_index(0)
+{
+ typedef HashCountedSet<CachedResourceClient*>::const_iterator Iterator;
+ Iterator end = set.end();
+ size_t clientIndex = 0;
+ for (Iterator current = set.begin(); current != end; ++current)
+ m_clientVector[clientIndex++] = current->first;
+}
+
+CachedResourceClient* CachedResourceClientWalker::next()
+{
+ size_t size = m_clientVector.size();
+ while (m_index < size) {
+ CachedResourceClient* next = m_clientVector[m_index++];
+ if (m_clientSet.contains(next))
+ return next;
+ }
+
+ return 0;
+}
+
+}
diff --git a/Source/WebCore/loader/cache/CachedResourceClientWalker.h b/Source/WebCore/loader/cache/CachedResourceClientWalker.h
new file mode 100644
index 0000000..d079584
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceClientWalker.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+
+ 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 CachedResourceClientWalker_h
+#define CachedResourceClientWalker_h
+
+#include <wtf/HashCountedSet.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+ class CachedResourceClient;
+
+ // Call this "walker" instead of iterator so people won't expect Qt or STL-style iterator interface.
+ // Just keep calling next() on this. It's safe from deletions of items.
+ class CachedResourceClientWalker {
+ public:
+ CachedResourceClientWalker(const HashCountedSet<CachedResourceClient*>&);
+ CachedResourceClient* next();
+ private:
+ const HashCountedSet<CachedResourceClient*>& m_clientSet;
+ Vector<CachedResourceClient*> m_clientVector;
+ size_t m_index;
+ };
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedResourceHandle.cpp b/Source/WebCore/loader/cache/CachedResourceHandle.cpp
new file mode 100644
index 0000000..871292c
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceHandle.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "CachedResourceHandle.h"
+
+namespace WebCore {
+
+void CachedResourceHandleBase::setResource(CachedResource* resource)
+{
+ if (resource == m_resource)
+ return;
+ if (m_resource)
+ m_resource->unregisterHandle(this);
+ m_resource = resource;
+ if (m_resource)
+ m_resource->registerHandle(this);
+}
+
+}
diff --git a/Source/WebCore/loader/cache/CachedResourceHandle.h b/Source/WebCore/loader/cache/CachedResourceHandle.h
new file mode 100644
index 0000000..7d485bf
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceHandle.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CachedResourceHandle_h
+#define CachedResourceHandle_h
+
+#include "CachedResource.h"
+
+namespace WebCore {
+
+ class CachedResourceHandleBase {
+ public:
+ ~CachedResourceHandleBase() { if (m_resource) m_resource->unregisterHandle(this); }
+ CachedResource* get() const { return m_resource; }
+
+ bool operator!() const { return !m_resource; }
+
+ // This conversion operator allows implicit conversion to bool but not to other integer types.
+ // Parenthesis is needed for winscw compiler to resolve class qualifier in this case.
+ typedef CachedResource* (CachedResourceHandleBase::*UnspecifiedBoolType);
+ operator UnspecifiedBoolType() const { return m_resource ? &CachedResourceHandleBase::m_resource : 0; }
+
+ protected:
+ CachedResourceHandleBase() : m_resource(0) {}
+ CachedResourceHandleBase(CachedResource* res) { m_resource = res; if (m_resource) m_resource->registerHandle(this); }
+ CachedResourceHandleBase(const CachedResourceHandleBase& o) : m_resource(o.m_resource) { if (m_resource) m_resource->registerHandle(this); }
+
+ void setResource(CachedResource*);
+
+ private:
+ CachedResourceHandleBase& operator=(const CachedResourceHandleBase&) { return *this; }
+
+ friend class CachedResource;
+
+ CachedResource* m_resource;
+ };
+
+ template <class R> class CachedResourceHandle : public CachedResourceHandleBase {
+ public:
+ CachedResourceHandle() { }
+ CachedResourceHandle(R* res);
+ CachedResourceHandle(const CachedResourceHandle<R>& o) : CachedResourceHandleBase(o) { }
+
+ R* get() const { return reinterpret_cast<R*>(CachedResourceHandleBase::get()); }
+ R* operator->() const { return get(); }
+
+ CachedResourceHandle& operator=(R* res) { setResource(res); return *this; }
+ CachedResourceHandle& operator=(const CachedResourceHandle& o) { setResource(o.get()); return *this; }
+ bool operator==(const CachedResourceHandleBase& o) const { return get() == o.get(); }
+ bool operator!=(const CachedResourceHandleBase& o) const { return get() != o.get(); }
+ };
+
+ // Don't inline for winscw compiler to prevent the compiler aggressively resolving
+ // the base class of R* when CachedResourceHandler<T>(R*) is inlined. The bug is
+ // reported at: https://xdabug001.ext.nokia.com/bugzilla/show_bug.cgi?id=9812.
+ template <class R>
+#if !COMPILER(WINSCW)
+ inline
+#endif
+ CachedResourceHandle<R>::CachedResourceHandle(R* res) : CachedResourceHandleBase(res)
+ {
+ }
+
+ template <class R, class RR> bool operator==(const CachedResourceHandle<R>& h, const RR* res)
+ {
+ return h.get() == res;
+ }
+ template <class R, class RR> bool operator==(const RR* res, const CachedResourceHandle<R>& h)
+ {
+ return h.get() == res;
+ }
+ template <class R, class RR> bool operator!=(const CachedResourceHandle<R>& h, const RR* res)
+ {
+ return h.get() != res;
+ }
+ template <class R, class RR> bool operator!=(const RR* res, const CachedResourceHandle<R>& h)
+ {
+ return h.get() != res;
+ }
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.cpp b/Source/WebCore/loader/cache/CachedResourceLoader.cpp
new file mode 100644
index 0000000..3fcace6
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceLoader.cpp
@@ -0,0 +1,733 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
+ Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ 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"
+#include "CachedResourceLoader.h"
+
+#include "CachedCSSStyleSheet.h"
+#include "CachedFont.h"
+#include "CachedImage.h"
+#include "CachedResourceRequest.h"
+#include "CachedScript.h"
+#include "CachedXSLStyleSheet.h"
+#include "Console.h"
+#include "DOMWindow.h"
+#include "Document.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "HTMLElement.h"
+#include "Logging.h"
+#include "MemoryCache.h"
+#include "PingLoader.h"
+#include "SecurityOrigin.h"
+#include "Settings.h"
+#include <wtf/text/StringConcatenate.h>
+
+#define PRELOAD_DEBUG 0
+
+namespace WebCore {
+
+static CachedResource* createResource(CachedResource::Type type, const KURL& url, const String& charset)
+{
+ switch (type) {
+ case CachedResource::ImageResource:
+ return new CachedImage(url.string());
+ case CachedResource::CSSStyleSheet:
+ return new CachedCSSStyleSheet(url.string(), charset);
+ case CachedResource::Script:
+ return new CachedScript(url.string(), charset);
+ case CachedResource::FontResource:
+ return new CachedFont(url.string());
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+ return new CachedXSLStyleSheet(url.string());
+#endif
+#if ENABLE(LINK_PREFETCH)
+ case CachedResource::LinkPrefetch:
+ return new CachedResource(url.string(), CachedResource::LinkPrefetch);
+#endif
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+CachedResourceLoader::CachedResourceLoader(Document* document)
+ : m_cache(cache())
+ , m_document(document)
+ , m_requestCount(0)
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ , m_blockNetworkImage(false)
+#endif
+ , m_autoLoadImages(true)
+ , m_loadFinishing(false)
+ , m_allowStaleResources(false)
+{
+ m_cache->addCachedResourceLoader(this);
+}
+
+CachedResourceLoader::~CachedResourceLoader()
+{
+ cancelRequests();
+ clearPreloads();
+ DocumentResourceMap::iterator end = m_documentResources.end();
+ for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
+ it->second->setOwningCachedResourceLoader(0);
+ m_cache->removeCachedResourceLoader(this);
+
+ // Make sure no requests still point to this CachedResourceLoader
+ ASSERT(m_requestCount == 0);
+}
+
+CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
+{
+ KURL url = m_document->completeURL(resourceURL);
+ return cachedResource(url);
+}
+
+CachedResource* CachedResourceLoader::cachedResource(const KURL& resourceURL) const
+{
+ KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
+ return m_documentResources.get(url).get();
+}
+
+Frame* CachedResourceLoader::frame() const
+{
+ return m_document->frame();
+}
+
+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()) {
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ if (shouldBlockNetworkImage(url)) {
+ return resource;
+ }
+#endif
+ resource->setLoading(true);
+ load(resource, true);
+ }
+ return resource;
+}
+
+CachedFont* CachedResourceLoader::requestFont(const String& url)
+{
+ return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String()));
+}
+
+CachedCSSStyleSheet* CachedResourceLoader::requestCSSStyleSheet(const String& url, const String& charset, ResourceLoadPriority priority)
+{
+ return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset, priority));
+}
+
+CachedCSSStyleSheet* CachedResourceLoader::requestUserCSSStyleSheet(const String& requestURL, const String& charset)
+{
+ KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(KURL(KURL(), requestURL));
+
+ if (CachedResource* existing = cache()->resourceForURL(url)) {
+ if (existing->type() == CachedResource::CSSStyleSheet)
+ return static_cast<CachedCSSStyleSheet*>(existing);
+ cache()->remove(existing);
+ }
+ CachedCSSStyleSheet* userSheet = new CachedCSSStyleSheet(url, charset);
+
+ bool inCache = cache()->add(userSheet);
+ if (!inCache)
+ userSheet->setInCache(true);
+
+ userSheet->load(this, /*incremental*/ false, SkipSecurityCheck, /*sendResourceLoadCallbacks*/ false);
+
+ if (!inCache)
+ userSheet->setInCache(false);
+
+ return userSheet;
+}
+
+CachedScript* CachedResourceLoader::requestScript(const String& url, const String& charset)
+{
+ return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, charset));
+}
+
+#if ENABLE(XSLT)
+CachedXSLStyleSheet* CachedResourceLoader::requestXSLStyleSheet(const String& url)
+{
+ return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url, String()));
+}
+#endif
+
+#if ENABLE(LINK_PREFETCH)
+CachedResource* CachedResourceLoader::requestLinkPrefetch(const String& url)
+{
+ ASSERT(frame());
+ return requestResource(CachedResource::LinkPrefetch, url, String());
+}
+#endif
+
+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
+ // any URL.
+ switch (type) {
+ case CachedResource::ImageResource:
+ 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:
+ if (!m_document->securityOrigin()->canRequest(url)) {
+ printAccessDeniedMessage(url);
+ return false;
+ }
+ break;
+#endif
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ // Given that the load is allowed by the same-origin policy, we should
+ // check whether the load passes the mixed-content policy.
+ //
+ // Note: Currently, we always allow mixed content, but we generate a
+ // callback to the FrameLoaderClient in case the embedder wants to
+ // update any security indicators.
+ //
+ switch (type) {
+ case CachedResource::Script:
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+#endif
+ // These resource can inject script into the current document.
+ if (Frame* f = frame())
+ f->loader()->checkIfRunInsecureContent(m_document->securityOrigin(), url);
+ break;
+ case CachedResource::ImageResource:
+ case CachedResource::CSSStyleSheet:
+ case CachedResource::FontResource: {
+ // These resources can corrupt only the frame's pixels.
+ if (Frame* f = frame()) {
+ Frame* top = f->tree()->top();
+ top->loader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), url);
+ }
+ break;
+ }
+#if ENABLE(LINK_PREFETCH)
+ case CachedResource::LinkPrefetch:
+ // Prefetch cannot affect the current document.
+ break;
+#endif
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ // FIXME: Consider letting the embedder block mixed content loads.
+ return true;
+}
+
+CachedResource* CachedResourceLoader::requestResource(CachedResource::Type type, const String& resourceURL, const String& charset, ResourceLoadPriority priority, bool forPreload)
+{
+ KURL url = m_document->completeURL(resourceURL);
+
+ LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.string().latin1().data(), charset.latin1().data(), priority, forPreload);
+
+ // If only the fragment identifiers differ, it is the same resource.
+ url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
+
+ if (!url.isValid())
+ return 0;
+
+ if (!canRequest(type, url))
+ return 0;
+
+ // FIXME: Figure out what is the correct way to merge this security check with the one above.
+ if (!document()->securityOrigin()->canDisplay(url)) {
+ if (!forPreload)
+ FrameLoader::reportLocalLoadFailed(document()->frame(), url.string());
+ LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
+ return 0;
+ }
+
+ if (cache()->disabled()) {
+ DocumentResourceMap::iterator it = m_documentResources.find(url.string());
+ if (it != m_documentResources.end()) {
+ it->second->setOwningCachedResourceLoader(0);
+ m_documentResources.remove(it);
+ }
+ }
+
+ // See if we can use an existing resource from the cache.
+ CachedResource* resource = cache()->resourceForURL(url);
+
+ switch (determineRevalidationPolicy(type, forPreload, resource)) {
+ case Load:
+ resource = loadResource(type, url, charset, priority);
+ break;
+ case Reload:
+ cache()->remove(resource);
+ resource = loadResource(type, url, charset, priority);
+ break;
+ case Revalidate:
+ resource = revalidateResource(resource, priority);
+ break;
+ case Use:
+ cache()->resourceAccessed(resource);
+ notifyLoadedFromMemoryCache(resource);
+ break;
+ }
+
+ if (!resource)
+ return 0;
+
+ ASSERT(resource->url() == url.string());
+ m_documentResources.set(resource->url(), resource);
+
+ return resource;
+}
+
+CachedResource* CachedResourceLoader::revalidateResource(CachedResource* resource, ResourceLoadPriority priority)
+{
+ ASSERT(resource);
+ ASSERT(resource->inCache());
+ ASSERT(!cache()->disabled());
+ ASSERT(resource->canUseCacheValidator());
+ ASSERT(!resource->resourceToRevalidate());
+
+ 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);
+
+ cache()->remove(resource);
+ cache()->add(newResource);
+
+ newResource->setLoadPriority(priority);
+ newResource->load(this);
+
+ m_validatedURLs.add(url);
+ return newResource;
+}
+
+CachedResource* CachedResourceLoader::loadResource(CachedResource::Type type, const KURL& url, const String& charset, ResourceLoadPriority priority)
+{
+ ASSERT(!cache()->resourceForURL(url));
+
+ LOG(ResourceLoading, "Loading CachedResource for '%s'.", url.string().latin1().data());
+
+ CachedResource* resource = createResource(type, url, charset);
+
+ bool inCache = cache()->add(resource);
+
+ // Pretend the resource is in the cache, to prevent it from being deleted during the load() call.
+ // FIXME: CachedResource should just use normal refcounting instead.
+ if (!inCache)
+ resource->setInCache(true);
+
+ resource->setLoadPriority(priority);
+ resource->load(this);
+
+ if (!inCache) {
+ resource->setOwningCachedResourceLoader(this);
+ resource->setInCache(false);
+ }
+
+ // We don't support immediate loads, but we do support immediate failure.
+ if (resource->errorOccurred()) {
+ if (inCache)
+ cache()->remove(resource);
+ else
+ delete resource;
+ return 0;
+ }
+
+ m_validatedURLs.add(url.string());
+ return resource;
+}
+
+CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, bool forPreload, CachedResource* existingResource) const
+{
+ if (!existingResource)
+ return Load;
+
+ // We already have a preload going for this URL.
+ if (forPreload && existingResource->isPreloaded())
+ return Use;
+
+ // If the same URL has been loaded as a different type, we need to reload.
+ if (existingResource->type() != type) {
+ LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
+ return Reload;
+ }
+
+ // Don't reload resources while pasting.
+ if (m_allowStaleResources)
+ return Use;
+
+ // Alwaus use preloads.
+ if (existingResource->isPreloaded())
+ return Use;
+
+ // Avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
+ if (m_validatedURLs.contains(existingResource->url()))
+ return Use;
+
+ // CachePolicyReload always reloads
+ if (cachePolicy() == CachePolicyReload) {
+ LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
+ return Reload;
+ }
+
+ // CachePolicyHistoryBuffer uses the cache no matter what.
+ if (cachePolicy() == CachePolicyHistoryBuffer)
+ return Use;
+
+ // We'll try to reload the resource if it failed last time.
+ if (existingResource->errorOccurred()) {
+ LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
+ return Reload;
+ }
+
+ // For resources that are not yet loaded we ignore the cache policy.
+ if (existingResource->isLoading())
+ return Use;
+
+ // Check if the cache headers requires us to revalidate (cache expiration for example).
+ if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy())) {
+ // See if the resource has usable ETag or Last-modified headers.
+ if (existingResource->canUseCacheValidator())
+ return Revalidate;
+
+ // No, must reload.
+ LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
+ return Reload;
+ }
+
+ return Use;
+}
+
+void CachedResourceLoader::printAccessDeniedMessage(const KURL& url) const
+{
+ if (url.isNull())
+ return;
+
+ if (!frame())
+ return;
+
+ Settings* settings = frame()->settings();
+ if (!settings || settings->privateBrowsingEnabled())
+ return;
+
+ 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 CachedResourceLoader::setAutoLoadImages(bool enable)
+{
+ if (enable == m_autoLoadImages)
+ return;
+
+ m_autoLoadImages = enable;
+
+ if (!m_autoLoadImages)
+ return;
+
+ DocumentResourceMap::iterator end = m_documentResources.end();
+ for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
+ CachedResource* resource = it->second.get();
+ if (resource->type() == CachedResource::ImageResource) {
+ CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ if (shouldBlockNetworkImage(image->url()))
+ continue;
+#endif
+
+ if (image->stillNeedsLoad())
+ load(image, true);
+ }
+ }
+}
+
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+bool CachedResourceLoader::shouldBlockNetworkImage(const String& url) const
+{
+ if (!m_blockNetworkImage)
+ return false;
+
+ KURL kurl = m_document->completeURL(url);
+ if (kurl.protocolIs("http") || kurl.protocolIs("https"))
+ return true;
+
+ return false;
+}
+
+void CachedResourceLoader::setBlockNetworkImage(bool block)
+{
+ if (block == m_blockNetworkImage)
+ return;
+
+ m_blockNetworkImage = block;
+
+ if (!m_autoLoadImages || m_blockNetworkImage)
+ return;
+
+ DocumentResourceMap::iterator end = m_documentResources.end();
+ for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
+ CachedResource* resource = it->second.get();
+ if (resource->type() == CachedResource::ImageResource) {
+ CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
+ if (image->stillNeedsLoad())
+ load(image, true);
+ }
+ }
+}
+#endif
+
+CachePolicy CachedResourceLoader::cachePolicy() const
+{
+ return frame() ? frame()->loader()->subresourceCachePolicy() : CachePolicyVerify;
+}
+
+void CachedResourceLoader::removeCachedResource(CachedResource* resource) const
+{
+#ifndef NDEBUG
+ DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
+ if (it != m_documentResources.end())
+ ASSERT(it->second.get() == resource);
+#endif
+ m_documentResources.remove(resource->url());
+}
+
+void CachedResourceLoader::load(CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
+{
+ incrementRequestCount(resource);
+
+ RefPtr<CachedResourceRequest> request = CachedResourceRequest::load(this, resource, incremental, securityCheck, sendResourceLoadCallbacks);
+ if (request)
+ m_requests.add(request);
+}
+
+void CachedResourceLoader::loadDone(CachedResourceRequest* request)
+{
+ m_loadFinishing = false;
+ RefPtr<CachedResourceRequest> protect(request);
+ if (request)
+ m_requests.remove(request);
+ if (frame())
+ frame()->loader()->loadDone();
+ checkForPendingPreloads();
+}
+
+void CachedResourceLoader::cancelRequests()
+{
+ clearPendingPreloads();
+ Vector<CachedResourceRequest*, 256> requestsToCancel;
+ RequestSet::iterator end = m_requests.end();
+ for (RequestSet::iterator i = m_requests.begin(); i != end; ++i)
+ requestsToCancel.append((*i).get());
+
+ for (unsigned i = 0; i < requestsToCancel.size(); ++i)
+ requestsToCancel[i]->didFail(true);
+}
+
+void CachedResourceLoader::notifyLoadedFromMemoryCache(CachedResource* resource)
+{
+ if (!resource || !frame() || resource->status() != CachedResource::Cached)
+ 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 CachedResourceLoader::incrementRequestCount(const CachedResource* res)
+{
+ if (res->isPrefetch())
+ return;
+
+ ++m_requestCount;
+}
+
+void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
+{
+ if (res->isPrefetch())
+ return;
+
+ --m_requestCount;
+ ASSERT(m_requestCount > -1);
+}
+
+int CachedResourceLoader::requestCount()
+{
+ if (m_loadFinishing)
+ return m_requestCount + 1;
+ return m_requestCount;
+}
+
+void CachedResourceLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody)
+{
+ 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.
+ PendingPreload pendingPreload = { type, url, charset };
+ m_pendingPreloads.append(pendingPreload);
+ return;
+ }
+ requestPreload(type, url, charset);
+}
+
+void CachedResourceLoader::checkForPendingPreloads()
+{
+ unsigned count = m_pendingPreloads.size();
+ 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_document->completeURL(preload.m_url)))
+ requestPreload(preload.m_type, preload.m_url, preload.m_charset);
+ }
+ m_pendingPreloads.clear();
+}
+
+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_document->frame()->loader()->writer()->encoding() : charset;
+
+ CachedResource* resource = requestResource(type, url, encoding, ResourceLoadPriorityUnresolved, true);
+ if (!resource || (m_preloads && m_preloads->contains(resource)))
+ return;
+ resource->increasePreloadCount();
+
+ 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 CachedResourceLoader::clearPreloads()
+{
+#if PRELOAD_DEBUG
+ printPreloadStats();
+#endif
+ 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())
+ delete res;
+ else if (res->preloadResult() == CachedResource::PreloadNotReferenced)
+ cache()->remove(res);
+ }
+ m_preloads.clear();
+}
+
+void CachedResourceLoader::clearPendingPreloads()
+{
+ m_pendingPreloads.clear();
+}
+
+#if PRELOAD_DEBUG
+void CachedResourceLoader::printPreloadStats()
+{
+ unsigned scripts = 0;
+ unsigned scriptMisses = 0;
+ unsigned stylesheets = 0;
+ unsigned stylesheetMisses = 0;
+ unsigned images = 0;
+ unsigned imageMisses = 0;
+ ListHashSet<CachedResource*>::iterator end = m_preloads.end();
+ for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
+ CachedResource* res = *it;
+ if (res->preloadResult() == CachedResource::PreloadNotReferenced)
+ printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
+ else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
+ printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
+ else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
+ printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
+
+ if (res->type() == CachedResource::Script) {
+ scripts++;
+ if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
+ scriptMisses++;
+ } else if (res->type() == CachedResource::CSSStyleSheet) {
+ stylesheets++;
+ if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
+ stylesheetMisses++;
+ } else {
+ images++;
+ if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
+ imageMisses++;
+ }
+
+ if (res->errorOccurred())
+ cache()->remove(res);
+
+ res->decreasePreloadCount();
+ }
+ m_preloads.clear();
+
+ if (scripts)
+ printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
+ if (stylesheets)
+ printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
+ if (images)
+ printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
+}
+#endif
+
+}
diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.h b/Source/WebCore/loader/cache/CachedResourceLoader.h
new file mode 100644
index 0000000..1d53976
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceLoader.h
@@ -0,0 +1,156 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
+ Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ 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 CachedResourceLoader_h
+#define CachedResourceLoader_h
+
+#include "CachedResource.h"
+#include "CachedResourceHandle.h"
+#include "CachePolicy.h"
+#include "ResourceLoadPriority.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+class CachedCSSStyleSheet;
+class CachedFont;
+class CachedImage;
+class CachedResourceRequest;
+class CachedScript;
+class CachedXSLStyleSheet;
+class Document;
+class Frame;
+class ImageLoader;
+class KURL;
+
+// The CachedResourceLoader manages the loading of scripts/images/stylesheets for a single document.
+class CachedResourceLoader : public Noncopyable {
+friend class MemoryCache;
+friend class ImageLoader;
+
+public:
+ CachedResourceLoader(Document*);
+ ~CachedResourceLoader();
+
+ CachedImage* requestImage(const String& url);
+ CachedCSSStyleSheet* requestCSSStyleSheet(const String& url, const String& charset, ResourceLoadPriority priority = ResourceLoadPriorityUnresolved);
+ CachedCSSStyleSheet* requestUserCSSStyleSheet(const String& url, const String& charset);
+ CachedScript* requestScript(const String& url, const String& charset);
+ CachedFont* requestFont(const String& url);
+
+#if ENABLE(XSLT)
+ CachedXSLStyleSheet* requestXSLStyleSheet(const String& url);
+#endif
+#if ENABLE(LINK_PREFETCH)
+ CachedResource* requestLinkPrefetch(const String &url);
+#endif
+
+ // Logs an access denied message to the console for the specified URL.
+ void printAccessDeniedMessage(const KURL& url) const;
+
+ CachedResource* cachedResource(const String& url) const;
+ CachedResource* cachedResource(const KURL& url) const;
+
+ typedef HashMap<String, CachedResourceHandle<CachedResource> > DocumentResourceMap;
+ const DocumentResourceMap& allCachedResources() const { return m_documentResources; }
+
+ bool autoLoadImages() const { return m_autoLoadImages; }
+ void setAutoLoadImages(bool);
+
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ bool blockNetworkImage() const { return m_blockNetworkImage; }
+ void setBlockNetworkImage(bool);
+ bool shouldBlockNetworkImage(const String& url) const;
+#endif
+
+ CachePolicy cachePolicy() const;
+
+ Frame* frame() const; // Can be NULL
+ Document* document() const { return m_document; }
+
+ void removeCachedResource(CachedResource*) const;
+
+ void load(CachedResource*, bool incremental = false, SecurityCheckPolicy = DoSecurityCheck, bool sendResourceLoadCallbacks = true);
+ void loadFinishing() { m_loadFinishing = true; }
+ void loadDone(CachedResourceRequest*);
+ void cancelRequests();
+
+ void setAllowStaleResources(bool allowStaleResources) { m_allowStaleResources = allowStaleResources; }
+
+ void incrementRequestCount(const CachedResource*);
+ void decrementRequestCount(const CachedResource*);
+ int requestCount();
+
+ void clearPreloads();
+ void clearPendingPreloads();
+ void preload(CachedResource::Type, const String& url, const String& charset, bool referencedFromBody);
+ void checkForPendingPreloads();
+ void printPreloadStats();
+
+private:
+ CachedResource* requestResource(CachedResource::Type, const String& url, const String& charset, ResourceLoadPriority priority = ResourceLoadPriorityUnresolved, bool isPreload = false);
+ CachedResource* revalidateResource(CachedResource*, ResourceLoadPriority priority);
+ CachedResource* loadResource(CachedResource::Type, const KURL&, const String& charset, ResourceLoadPriority priority);
+ void requestPreload(CachedResource::Type, const String& url, const String& charset);
+
+ enum RevalidationPolicy { Use, Revalidate, Reload, Load };
+ RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, bool forPreload, CachedResource* existingResource) const;
+
+ void notifyLoadedFromMemoryCache(CachedResource*);
+ bool canRequest(CachedResource::Type, const KURL&);
+
+ MemoryCache* m_cache;
+ HashSet<String> m_validatedURLs;
+ mutable DocumentResourceMap m_documentResources;
+ Document* m_document;
+
+ typedef HashSet<RefPtr<CachedResourceRequest> > RequestSet;
+ RequestSet m_requests;
+
+ int m_requestCount;
+
+ OwnPtr<ListHashSet<CachedResource*> > m_preloads;
+ struct PendingPreload {
+ CachedResource::Type m_type;
+ String m_url;
+ String m_charset;
+ };
+ Vector<PendingPreload> m_pendingPreloads;
+
+ //29 bits left
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ bool m_blockNetworkImage : 1;
+#endif
+ bool m_autoLoadImages : 1;
+ bool m_loadFinishing : 1;
+ bool m_allowStaleResources : 1;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedResourceRequest.cpp b/Source/WebCore/loader/cache/CachedResourceRequest.cpp
new file mode 100644
index 0000000..827bb8e
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceRequest.cpp
@@ -0,0 +1,280 @@
+/*
+ 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.
+*/
+
+#include "config.h"
+#include "CachedResourceRequest.h"
+
+#include "MemoryCache.h"
+#include "CachedImage.h"
+#include "CachedResource.h"
+#include "CachedResourceLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "Logging.h"
+#include "ResourceHandle.h"
+#include "ResourceLoadScheduler.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "SharedBuffer.h"
+#include <wtf/Assertions.h>
+#include <wtf/Vector.h>
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type)
+{
+ switch (type) {
+ case CachedResource::CSSStyleSheet:
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+#endif
+ return ResourceRequest::TargetIsStyleSheet;
+ case CachedResource::Script:
+ return ResourceRequest::TargetIsScript;
+ case CachedResource::FontResource:
+ 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;
+}
+
+CachedResourceRequest::CachedResourceRequest(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental)
+ : m_cachedResourceLoader(cachedResourceLoader)
+ , m_resource(resource)
+ , m_incremental(incremental)
+ , m_multipart(false)
+ , m_finishing(false)
+{
+ m_resource->setRequest(this);
+}
+
+CachedResourceRequest::~CachedResourceRequest()
+{
+ m_resource->setRequest(0);
+}
+
+PassRefPtr<CachedResourceRequest> CachedResourceRequest::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
+{
+ RefPtr<CachedResourceRequest> request = adoptRef(new CachedResourceRequest(cachedResourceLoader, resource, incremental));
+
+ ResourceRequest resourceRequest(resource->url());
+ resourceRequest.setTargetType(cachedResourceTypeToTargetType(resource->type()));
+
+ if (!resource->accept().isEmpty())
+ resourceRequest.setHTTPAccept(resource->accept());
+
+ if (resource->isCacheValidator()) {
+ CachedResource* resourceToRevalidate = resource->resourceToRevalidate();
+ ASSERT(resourceToRevalidate->canUseCacheValidator());
+ ASSERT(resourceToRevalidate->isLoaded());
+ const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
+ const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
+ if (!lastModified.isEmpty() || !eTag.isEmpty()) {
+ ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload);
+ if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate)
+ resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
+ if (!lastModified.isEmpty())
+ resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
+ if (!eTag.isEmpty())
+ resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
+ }
+ }
+
+#if ENABLE(LINK_PREFETCH)
+ if (resource->type() == CachedResource::LinkPrefetch)
+ resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
+#endif
+
+ ResourceLoadPriority priority = resource->loadPriority();
+
+ RefPtr<SubresourceLoader> loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(),
+ request.get(), resourceRequest, priority, securityCheck, sendResourceLoadCallbacks);
+ if (!loader || loader->reachedTerminalState()) {
+ // FIXME: What if resources in other frames were waiting for this revalidation?
+ LOG(ResourceLoading, "Cannot start loading '%s'", resource->url().latin1().data());
+ cachedResourceLoader->decrementRequestCount(resource);
+ cachedResourceLoader->loadFinishing();
+ if (resource->resourceToRevalidate())
+ cache()->revalidationFailed(resource);
+ resource->error(CachedResource::LoadError);
+ cachedResourceLoader->loadDone(0);
+ return 0;
+ }
+ request->m_loader = loader;
+ return request.release();
+}
+
+void CachedResourceRequest::willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse&)
+{
+ m_resource->setRequestedFromNetworkingLayer();
+}
+
+void CachedResourceRequest::didFinishLoading(SubresourceLoader* loader)
+{
+ if (m_finishing)
+ return;
+
+ ASSERT(loader == m_loader.get());
+ ASSERT(!m_resource->resourceToRevalidate());
+ LOG(ResourceLoading, "Received '%s'.", m_resource->url().latin1().data());
+
+ // Prevent the document from being destroyed before we are done with
+ // the cachedResourceLoader that it will delete when the document gets deleted.
+ RefPtr<Document> protector(m_cachedResourceLoader->document());
+ if (!m_multipart)
+ m_cachedResourceLoader->decrementRequestCount(m_resource);
+ m_finishing = true;
+
+ // 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 (!m_resource->errorOccurred()) {
+ m_cachedResourceLoader->loadFinishing();
+ m_resource->data(loader->resourceData(), true);
+ if (!m_resource->errorOccurred())
+ m_resource->finish();
+ }
+ m_cachedResourceLoader->loadDone(this);
+}
+
+void CachedResourceRequest::didFail(SubresourceLoader*, const ResourceError&)
+{
+ if (!m_loader)
+ return;
+ didFail();
+}
+
+void CachedResourceRequest::didFail(bool cancelled)
+{
+ if (m_finishing)
+ return;
+
+ LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", m_resource->url().latin1().data(), cancelled);
+
+ // Prevent the document from being destroyed before we are done with
+ // the cachedResourceLoader that it will delete when the document gets deleted.
+ RefPtr<Document> protector(m_cachedResourceLoader->document());
+ if (!m_multipart)
+ m_cachedResourceLoader->decrementRequestCount(m_resource);
+ m_finishing = true;
+ m_loader->clearClient();
+
+ if (m_resource->resourceToRevalidate())
+ cache()->revalidationFailed(m_resource);
+
+ if (!cancelled) {
+ m_cachedResourceLoader->loadFinishing();
+ m_resource->error(CachedResource::LoadError);
+ }
+
+ if (cancelled || !m_resource->isPreloaded())
+ cache()->remove(m_resource);
+
+ m_cachedResourceLoader->loadDone(this);
+}
+
+void CachedResourceRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
+{
+ ASSERT(loader == m_loader.get());
+ if (m_resource->isCacheValidator()) {
+ if (response.httpStatusCode() == 304) {
+ // 304 Not modified / Use local copy
+ loader->clearClient();
+ RefPtr<Document> protector(m_cachedResourceLoader->document());
+ m_cachedResourceLoader->decrementRequestCount(m_resource);
+ m_finishing = true;
+
+ // Existing resource is ok, just use it updating the expiration time.
+ cache()->revalidationSucceeded(m_resource, response);
+
+ if (m_cachedResourceLoader->frame())
+ m_cachedResourceLoader->frame()->loader()->checkCompleted();
+
+ m_cachedResourceLoader->loadDone(this);
+ return;
+ }
+ // Did not get 304 response, continue as a regular resource load.
+ cache()->revalidationFailed(m_resource);
+ }
+
+ m_resource->setResponse(response);
+
+ String encoding = response.textEncodingName();
+ if (!encoding.isNull())
+ m_resource->setEncoding(encoding);
+
+ if (m_multipart) {
+ ASSERT(m_resource->isImage());
+ static_cast<CachedImage*>(m_resource)->clear();
+ if (m_cachedResourceLoader->frame())
+ m_cachedResourceLoader->frame()->loader()->checkCompleted();
+ } else if (response.isMultipart()) {
+ m_multipart = true;
+
+ // We don't count multiParts in a CachedResourceLoader's request count
+ m_cachedResourceLoader->decrementRequestCount(m_resource);
+
+ // If we get a multipart response, we must have a handle
+ ASSERT(loader->handle());
+ if (!m_resource->isImage())
+ loader->handle()->cancel();
+ }
+}
+
+void CachedResourceRequest::didReceiveData(SubresourceLoader* loader, const char* data, int size)
+{
+ ASSERT(loader == m_loader.get());
+ ASSERT(!m_resource->isCacheValidator());
+
+ if (m_resource->errorOccurred())
+ return;
+
+ if (m_resource->response().httpStatusCode() >= 400) {
+ if (!m_resource->shouldIgnoreHTTPStatusCodeErrors())
+ m_resource->error(CachedResource::LoadError);
+ return;
+ }
+
+ // Set the data.
+ if (m_multipart) {
+ // The loader delivers the data in a multipart section all at once, send eof.
+ // The resource data will change as the next part is loaded, so we need to make a copy.
+ RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size);
+ m_resource->data(copiedData.release(), true);
+ } else if (m_incremental)
+ m_resource->data(loader->resourceData(), false);
+}
+
+void CachedResourceRequest::didReceiveCachedMetadata(SubresourceLoader*, const char* data, int size)
+{
+ ASSERT(!m_resource->isCacheValidator());
+ m_resource->setSerializedCachedMetadata(data, size);
+}
+
+} //namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedResourceRequest.h b/Source/WebCore/loader/cache/CachedResourceRequest.h
new file mode 100644
index 0000000..389b9ce
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedResourceRequest.h
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ 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 CachedResourceRequest_h
+#define CachedResourceRequest_h
+
+#include "FrameLoaderTypes.h"
+#include "SubresourceLoader.h"
+#include "SubresourceLoaderClient.h"
+#include <wtf/HashMap.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+ class CachedResource;
+ class CachedResourceLoader;
+ class Request;
+
+ class CachedResourceRequest : public RefCounted<CachedResourceRequest>, private SubresourceLoaderClient {
+ public:
+ static PassRefPtr<CachedResourceRequest> load(CachedResourceLoader*, CachedResource*, bool incremental, SecurityCheckPolicy, bool sendResourceLoadCallbacks);
+ ~CachedResourceRequest();
+ void didFail(bool cancelled = false);
+
+ CachedResourceLoader* cachedResourceLoader() const { return m_cachedResourceLoader; }
+
+ private:
+ CachedResourceRequest(CachedResourceLoader*, CachedResource*, bool incremental);
+ virtual void willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse&);
+ 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&);
+
+ RefPtr<SubresourceLoader> m_loader;
+ CachedResourceLoader* m_cachedResourceLoader;
+ CachedResource* m_resource;
+ bool m_incremental;
+ bool m_multipart;
+ bool m_finishing;
+ };
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedScript.cpp b/Source/WebCore/loader/cache/CachedScript.cpp
new file mode 100644
index 0000000..54b4503
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedScript.cpp
@@ -0,0 +1,124 @@
+/*
+ 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"
+#include "CachedScript.h"
+
+#include "MemoryCache.h"
+#include "CachedResourceClient.h"
+#include "CachedResourceClientWalker.h"
+#include "SharedBuffer.h"
+#include "TextResourceDecoder.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+CachedScript::CachedScript(const String& url, const String& charset)
+ : CachedResource(url, Script)
+ , m_decoder(TextResourceDecoder::create("application/javascript", charset))
+ , m_decodedDataDeletionTimer(this, &CachedScript::decodedDataDeletionTimerFired)
+{
+ // It's javascript we want.
+ // But some websites think their scripts are <some wrong mimetype here>
+ // and refuse to serve them if we only accept application/x-javascript.
+ setAccept("*/*");
+}
+
+CachedScript::~CachedScript()
+{
+}
+
+void CachedScript::allClientsRemoved()
+{
+ m_decodedDataDeletionTimer.startOneShot(0);
+}
+
+void CachedScript::setEncoding(const String& chs)
+{
+ m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader);
+}
+
+String CachedScript::encoding() const
+{
+ return m_decoder->encoding().name();
+}
+
+const String& CachedScript::script()
+{
+ ASSERT(!isPurgeable());
+
+ if (!m_script && m_data) {
+ m_script = m_decoder->decode(m_data->data(), encodedSize());
+ m_script += m_decoder->flush();
+ setDecodedSize(m_script.length() * sizeof(UChar));
+ }
+ m_decodedDataDeletionTimer.startOneShot(0);
+ return m_script;
+}
+
+void CachedScript::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
+{
+ if (!allDataReceived)
+ return;
+
+ m_data = data;
+ setEncodedSize(m_data.get() ? m_data->size() : 0);
+ setLoading(false);
+ checkNotify();
+}
+
+void CachedScript::checkNotify()
+{
+ if (isLoading())
+ return;
+
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient* c = w.next())
+ c->notifyFinished(this);
+}
+
+void CachedScript::error(CachedResource::Status status)
+{
+ setStatus(status);
+ ASSERT(errorOccurred());
+ setLoading(false);
+ checkNotify();
+}
+
+void CachedScript::destroyDecodedData()
+{
+ m_script = String();
+ setDecodedSize(0);
+ if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable())
+ makePurgeable(true);
+}
+
+void CachedScript::decodedDataDeletionTimerFired(Timer<CachedScript>*)
+{
+ destroyDecodedData();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedScript.h b/Source/WebCore/loader/cache/CachedScript.h
new file mode 100644
index 0000000..30fcb1e
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedScript.h
@@ -0,0 +1,65 @@
+/*
+ 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 CachedScript_h
+#define CachedScript_h
+
+#include "CachedResource.h"
+#include "Timer.h"
+
+namespace WebCore {
+
+ class CachedResourceLoader;
+ class TextResourceDecoder;
+
+ class CachedScript : public CachedResource {
+ public:
+ CachedScript(const String& url, const String& charset);
+ virtual ~CachedScript();
+
+ const String& script();
+
+ virtual void allClientsRemoved();
+
+ virtual void setEncoding(const String&);
+ virtual String encoding() const;
+ virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived);
+ virtual void error(CachedResource::Status);
+
+ void checkNotify();
+
+ virtual void destroyDecodedData();
+
+ private:
+ void decodedDataDeletionTimerFired(Timer<CachedScript>*);
+ virtual PurgePriority purgePriority() const { return PurgeLast; }
+
+ String m_script;
+ RefPtr<TextResourceDecoder> m_decoder;
+ Timer<CachedScript> m_decodedDataDeletionTimer;
+ };
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp b/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp
new file mode 100644
index 0000000..ca7bf13
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp
@@ -0,0 +1,100 @@
+/*
+ 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"
+#include "CachedXSLStyleSheet.h"
+
+#include "CachedResourceClient.h"
+#include "CachedResourceClientWalker.h"
+#include "SharedBuffer.h"
+#include "TextResourceDecoder.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+#if ENABLE(XSLT)
+
+CachedXSLStyleSheet::CachedXSLStyleSheet(const String &url)
+ : CachedResource(url, XSLStyleSheet)
+ , m_decoder(TextResourceDecoder::create("text/xsl"))
+{
+ // It's XML we want.
+ // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example.
+ setAccept("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml");
+}
+
+void CachedXSLStyleSheet::didAddClient(CachedResourceClient* c)
+{
+ if (!isLoading())
+ c->setXSLStyleSheet(m_url, m_response.url(), m_sheet);
+}
+
+void CachedXSLStyleSheet::setEncoding(const String& chs)
+{
+ m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader);
+}
+
+String CachedXSLStyleSheet::encoding() const
+{
+ return m_decoder->encoding().name();
+}
+
+void CachedXSLStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
+{
+ if (!allDataReceived)
+ return;
+
+ m_data = data;
+ setEncodedSize(m_data.get() ? m_data->size() : 0);
+ if (m_data.get()) {
+ m_sheet = String(m_decoder->decode(m_data->data(), encodedSize()));
+ m_sheet += m_decoder->flush();
+ }
+ setLoading(false);
+ checkNotify();
+}
+
+void CachedXSLStyleSheet::checkNotify()
+{
+ if (isLoading())
+ return;
+
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient *c = w.next())
+ c->setXSLStyleSheet(m_url, m_response.url(), m_sheet);
+}
+
+void CachedXSLStyleSheet::error(CachedResource::Status status)
+{
+ setStatus(status);
+ ASSERT(errorOccurred());
+ setLoading(false);
+ checkNotify();
+}
+
+#endif
+
+}
diff --git a/Source/WebCore/loader/cache/CachedXSLStyleSheet.h b/Source/WebCore/loader/cache/CachedXSLStyleSheet.h
new file mode 100644
index 0000000..8b29792
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedXSLStyleSheet.h
@@ -0,0 +1,62 @@
+/*
+ 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 CachedXSLStyleSheet_h
+#define CachedXSLStyleSheet_h
+
+#include "CachedResource.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+ class CachedResourceLoader;
+ class TextResourceDecoder;
+
+#if ENABLE(XSLT)
+ class CachedXSLStyleSheet : public CachedResource {
+ public:
+ CachedXSLStyleSheet(const String& url);
+
+ const String& sheet() const { return m_sheet; }
+
+ virtual void didAddClient(CachedResourceClient*);
+
+ virtual void setEncoding(const String&);
+ virtual String encoding() const;
+ virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived);
+ virtual void error(CachedResource::Status);
+
+ void checkNotify();
+
+ protected:
+ String m_sheet;
+ RefPtr<TextResourceDecoder> m_decoder;
+ };
+
+#endif
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/MemoryCache.cpp b/Source/WebCore/loader/cache/MemoryCache.cpp
new file mode 100644
index 0000000..930033a
--- /dev/null
+++ b/Source/WebCore/loader/cache/MemoryCache.cpp
@@ -0,0 +1,670 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2004, 2005, 2006, 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.
+*/
+
+#include "config.h"
+#include "MemoryCache.h"
+
+#include "CachedCSSStyleSheet.h"
+#include "CachedFont.h"
+#include "CachedImage.h"
+#include "CachedScript.h"
+#include "CachedXSLStyleSheet.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>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+using namespace std;
+
+namespace WebCore {
+
+static const int cDefaultCacheCapacity = 8192 * 1024;
+static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds.
+static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again.
+static const double cDefaultDecodedDataDeletionInterval = 0;
+
+MemoryCache* cache()
+{
+ static MemoryCache* staticCache = new MemoryCache;
+ return staticCache;
+}
+
+MemoryCache::MemoryCache()
+ : m_disabled(false)
+ , m_pruneEnabled(true)
+ , m_inPruneDeadResources(false)
+ , m_capacity(cDefaultCacheCapacity)
+ , m_minDeadCapacity(0)
+ , m_maxDeadCapacity(cDefaultCacheCapacity)
+ , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval)
+ , m_liveSize(0)
+ , m_deadSize(0)
+{
+}
+
+KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL)
+{
+ if (!originalURL.hasFragmentIdentifier())
+ return originalURL;
+ // Strip away fragment identifier from HTTP and file urls.
+ // Data urls must be unmodified and it is also safer to keep them for custom protocols.
+ if (!(originalURL.protocolInHTTPFamily() || originalURL.isLocalFile()))
+ return originalURL;
+ KURL url = originalURL;
+ url.removeFragmentIdentifier();
+ return url;
+}
+
+bool MemoryCache::add(CachedResource* resource)
+{
+ if (disabled())
+ return false;
+
+ m_resources.set(resource->url(), resource);
+ resource->setInCache(true);
+
+ resourceAccessed(resource);
+
+ LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource->url().latin1().data(), resource);
+ return true;
+}
+
+void MemoryCache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response)
+{
+ CachedResource* resource = revalidatingResource->resourceToRevalidate();
+ ASSERT(resource);
+ ASSERT(!resource->inCache());
+ ASSERT(resource->isLoaded());
+ ASSERT(revalidatingResource->inCache());
+
+ evict(revalidatingResource);
+
+ ASSERT(!m_resources.get(resource->url()));
+ m_resources.set(resource->url(), resource);
+ resource->setInCache(true);
+ resource->updateResponseAfterRevalidation(response);
+ insertInLRUList(resource);
+ int delta = resource->size();
+ if (resource->decodedSize() && resource->hasClients())
+ insertInLiveDecodedResourcesList(resource);
+ if (delta)
+ adjustSize(resource->hasClients(), delta);
+
+ revalidatingResource->switchClientsToRevalidatedResource();
+ // this deletes the revalidating resource
+ revalidatingResource->clearResourceToRevalidate();
+}
+
+void MemoryCache::revalidationFailed(CachedResource* revalidatingResource)
+{
+ LOG(ResourceLoading, "Revalidation failed for %p", revalidatingResource);
+ ASSERT(revalidatingResource->resourceToRevalidate());
+ revalidatingResource->clearResourceToRevalidate();
+}
+
+CachedResource* MemoryCache::resourceForURL(const KURL& resourceURL)
+{
+ KURL url = removeFragmentIdentifierIfNeeded(resourceURL);
+ 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 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.
+ capacity = max(capacity, m_minDeadCapacity); // Make sure it's above the minimum.
+ capacity = min(capacity, m_maxDeadCapacity); // Make sure it's below the maximum.
+ return capacity;
+}
+
+unsigned MemoryCache::liveCapacity() const
+{
+ // Live resource capacity is whatever is left over after calculating dead resource capacity.
+ return m_capacity - deadCapacity();
+}
+
+void MemoryCache::pruneLiveResources()
+{
+ if (!m_pruneEnabled)
+ return;
+
+ unsigned capacity = liveCapacity();
+ if (capacity && m_liveSize <= capacity)
+ return;
+
+ unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
+ double currentTime = FrameView::currentPaintTimeStamp();
+ if (!currentTime) // In case prune is called directly, outside of a Frame paint.
+ currentTime = WTF::currentTime();
+
+ // Destroy any decoded data in live objects that we can.
+ // Start from the tail, since this is the least recently accessed of the objects.
+
+ // The list might not be sorted by the m_lastDecodedAccessTime. The impact
+ // of this weaker invariant is minor as the below if statement to check the
+ // elapsedTime will evaluate to false as the currentTime will be a lot
+ // greater than the current->m_lastDecodedAccessTime.
+ // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209
+ CachedResource* current = m_liveDecodedResources.m_tail;
+ while (current) {
+ CachedResource* prev = current->m_prevInLiveResourcesList;
+ ASSERT(current->hasClients());
+ if (current->isLoaded() && current->decodedSize()) {
+ // Check to see if the remaining resources are too new to prune.
+ double elapsedTime = currentTime - current->m_lastDecodedAccessTime;
+ if (elapsedTime < cMinDelayBeforeLiveDecodedPrune)
+ return;
+
+ // Destroy our decoded data. This will remove us from
+ // m_liveDecodedResources, and possibly move us to a different LRU
+ // list in m_allResources.
+ current->destroyDecodedData();
+
+ if (targetSize && m_liveSize <= targetSize)
+ return;
+ }
+ current = prev;
+ }
+}
+
+void MemoryCache::pruneDeadResources()
+{
+ if (!m_pruneEnabled)
+ return;
+
+ unsigned capacity = deadCapacity();
+ if (capacity && m_deadSize <= capacity)
+ return;
+
+ unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
+ int size = m_allResources.size();
+
+ if (!m_inPruneDeadResources) {
+ // See if we have any purged resources we can evict.
+ for (int i = 0; i < size; i++) {
+ CachedResource* current = m_allResources[i].m_tail;
+ while (current) {
+ CachedResource* prev = current->m_prevInAllResourcesList;
+ if (current->wasPurged()) {
+ ASSERT(!current->hasClients());
+ ASSERT(!current->isPreloaded());
+ evict(current);
+ }
+ current = prev;
+ }
+ }
+ if (targetSize && m_deadSize <= targetSize)
+ return;
+ }
+
+ bool canShrinkLRULists = true;
+ m_inPruneDeadResources = true;
+ for (int i = size - 1; i >= 0; i--) {
+ // Remove from the tail, since this is the least frequently accessed of the objects.
+ CachedResource* current = m_allResources[i].m_tail;
+
+ // First flush all the decoded data in this queue.
+ while (current) {
+ CachedResource* prev = current->m_prevInAllResourcesList;
+ if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) {
+ // Destroy our decoded data. This will remove us from
+ // m_liveDecodedResources, and possibly move us to a different
+ // LRU list in m_allResources.
+ current->destroyDecodedData();
+
+ if (targetSize && m_deadSize <= targetSize) {
+ m_inPruneDeadResources = false;
+ return;
+ }
+ }
+ current = prev;
+ }
+
+ // Now evict objects from this queue.
+ current = m_allResources[i].m_tail;
+ while (current) {
+ CachedResource* prev = current->m_prevInAllResourcesList;
+ if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) {
+ 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)
+ return;
+
+ if (targetSize && m_deadSize <= targetSize) {
+ m_inPruneDeadResources = false;
+ return;
+ }
+ }
+ current = prev;
+ }
+
+ // Shrink the vector back down so we don't waste time inspecting
+ // empty LRU lists on future prunes.
+ if (m_allResources[i].m_head)
+ canShrinkLRULists = false;
+ else if (canShrinkLRULists)
+ m_allResources.resize(i);
+ }
+ m_inPruneDeadResources = false;
+}
+
+void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes)
+{
+ ASSERT(minDeadBytes <= maxDeadBytes);
+ ASSERT(maxDeadBytes <= totalBytes);
+ m_minDeadCapacity = minDeadBytes;
+ m_maxDeadCapacity = maxDeadBytes;
+ m_capacity = totalBytes;
+ prune();
+}
+
+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()) {
+ // Remove from the resource map.
+ m_resources.remove(resource->url());
+ resource->setInCache(false);
+
+ // Remove from the appropriate LRU list.
+ removeFromLRUList(resource);
+ removeFromLiveDecodedResourcesList(resource);
+
+ // 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);
+
+ if (resource->canDelete())
+ delete resource;
+}
+
+void MemoryCache::addCachedResourceLoader(CachedResourceLoader* cachedResourceLoader)
+{
+ m_cachedResourceLoaders.add(cachedResourceLoader);
+}
+
+void MemoryCache::removeCachedResourceLoader(CachedResourceLoader* cachedResourceLoader)
+{
+ m_cachedResourceLoaders.remove(cachedResourceLoader);
+}
+
+static inline unsigned fastLog2(unsigned i)
+{
+ unsigned log2 = 0;
+ if (i & (i - 1))
+ log2 += 1;
+ if (i >> 16)
+ log2 += 16, i >>= 16;
+ if (i >> 8)
+ log2 += 8, i >>= 8;
+ if (i >> 4)
+ log2 += 4, i >>= 4;
+ if (i >> 2)
+ log2 += 2, i >>= 2;
+ if (i >> 1)
+ log2 += 1;
+ return log2;
+}
+
+MemoryCache::LRUList* MemoryCache::lruListFor(CachedResource* resource)
+{
+ unsigned accessCount = max(resource->accessCount(), 1U);
+ unsigned queueIndex = fastLog2(resource->size() / accessCount);
+#ifndef NDEBUG
+ resource->m_lruIndex = queueIndex;
+#endif
+ if (m_allResources.size() <= queueIndex)
+ m_allResources.grow(queueIndex + 1);
+ return &m_allResources[queueIndex];
+}
+
+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)
+ return;
+
+#if !ASSERT_DISABLED
+ unsigned oldListIndex = resource->m_lruIndex;
+#endif
+
+ LRUList* list = lruListFor(resource);
+
+#if !ASSERT_DISABLED
+ // Verify that the list we got is the list we want.
+ ASSERT(resource->m_lruIndex == oldListIndex);
+
+ // Verify that we are in fact in this list.
+ bool found = false;
+ for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) {
+ if (current == resource) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT(found);
+#endif
+
+ CachedResource* next = resource->m_nextInAllResourcesList;
+ CachedResource* prev = resource->m_prevInAllResourcesList;
+
+ if (next == 0 && prev == 0 && list->m_head != resource)
+ return;
+
+ resource->m_nextInAllResourcesList = 0;
+ resource->m_prevInAllResourcesList = 0;
+
+ if (next)
+ next->m_prevInAllResourcesList = prev;
+ else if (list->m_tail == resource)
+ list->m_tail = prev;
+
+ if (prev)
+ prev->m_nextInAllResourcesList = next;
+ else if (list->m_head == resource)
+ list->m_head = next;
+}
+
+void MemoryCache::insertInLRUList(CachedResource* resource)
+{
+ // Make sure we aren't in some list already.
+ ASSERT(!resource->m_nextInAllResourcesList && !resource->m_prevInAllResourcesList);
+ ASSERT(resource->inCache());
+ ASSERT(resource->accessCount() > 0);
+
+ LRUList* list = lruListFor(resource);
+
+ resource->m_nextInAllResourcesList = list->m_head;
+ if (list->m_head)
+ list->m_head->m_prevInAllResourcesList = resource;
+ list->m_head = resource;
+
+ if (!resource->m_nextInAllResourcesList)
+ list->m_tail = resource;
+
+#ifndef NDEBUG
+ // Verify that we are in now in the list like we should be.
+ list = lruListFor(resource);
+ bool found = false;
+ for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) {
+ if (current == resource) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT(found);
+#endif
+
+}
+
+void MemoryCache::resourceAccessed(CachedResource* resource)
+{
+ ASSERT(resource->inCache());
+
+ // Need to make sure to remove before we increase the access count, since
+ // the queue will possibly change.
+ removeFromLRUList(resource);
+
+ // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size.
+ if (!resource->accessCount())
+ adjustSize(resource->hasClients(), resource->size());
+
+ // Add to our access count.
+ resource->increaseAccessCount();
+
+ // Now insert into the new queue.
+ insertInLRUList(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)
+ return;
+ resource->m_inLiveDecodedResourcesList = false;
+
+#ifndef NDEBUG
+ // Verify that we are in fact in this list.
+ bool found = false;
+ for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) {
+ if (current == resource) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT(found);
+#endif
+
+ CachedResource* next = resource->m_nextInLiveResourcesList;
+ CachedResource* prev = resource->m_prevInLiveResourcesList;
+
+ if (next == 0 && prev == 0 && m_liveDecodedResources.m_head != resource)
+ return;
+
+ resource->m_nextInLiveResourcesList = 0;
+ resource->m_prevInLiveResourcesList = 0;
+
+ if (next)
+ next->m_prevInLiveResourcesList = prev;
+ else if (m_liveDecodedResources.m_tail == resource)
+ m_liveDecodedResources.m_tail = prev;
+
+ if (prev)
+ prev->m_nextInLiveResourcesList = next;
+ else if (m_liveDecodedResources.m_head == resource)
+ m_liveDecodedResources.m_head = next;
+}
+
+void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource)
+{
+ // Make sure we aren't in the list already.
+ ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResourcesList && !resource->m_inLiveDecodedResourcesList);
+ resource->m_inLiveDecodedResourcesList = true;
+
+ resource->m_nextInLiveResourcesList = m_liveDecodedResources.m_head;
+ if (m_liveDecodedResources.m_head)
+ m_liveDecodedResources.m_head->m_prevInLiveResourcesList = resource;
+ m_liveDecodedResources.m_head = resource;
+
+ if (!resource->m_nextInLiveResourcesList)
+ m_liveDecodedResources.m_tail = resource;
+
+#ifndef NDEBUG
+ // Verify that we are in now in the list like we should be.
+ bool found = false;
+ for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) {
+ if (current == resource) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT(found);
+#endif
+
+}
+
+void MemoryCache::addToLiveResourcesSize(CachedResource* resource)
+{
+ m_liveSize += resource->size();
+ m_deadSize -= resource->size();
+}
+
+void MemoryCache::removeFromLiveResourcesSize(CachedResource* resource)
+{
+ m_liveSize -= resource->size();
+ m_deadSize += resource->size();
+}
+
+void MemoryCache::adjustSize(bool live, int delta)
+{
+ if (live) {
+ ASSERT(delta >= 0 || ((int)m_liveSize + delta >= 0));
+ m_liveSize += delta;
+ } else {
+ ASSERT(delta >= 0 || ((int)m_deadSize + delta >= 0));
+ m_deadSize += delta;
+ }
+}
+
+void MemoryCache::TypeStatistic::addResource(CachedResource* o)
+{
+ bool purged = o->wasPurged();
+ bool purgeable = o->isPurgeable() && !purged;
+ int pageSize = (o->encodedSize() + o->overheadSize() + 4095) & ~4095;
+ count++;
+ size += purged ? 0 : o->size();
+ liveSize += o->hasClients() ? o->size() : 0;
+ decodedSize += o->decodedSize();
+ purgeableSize += purgeable ? pageSize : 0;
+ purgedSize += purged ? pageSize : 0;
+}
+
+MemoryCache::Statistics MemoryCache::getStatistics()
+{
+ Statistics stats;
+ CachedResourceMap::iterator e = m_resources.end();
+ for (CachedResourceMap::iterator i = m_resources.begin(); i != e; ++i) {
+ CachedResource* resource = i->second;
+ switch (resource->type()) {
+ case CachedResource::ImageResource:
+ stats.images.addResource(resource);
+ break;
+ case CachedResource::CSSStyleSheet:
+ stats.cssStyleSheets.addResource(resource);
+ break;
+ case CachedResource::Script:
+ stats.scripts.addResource(resource);
+ break;
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+ stats.xslStyleSheets.addResource(resource);
+ break;
+#endif
+ case CachedResource::FontResource:
+ stats.fonts.addResource(resource);
+ break;
+ default:
+ break;
+ }
+ }
+ return stats;
+}
+
+void MemoryCache::setDisabled(bool disabled)
+{
+ m_disabled = disabled;
+ if (!m_disabled)
+ return;
+
+ for (;;) {
+ CachedResourceMap::iterator i = m_resources.begin();
+ if (i == m_resources.end())
+ break;
+ evict(i->second);
+ }
+}
+
+#ifndef NDEBUG
+void MemoryCache::dumpStats()
+{
+ Statistics s = getStatistics();
+ 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("%-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("%-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 MemoryCache::dumpLRULists(bool includeLive) const
+{
+ 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--) {
+ printf("\n\nList %d: ", i);
+ CachedResource* current = m_allResources[i].m_tail;
+ while (current) {
+ CachedResource* prev = current->m_prevInAllResourcesList;
+ if (includeLive || !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;
+ }
+ }
+}
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/MemoryCache.h b/Source/WebCore/loader/cache/MemoryCache.h
new file mode 100644
index 0000000..dc47733
--- /dev/null
+++ b/Source/WebCore/loader/cache/MemoryCache.h
@@ -0,0 +1,231 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ 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 Cache_h
+#define Cache_h
+
+#include "CachePolicy.h"
+#include "CachedResource.h"
+#include "PlatformString.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 CachedResourceLoader;
+class KURL;
+
+// This cache holds subresources used by Web pages: images, scripts, stylesheets, etc.
+
+// The cache keeps a flexible but bounded window of dead resources that grows/shrinks
+// depending on the live resource load. Here's an example of cache growth over time,
+// with a min dead resource capacity of 25% and a max dead resource capacity of 50%:
+
+// |-----| Dead: -
+// |----------| Live: +
+// --|----------| Cache boundary: | (objects outside this mark have been evicted)
+// --|----------++++++++++|
+// -------|-----+++++++++++++++|
+// -------|-----+++++++++++++++|+++++
+
+// 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 MemoryCache* cache();
+
+ typedef HashMap<String, CachedResource*> CachedResourceMap;
+
+ struct LRUList {
+ CachedResource* m_head;
+ CachedResource* m_tail;
+ LRUList() : m_head(0), m_tail(0) { }
+ };
+
+ struct TypeStatistic {
+ int count;
+ int size;
+ int liveSize;
+ int decodedSize;
+ int purgeableSize;
+ int purgedSize;
+ TypeStatistic() : count(0), size(0), liveSize(0), decodedSize(0), purgeableSize(0), purgedSize(0) { }
+ void addResource(CachedResource*);
+ };
+
+ struct Statistics {
+ TypeStatistic images;
+ TypeStatistic cssStyleSheets;
+ TypeStatistic scripts;
+#if ENABLE(XSLT)
+ TypeStatistic xslStyleSheets;
+#endif
+ TypeStatistic fonts;
+ };
+
+ CachedResource* resourceForURL(const KURL&);
+
+ bool add(CachedResource* resource);
+ void remove(CachedResource* resource) { evict(resource); }
+
+ static KURL removeFragmentIdentifierIfNeeded(const KURL& originalURL);
+
+ void revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse&);
+ void revalidationFailed(CachedResource* revalidatingResource);
+
+ // Sets the cache's memory capacities, in bytes. These will hold only approximately,
+ // since the decoded cost of resources like scripts and stylesheets is not known.
+ // - minDeadBytes: The maximum number of bytes that dead resources should consume when the cache is under pressure.
+ // - maxDeadBytes: The maximum number of bytes that dead resources should consume when the cache is not under pressure.
+ // - totalBytes: The maximum number of bytes that the cache should consume overall.
+ void setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes);
+
+ // Turn the cache on and off. Disabling the cache will remove all resources from the cache. They may
+ // still live on if they are referenced by some Web page though.
+ void setDisabled(bool);
+ bool disabled() const { return m_disabled; }
+
+ void setPruneEnabled(bool enabled) { m_pruneEnabled = enabled; }
+ void prune()
+ {
+ if (m_liveSize + m_deadSize <= m_capacity && m_maxDeadCapacity && m_deadSize <= m_maxDeadCapacity) // Fast path.
+ return;
+
+ pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live.
+ pruneLiveResources();
+ }
+
+ void setDeadDecodedDataDeletionInterval(double interval) { m_deadDecodedDataDeletionInterval = interval; }
+ double deadDecodedDataDeletionInterval() const { return m_deadDecodedDataDeletionInterval; }
+
+ void addCachedResourceLoader(CachedResourceLoader*);
+ void removeCachedResourceLoader(CachedResourceLoader*);
+
+ // Calls to put the cached resource into and out of LRU lists.
+ void insertInLRUList(CachedResource*);
+ void removeFromLRUList(CachedResource*);
+
+ // Called to adjust the cache totals when a resource changes size.
+ void adjustSize(bool live, int delta);
+
+ // Track decoded resources that are in the cache and referenced by a Web page.
+ void insertInLiveDecodedResourcesList(CachedResource*);
+ void removeFromLiveDecodedResourcesList(CachedResource*);
+
+ 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();
+
+ void resourceAccessed(CachedResource*);
+
+#ifdef ANDROID_INSTRUMENT
+ unsigned getLiveSize() { return m_liveSize; }
+ unsigned getDeadSize() { return m_deadSize; }
+#endif
+
+private:
+ MemoryCache();
+ ~MemoryCache(); // Not implemented to make sure nobody accidentally calls delete -- WebCore does not delete singletons.
+
+ LRUList* lruListFor(CachedResource*);
+#ifndef NDEBUG
+ void dumpStats();
+ void dumpLRULists(bool includeLive) const;
+#endif
+
+ unsigned liveCapacity() const;
+ unsigned deadCapacity() const;
+
+ 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<CachedResourceLoader*> m_cachedResourceLoaders;
+
+ bool m_disabled; // Whether or not the cache is enabled.
+ bool m_pruneEnabled;
+ bool m_inPruneDeadResources;
+
+ unsigned m_capacity;
+ unsigned m_minDeadCapacity;
+ unsigned m_maxDeadCapacity;
+ double m_deadDecodedDataDeletionInterval;
+
+ unsigned m_liveSize; // The number of bytes currently consumed by "live" resources in the cache.
+ unsigned m_deadSize; // The number of bytes currently consumed by "dead" resources in the cache.
+
+ // Size-adjusted and popularity-aware LRU list collection for cache objects. This collection can hold
+ // more resources than the cached resource map, since it can also hold "stale" multiple versions of objects that are
+ // waiting to die when the clients referencing them go away.
+ Vector<LRUList, 32> m_allResources;
+
+ // List just for live resources with decoded data. Access to this list is based off of painting the resource.
+ LRUList m_liveDecodedResources;
+
+ // A URL-based map of all resources that are in the cache (including the freshest version of objects that are currently being
+ // referenced by a Web page).
+ HashMap<String, CachedResource*> m_resources;
+};
+
+inline bool MemoryCache::shouldMakeResourcePurgeableOnEviction()
+{
+#if PLATFORM(IOS)
+ return true;
+#else
+ return false;
+#endif
+}
+
+// Function to obtain the global cache.
+MemoryCache* cache();
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cf/ResourceLoaderCFNet.cpp b/Source/WebCore/loader/cf/ResourceLoaderCFNet.cpp
new file mode 100644
index 0000000..833fdeb
--- /dev/null
+++ b/Source/WebCore/loader/cf/ResourceLoaderCFNet.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ResourceLoader.h"
+
+#if USE(CFNETWORK)
+
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+
+namespace WebCore {
+
+bool ResourceLoader::shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef cachedResponse)
+{
+ if (!m_sendResourceLoadCallbacks)
+ return 0;
+
+ CFURLResponseRef response = CFCachedURLResponseGetWrappedResponse(cachedResponse);
+ CFDataRef data = CFCachedURLResponseGetReceiverData(cachedResponse);
+ return frameLoader()->client()->shouldCacheResponse(documentLoader(), identifier(), response, CFDataGetBytePtr(data), CFDataGetLength(data));
+}
+
+} // namespace WebCore
+
+#endif // USE(CFNETWORK)
diff --git a/Source/WebCore/loader/icon/IconDatabase.cpp b/Source/WebCore/loader/icon/IconDatabase.cpp
new file mode 100644
index 0000000..6040037
--- /dev/null
+++ b/Source/WebCore/loader/icon/IconDatabase.cpp
@@ -0,0 +1,2096 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "IconDatabase.h"
+
+#if ENABLE(ICONDATABASE)
+
+#include "AutodrainedPool.h"
+#include "DocumentLoader.h"
+#include "FileSystem.h"
+#include "IconDatabaseClient.h"
+#include "IconRecord.h"
+#include "IntSize.h"
+#include "Logging.h"
+#include "ScriptController.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include "SuddenTermination.h"
+#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())
+
+// For methods that are meant to support the sync thread ONLY
+#define IS_ICON_SYNC_THREAD() (m_syncThread == currentThread())
+#define ASSERT_ICON_SYNC_THREAD() ASSERT(IS_ICON_SYNC_THREAD())
+
+#if PLATFORM(QT) || PLATFORM(GTK)
+#define CAN_THEME_URL_ICON
+#endif
+
+namespace WebCore {
+
+static IconDatabase* sharedIconDatabase = 0;
+static int databaseCleanupCounter = 0;
+
+// This version number is in the DB and marks the current generation of the schema
+// Currently, a mismatched schema causes the DB to be wiped and reset. This isn't
+// so bad during development but in the future, we would need to write a conversion
+// function to advance older released schemas to "current"
+static const int currentDatabaseVersion = 6;
+
+// Icons expire once every 4 days
+static const int iconExpirationTime = 60*60*24*4;
+
+static const int updateTimerDelay = 5;
+
+static bool checkIntegrityOnOpen = false;
+
+#ifndef NDEBUG
+static String urlForLogging(const String& url)
+{
+ static unsigned urlTruncationLength = 120;
+
+ if (url.length() < urlTruncationLength)
+ return url;
+ return url.substring(0, urlTruncationLength) + "...";
+}
+#endif
+
+static IconDatabaseClient* defaultClient()
+{
+ static IconDatabaseClient* defaultClient = new IconDatabaseClient();
+ return defaultClient;
+}
+
+IconDatabase* iconDatabase()
+{
+ if (!sharedIconDatabase) {
+ ScriptController::initializeThreading();
+ sharedIconDatabase = new IconDatabase;
+ }
+ return sharedIconDatabase;
+}
+
+// ************************
+// *** Main Thread Only ***
+// ************************
+
+void IconDatabase::setClient(IconDatabaseClient* client)
+{
+ // We don't allow a null client, because we never null check it anywhere in this code
+ // Also don't allow a client change after the thread has already began
+ // (setting the client should occur before the database is opened)
+ ASSERT(client);
+ ASSERT(!m_syncThreadRunning);
+ if (!client || m_syncThreadRunning)
+ return;
+
+ m_client = client;
+}
+
+bool IconDatabase::open(const String& databasePath)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ if (!m_isEnabled)
+ return false;
+
+ if (isOpen()) {
+ LOG_ERROR("Attempt to reopen the IconDatabase which is already open. Must close it first.");
+ return false;
+ }
+
+ m_databaseDirectory = databasePath.crossThreadString();
+
+ // Formulate the full path for the database file
+ m_completeDatabasePath = pathByAppendingComponent(m_databaseDirectory, defaultDatabaseFilename());
+
+ // Lock here as well as first thing in the thread so the thread doesn't actually commence until the createThread() call
+ // completes and m_syncThreadRunning is properly set
+ m_syncLock.lock();
+ m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore: IconDatabase");
+ m_syncThreadRunning = m_syncThread;
+ m_syncLock.unlock();
+ if (!m_syncThread)
+ return false;
+ return true;
+}
+
+void IconDatabase::close()
+{
+#ifdef ANDROID
+ // Since we close and reopen the database within the same process, reset
+ // this flag
+ m_initialPruningComplete = false;
+#endif
+ ASSERT_NOT_SYNC_THREAD();
+
+ if (m_syncThreadRunning) {
+ // Set the flag to tell the sync thread to wrap it up
+ m_threadTerminationRequested = true;
+
+ // Wake up the sync thread if it's waiting
+ wakeSyncThread();
+
+ // Wait for the sync thread to terminate
+ waitForThreadCompletion(m_syncThread, 0);
+ }
+
+ m_syncThreadRunning = false;
+ m_threadTerminationRequested = false;
+ m_removeIconsRequested = false;
+
+ m_syncDB.close();
+ ASSERT(!isOpen());
+}
+
+void IconDatabase::removeAllIcons()
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ if (!isOpen())
+ return;
+
+ LOG(IconDatabase, "Requesting background thread to remove all icons");
+
+ // Clear the in-memory record of every IconRecord, anything waiting to be read from disk, and anything waiting to be written to disk
+ {
+ MutexLocker locker(m_urlAndIconLock);
+
+ // Clear the IconRecords for every page URL - RefCounting will cause the IconRecords themselves to be deleted
+ // We don't delete the actual PageRecords because we have the "retain icon for url" count to keep track of
+ HashMap<String, PageURLRecord*>::iterator iter = m_pageURLToRecordMap.begin();
+ HashMap<String, PageURLRecord*>::iterator end = m_pageURLToRecordMap.end();
+ for (; iter != end; ++iter)
+ (*iter).second->setIconRecord(0);
+
+ // Clear the iconURL -> IconRecord map
+ m_iconURLToRecordMap.clear();
+
+ // Clear all in-memory records of things that need to be synced out to disk
+ {
+ MutexLocker locker(m_pendingSyncLock);
+ m_pageURLsPendingSync.clear();
+ m_iconsPendingSync.clear();
+ }
+
+ // Clear all in-memory records of things that need to be read in from disk
+ {
+ MutexLocker locker(m_pendingReadingLock);
+ m_pageURLsPendingImport.clear();
+ m_pageURLsInterestedInIcons.clear();
+ m_iconsPendingReading.clear();
+ m_loadersPendingDecision.clear();
+ }
+ }
+
+ m_removeIconsRequested = true;
+ wakeSyncThread();
+}
+
+Image* IconDatabase::iconForPageURL(const String& pageURLOriginal, const IntSize& size)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ // pageURLOriginal cannot be stored without being deep copied first.
+ // We should go our of our way to only copy it if we have to store it
+
+ if (!isOpen() || pageURLOriginal.isEmpty())
+ return defaultIcon(size);
+
+ MutexLocker locker(m_urlAndIconLock);
+
+ String pageURLCopy; // Creates a null string for easy testing
+
+ PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
+ if (!pageRecord) {
+ pageURLCopy = pageURLOriginal.crossThreadString();
+ pageRecord = getOrCreatePageURLRecord(pageURLCopy);
+ }
+
+ // If pageRecord is NULL, one of two things is true -
+ // 1 - The initial url import is incomplete and this pageURL was marked to be notified once it is complete if an iconURL exists
+ // 2 - The initial url import IS complete and this pageURL has no icon
+ if (!pageRecord) {
+ MutexLocker locker(m_pendingReadingLock);
+
+ // Import is ongoing, there might be an icon. In this case, register to be notified when the icon comes in
+ // If we ever reach this condition, we know we've already made the pageURL copy
+ if (!m_iconURLImportComplete)
+ m_pageURLsInterestedInIcons.add(pageURLCopy);
+
+ return 0;
+ }
+
+ IconRecord* iconRecord = pageRecord->iconRecord();
+
+ // If the initial URL import isn't complete, it's possible to have a PageURL record without an associated icon
+ // In this case, the pageURL is already in the set to alert the client when the iconURL mapping is complete so
+ // we can just bail now
+ if (!m_iconURLImportComplete && !iconRecord)
+ return 0;
+
+ // The only way we should *not* have an icon record is if this pageURL is retained but has no icon yet - make sure of that
+ ASSERT(iconRecord || m_retainedPageURLs.contains(pageURLOriginal));
+
+ if (!iconRecord)
+ return 0;
+
+ // If it's a new IconRecord object that doesn't have its imageData set yet,
+ // mark it to be read by the background thread
+ if (iconRecord->imageDataStatus() == ImageDataStatusUnknown) {
+ if (pageURLCopy.isNull())
+ pageURLCopy = pageURLOriginal.crossThreadString();
+
+ MutexLocker locker(m_pendingReadingLock);
+ m_pageURLsInterestedInIcons.add(pageURLCopy);
+ m_iconsPendingReading.add(iconRecord);
+ wakeSyncThread();
+ return 0;
+ }
+
+ // If the size parameter was (0, 0) that means the caller of this method just wanted the read from disk to be kicked off
+ // and isn't actually interested in the image return value
+ if (size == IntSize(0, 0))
+ return 0;
+
+ // PARANOID DISCUSSION: This method makes some assumptions. It returns a WebCore::image which the icon database might dispose of at anytime in the future,
+ // and Images aren't ref counted. So there is no way for the client to guarantee continued existence of the image.
+ // This has *always* been the case, but in practice clients would always create some other platform specific representation of the image
+ // and drop the raw Image*. On Mac an NSImage, and on windows drawing into an HBITMAP.
+ // The async aspect adds a huge question - what if the image is deleted before the platform specific API has a chance to create its own
+ // representation out of it?
+ // If an image is read in from the icondatabase, we do *not* overwrite any image data that exists in the in-memory cache.
+ // This is because we make the assumption that anything in memory is newer than whatever is in the database.
+ // So the only time the data will be set from the second thread is when it is INITIALLY being read in from the database, but we would never
+ // delete the image on the secondary thread if the image already exists.
+ return iconRecord->image(size);
+}
+
+void IconDatabase::readIconForPageURLFromDisk(const String& pageURL)
+{
+ // The effect of asking for an Icon for a pageURL automatically queues it to be read from disk
+ // if it hasn't already been set in memory. The special IntSize (0, 0) is a special way of telling
+ // that method "I don't care about the actual Image, i just want you to make sure you're getting it from disk.
+ iconForPageURL(pageURL, IntSize(0,0));
+}
+
+String IconDatabase::iconURLForPageURL(const String& pageURLOriginal)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
+ // Also, in the case we have a real answer for the caller, we must deep copy that as well
+
+ if (!isOpen() || pageURLOriginal.isEmpty())
+ return String();
+
+ MutexLocker locker(m_urlAndIconLock);
+
+ PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
+ if (!pageRecord)
+ pageRecord = getOrCreatePageURLRecord(pageURLOriginal.crossThreadString());
+
+ // If pageRecord is NULL, one of two things is true -
+ // 1 - The initial url import is incomplete and this pageURL has already been marked to be notified once it is complete if an iconURL exists
+ // 2 - The initial url import IS complete and this pageURL has no icon
+ if (!pageRecord)
+ return String();
+
+ // Possible the pageRecord is around because it's a retained pageURL with no iconURL, so we have to check
+ return pageRecord->iconRecord() ? pageRecord->iconRecord()->iconURL().threadsafeCopy() : String();
+}
+
+#ifdef CAN_THEME_URL_ICON
+static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord)
+{
+ defaultIconRecord->loadImageFromResource("urlIcon");
+}
+#else
+static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord)
+{
+ static const unsigned char defaultIconData[] = { 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x03, 0x32, 0x80, 0x00, 0x20, 0x50, 0x38, 0x24, 0x16, 0x0D, 0x07, 0x84, 0x42, 0x61, 0x50, 0xB8,
+ 0x64, 0x08, 0x18, 0x0D, 0x0A, 0x0B, 0x84, 0xA2, 0xA1, 0xE2, 0x08, 0x5E, 0x39, 0x28, 0xAF, 0x48, 0x24, 0xD3, 0x53, 0x9A, 0x37, 0x1D, 0x18, 0x0E, 0x8A, 0x4B, 0xD1, 0x38,
+ 0xB0, 0x7C, 0x82, 0x07, 0x03, 0x82, 0xA2, 0xE8, 0x6C, 0x2C, 0x03, 0x2F, 0x02, 0x82, 0x41, 0xA1, 0xE2, 0xF8, 0xC8, 0x84, 0x68, 0x6D, 0x1C, 0x11, 0x0A, 0xB7, 0xFA, 0x91,
+ 0x6E, 0xD1, 0x7F, 0xAF, 0x9A, 0x4E, 0x87, 0xFB, 0x19, 0xB0, 0xEA, 0x7F, 0xA4, 0x95, 0x8C, 0xB7, 0xF9, 0xA9, 0x0A, 0xA9, 0x7F, 0x8C, 0x88, 0x66, 0x96, 0xD4, 0xCA, 0x69,
+ 0x2F, 0x00, 0x81, 0x65, 0xB0, 0x29, 0x90, 0x7C, 0xBA, 0x2B, 0x21, 0x1E, 0x5C, 0xE6, 0xB4, 0xBD, 0x31, 0xB6, 0xE7, 0x7A, 0xBF, 0xDD, 0x6F, 0x37, 0xD3, 0xFD, 0xD8, 0xF2,
+ 0xB6, 0xDB, 0xED, 0xAC, 0xF7, 0x03, 0xC5, 0xFE, 0x77, 0x53, 0xB6, 0x1F, 0xE6, 0x24, 0x8B, 0x1D, 0xFE, 0x26, 0x20, 0x9E, 0x1C, 0xE0, 0x80, 0x65, 0x7A, 0x18, 0x02, 0x01,
+ 0x82, 0xC5, 0xA0, 0xC0, 0xF1, 0x89, 0xBA, 0x23, 0x30, 0xAD, 0x1F, 0xE7, 0xE5, 0x5B, 0x6D, 0xFE, 0xE7, 0x78, 0x3E, 0x1F, 0xEE, 0x97, 0x8B, 0xE7, 0x37, 0x9D, 0xCF, 0xE7,
+ 0x92, 0x8B, 0x87, 0x0B, 0xFC, 0xA0, 0x8E, 0x68, 0x3F, 0xC6, 0x27, 0xA6, 0x33, 0xFC, 0x36, 0x5B, 0x59, 0x3F, 0xC1, 0x02, 0x63, 0x3B, 0x74, 0x00, 0x03, 0x07, 0x0B, 0x61,
+ 0x00, 0x20, 0x60, 0xC9, 0x08, 0x00, 0x1C, 0x25, 0x9F, 0xE0, 0x12, 0x8A, 0xD5, 0xFE, 0x6B, 0x4F, 0x35, 0x9F, 0xED, 0xD7, 0x4B, 0xD9, 0xFE, 0x8A, 0x59, 0xB8, 0x1F, 0xEC,
+ 0x56, 0xD3, 0xC1, 0xFE, 0x63, 0x4D, 0xF2, 0x83, 0xC6, 0xB6, 0x1B, 0xFC, 0x34, 0x68, 0x61, 0x3F, 0xC1, 0xA6, 0x25, 0xEB, 0xFC, 0x06, 0x58, 0x5C, 0x3F, 0xC0, 0x03, 0xE4,
+ 0xC3, 0xFC, 0x04, 0x0F, 0x1A, 0x6F, 0xE0, 0xE0, 0x20, 0xF9, 0x61, 0x7A, 0x02, 0x28, 0x2B, 0xBC, 0x46, 0x25, 0xF3, 0xFC, 0x66, 0x3D, 0x99, 0x27, 0xF9, 0x7E, 0x6B, 0x1D,
+ 0xC7, 0xF9, 0x2C, 0x5E, 0x1C, 0x87, 0xF8, 0xC0, 0x4D, 0x9A, 0xE7, 0xF8, 0xDA, 0x51, 0xB2, 0xC1, 0x68, 0xF2, 0x64, 0x1F, 0xE1, 0x50, 0xED, 0x0A, 0x04, 0x23, 0x79, 0x8A,
+ 0x7F, 0x82, 0xA3, 0x39, 0x80, 0x7F, 0x80, 0xC2, 0xB1, 0x5E, 0xF7, 0x04, 0x2F, 0xB2, 0x10, 0x02, 0x86, 0x63, 0xC9, 0xCC, 0x07, 0xBF, 0x87, 0xF8, 0x4A, 0x38, 0xAF, 0xC1,
+ 0x88, 0xF8, 0x66, 0x1F, 0xE1, 0xD9, 0x08, 0xD4, 0x8F, 0x25, 0x5B, 0x4A, 0x49, 0x97, 0x87, 0x39, 0xFE, 0x25, 0x12, 0x10, 0x68, 0xAA, 0x4A, 0x2F, 0x42, 0x29, 0x12, 0x69,
+ 0x9F, 0xE1, 0xC1, 0x00, 0x67, 0x1F, 0xE1, 0x58, 0xED, 0x00, 0x83, 0x23, 0x49, 0x82, 0x7F, 0x81, 0x21, 0xE0, 0xFC, 0x73, 0x21, 0x00, 0x50, 0x7D, 0x2B, 0x84, 0x03, 0x83,
+ 0xC2, 0x1B, 0x90, 0x06, 0x69, 0xFE, 0x23, 0x91, 0xAE, 0x50, 0x9A, 0x49, 0x32, 0xC2, 0x89, 0x30, 0xE9, 0x0A, 0xC4, 0xD9, 0xC4, 0x7F, 0x94, 0xA6, 0x51, 0xDE, 0x7F, 0x9D,
+ 0x07, 0x89, 0xF6, 0x7F, 0x91, 0x85, 0xCA, 0x88, 0x25, 0x11, 0xEE, 0x50, 0x7C, 0x43, 0x35, 0x21, 0x60, 0xF1, 0x0D, 0x82, 0x62, 0x39, 0x07, 0x2C, 0x20, 0xE0, 0x80, 0x72,
+ 0x34, 0x17, 0xA1, 0x80, 0xEE, 0xF0, 0x89, 0x24, 0x74, 0x1A, 0x2C, 0x93, 0xB3, 0x78, 0xCC, 0x52, 0x9D, 0x6A, 0x69, 0x56, 0xBB, 0x0D, 0x85, 0x69, 0xE6, 0x7F, 0x9E, 0x27,
+ 0xB9, 0xFD, 0x50, 0x54, 0x47, 0xF9, 0xCC, 0x78, 0x9F, 0x87, 0xF9, 0x98, 0x70, 0xB9, 0xC2, 0x91, 0x2C, 0x6D, 0x1F, 0xE1, 0xE1, 0x00, 0xBF, 0x02, 0xC1, 0xF5, 0x18, 0x84,
+ 0x01, 0xE1, 0x48, 0x8C, 0x42, 0x07, 0x43, 0xC9, 0x76, 0x7F, 0x8B, 0x04, 0xE4, 0xDE, 0x35, 0x95, 0xAB, 0xB0, 0xF0, 0x5C, 0x55, 0x23, 0xF9, 0x7E, 0x7E, 0x9F, 0xE4, 0x0C,
+ 0xA7, 0x55, 0x47, 0xC7, 0xF9, 0xE6, 0xCF, 0x1F, 0xE7, 0x93, 0x35, 0x52, 0x54, 0x63, 0x19, 0x46, 0x73, 0x1F, 0xE2, 0x61, 0x08, 0xF0, 0x82, 0xE1, 0x80, 0x92, 0xF9, 0x20,
+ 0xC0, 0x28, 0x18, 0x0A, 0x05, 0xA1, 0xA2, 0xF8, 0x6E, 0xDB, 0x47, 0x49, 0xFE, 0x3E, 0x17, 0xB6, 0x61, 0x13, 0x1A, 0x29, 0x26, 0xA9, 0xFE, 0x7F, 0x92, 0x70, 0x69, 0xFE,
+ 0x4C, 0x2F, 0x55, 0x01, 0xF1, 0x54, 0xD4, 0x35, 0x49, 0x4A, 0x69, 0x59, 0x83, 0x81, 0x58, 0x76, 0x9F, 0xE2, 0x20, 0xD6, 0x4C, 0x9B, 0xA0, 0x48, 0x1E, 0x0B, 0xB7, 0x48,
+ 0x58, 0x26, 0x11, 0x06, 0x42, 0xE8, 0xA4, 0x40, 0x17, 0x27, 0x39, 0x00, 0x60, 0x2D, 0xA4, 0xC3, 0x2C, 0x7F, 0x94, 0x56, 0xE4, 0xE1, 0x77, 0x1F, 0xE5, 0xB9, 0xD7, 0x66,
+ 0x1E, 0x07, 0xB3, 0x3C, 0x63, 0x1D, 0x35, 0x49, 0x0E, 0x63, 0x2D, 0xA2, 0xF1, 0x12, 0x60, 0x1C, 0xE0, 0xE0, 0x52, 0x1B, 0x8B, 0xAC, 0x38, 0x0E, 0x07, 0x03, 0x60, 0x28,
+ 0x1C, 0x0E, 0x87, 0x00, 0xF0, 0x66, 0x27, 0x11, 0xA2, 0xC1, 0x02, 0x5A, 0x1C, 0xE4, 0x21, 0x83, 0x1F, 0x13, 0x86, 0xFA, 0xD2, 0x55, 0x1D, 0xD6, 0x61, 0xBC, 0x77, 0xD3,
+ 0xE6, 0x91, 0xCB, 0x4C, 0x90, 0xA6, 0x25, 0xB8, 0x2F, 0x90, 0xC5, 0xA9, 0xCE, 0x12, 0x07, 0x02, 0x91, 0x1B, 0x9F, 0x68, 0x00, 0x16, 0x76, 0x0D, 0xA1, 0x00, 0x08, 0x06,
+ 0x03, 0x81, 0xA0, 0x20, 0x1A, 0x0D, 0x06, 0x80, 0x30, 0x24, 0x12, 0x89, 0x20, 0x98, 0x4A, 0x1F, 0x0F, 0x21, 0xA0, 0x9E, 0x36, 0x16, 0xC2, 0x88, 0xE6, 0x48, 0x9B, 0x83,
+ 0x31, 0x1C, 0x55, 0x1E, 0x43, 0x59, 0x1A, 0x56, 0x1E, 0x42, 0xF0, 0xFA, 0x4D, 0x1B, 0x9B, 0x08, 0xDC, 0x5B, 0x02, 0xA1, 0x30, 0x7E, 0x3C, 0xEE, 0x5B, 0xA6, 0xDD, 0xB8,
+ 0x6D, 0x5B, 0x62, 0xB7, 0xCD, 0xF3, 0x9C, 0xEA, 0x04, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x01,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0xE0, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x01, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x11, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x08, 0x01, 0x15, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x16, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x17,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x29, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xE8, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x03, 0xF0, 0x01, 0x1C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
+ 0x00, 0x00, 0x01, 0x52, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0A,
+ 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10, 0x00, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10 };
+
+ DEFINE_STATIC_LOCAL(RefPtr<SharedBuffer>, defaultIconBuffer, (SharedBuffer::create(defaultIconData, sizeof(defaultIconData))));
+ defaultIconRecord->setImageData(defaultIconBuffer);
+}
+#endif
+
+Image* IconDatabase::defaultIcon(const IntSize& size)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+
+ if (!m_defaultIconRecord) {
+ m_defaultIconRecord = IconRecord::create("urlIcon");
+ loadDefaultIconRecord(m_defaultIconRecord.get());
+ }
+
+ return m_defaultIconRecord->image(size);
+}
+
+
+void IconDatabase::retainIconForPageURL(const String& pageURLOriginal)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
+
+ if (!isEnabled() || pageURLOriginal.isEmpty())
+ return;
+
+ MutexLocker locker(m_urlAndIconLock);
+
+ PageURLRecord* record = m_pageURLToRecordMap.get(pageURLOriginal);
+
+ String pageURL;
+
+ if (!record) {
+ pageURL = pageURLOriginal.crossThreadString();
+
+ record = new PageURLRecord(pageURL);
+ m_pageURLToRecordMap.set(pageURL, record);
+ }
+
+ if (!record->retain()) {
+ if (pageURL.isNull())
+ pageURL = pageURLOriginal.crossThreadString();
+
+ // This page just had its retain count bumped from 0 to 1 - Record that fact
+ m_retainedPageURLs.add(pageURL);
+
+ // If we read the iconURLs yet, we want to avoid any pageURL->iconURL lookups and the pageURLsPendingDeletion is moot,
+ // so we bail here and skip those steps
+ if (!m_iconURLImportComplete)
+ return;
+
+ MutexLocker locker(m_pendingSyncLock);
+ // If this pageURL waiting to be sync'ed, update the sync record
+ // This saves us in the case where a page was ready to be deleted from the database but was just retained - so theres no need to delete it!
+ if (!m_privateBrowsingEnabled && m_pageURLsPendingSync.contains(pageURL)) {
+ LOG(IconDatabase, "Bringing %s back from the brink", pageURL.ascii().data());
+ m_pageURLsPendingSync.set(pageURL, record->snapshot());
+ }
+ }
+}
+
+void IconDatabase::releaseIconForPageURL(const String& pageURLOriginal)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
+
+ if (!isEnabled() || pageURLOriginal.isEmpty())
+ return;
+
+ MutexLocker locker(m_urlAndIconLock);
+
+ // Check if this pageURL is actually retained
+ if (!m_retainedPageURLs.contains(pageURLOriginal)) {
+ LOG_ERROR("Attempting to release icon for URL %s which is not retained", urlForLogging(pageURLOriginal).ascii().data());
+ return;
+ }
+
+ // Get its retain count - if it's retained, we'd better have a PageURLRecord for it
+ PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
+ ASSERT(pageRecord);
+ LOG(IconDatabase, "Releasing pageURL %s to a retain count of %i", urlForLogging(pageURLOriginal).ascii().data(), pageRecord->retainCount() - 1);
+ ASSERT(pageRecord->retainCount() > 0);
+
+ // If it still has a positive retain count, store the new count and bail
+ if (pageRecord->release())
+ return;
+
+ // This pageRecord has now been fully released. Do the appropriate cleanup
+ LOG(IconDatabase, "No more retainers for PageURL %s", urlForLogging(pageURLOriginal).ascii().data());
+ m_pageURLToRecordMap.remove(pageURLOriginal);
+ m_retainedPageURLs.remove(pageURLOriginal);
+
+ // Grab the iconRecord for later use (and do a sanity check on it for kicks)
+ IconRecord* iconRecord = pageRecord->iconRecord();
+
+ ASSERT(!iconRecord || (iconRecord && m_iconURLToRecordMap.get(iconRecord->iconURL()) == iconRecord));
+
+ {
+ MutexLocker locker(m_pendingReadingLock);
+
+ // Since this pageURL is going away, there's no reason anyone would ever be interested in its read results
+ if (!m_iconURLImportComplete)
+ m_pageURLsPendingImport.remove(pageURLOriginal);
+ m_pageURLsInterestedInIcons.remove(pageURLOriginal);
+
+ // If this icon is down to it's last retainer, we don't care about reading it in from disk anymore
+ if (iconRecord && iconRecord->hasOneRef()) {
+ m_iconURLToRecordMap.remove(iconRecord->iconURL());
+ m_iconsPendingReading.remove(iconRecord);
+ }
+ }
+
+ // Mark stuff for deletion from the database only if we're not in private browsing
+ if (!m_privateBrowsingEnabled) {
+ MutexLocker locker(m_pendingSyncLock);
+ m_pageURLsPendingSync.set(pageURLOriginal.crossThreadString(), pageRecord->snapshot(true));
+
+ // If this page is the last page to refer to a particular IconRecord, that IconRecord needs to
+ // be marked for deletion
+ if (iconRecord && iconRecord->hasOneRef())
+ m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
+ }
+
+ delete pageRecord;
+
+ if (isOpen())
+ scheduleOrDeferSyncTimer();
+}
+
+void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, const String& iconURLOriginal)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ // Cannot do anything with dataOriginal or iconURLOriginal that would end up storing them without deep copying first
+
+ if (!isOpen() || iconURLOriginal.isEmpty())
+ return;
+
+ RefPtr<SharedBuffer> data = dataOriginal ? dataOriginal->copy() : 0;
+ String iconURL = iconURLOriginal.crossThreadString();
+
+ Vector<String> pageURLs;
+ {
+ MutexLocker locker(m_urlAndIconLock);
+
+ // If this icon was pending a read, remove it from that set because this new data should override what is on disk
+ RefPtr<IconRecord> icon = m_iconURLToRecordMap.get(iconURL);
+ if (icon) {
+ MutexLocker locker(m_pendingReadingLock);
+ m_iconsPendingReading.remove(icon.get());
+ } else
+ icon = getOrCreateIconRecord(iconURL);
+
+ // Update the data and set the time stamp
+ icon->setImageData(data);
+ icon->setTimestamp((int)currentTime());
+
+ // Copy the current retaining pageURLs - if any - to notify them of the change
+ pageURLs.appendRange(icon->retainingPageURLs().begin(), icon->retainingPageURLs().end());
+
+ // Mark the IconRecord as requiring an update to the database only if private browsing is disabled
+ if (!m_privateBrowsingEnabled) {
+ MutexLocker locker(m_pendingSyncLock);
+ m_iconsPendingSync.set(iconURL, icon->snapshot());
+ }
+
+ if (icon->hasOneRef()) {
+ ASSERT(icon->retainingPageURLs().isEmpty());
+ LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(icon->iconURL()).ascii().data());
+ m_iconURLToRecordMap.remove(icon->iconURL());
+ }
+ }
+
+ // Send notification out regarding all PageURLs that retain this icon
+ // But not if we're on the sync thread because that implies this mapping
+ // comes from the initial import which we don't want notifications for
+ if (!IS_ICON_SYNC_THREAD()) {
+ // Start the timer to commit this change - or further delay the timer if it was already started
+ scheduleOrDeferSyncTimer();
+
+ // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
+ // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up
+ AutodrainedPool pool(25);
+
+ for (unsigned i = 0; i < pageURLs.size(); ++i) {
+ LOG(IconDatabase, "Dispatching notification that retaining pageURL %s has a new icon", urlForLogging(pageURLs[i]).ascii().data());
+ m_client->dispatchDidAddIconForPageURL(pageURLs[i]);
+
+ pool.cycle();
+ }
+ }
+}
+
+void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const String& pageURLOriginal)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ // Cannot do anything with iconURLOriginal or pageURLOriginal that would end up storing them without deep copying first
+
+ ASSERT(!iconURLOriginal.isEmpty());
+
+ if (!isOpen() || pageURLOriginal.isEmpty())
+ return;
+
+ String iconURL, pageURL;
+
+ {
+ MutexLocker locker(m_urlAndIconLock);
+
+ PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
+
+ // If the urls already map to each other, bail.
+ // This happens surprisingly often, and seems to cream iBench performance
+ if (pageRecord && pageRecord->iconRecord() && pageRecord->iconRecord()->iconURL() == iconURLOriginal)
+ return;
+
+ pageURL = pageURLOriginal.crossThreadString();
+ iconURL = iconURLOriginal.crossThreadString();
+
+ if (!pageRecord) {
+ pageRecord = new PageURLRecord(pageURL);
+ m_pageURLToRecordMap.set(pageURL, pageRecord);
+ }
+
+ RefPtr<IconRecord> iconRecord = pageRecord->iconRecord();
+
+ // Otherwise, set the new icon record for this page
+ pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
+
+ // If the current icon has only a single ref left, it is about to get wiped out.
+ // Remove it from the in-memory records and don't bother reading it in from disk anymore
+ if (iconRecord && iconRecord->hasOneRef()) {
+ ASSERT(iconRecord->retainingPageURLs().size() == 0);
+ LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(iconRecord->iconURL()).ascii().data());
+ m_iconURLToRecordMap.remove(iconRecord->iconURL());
+ MutexLocker locker(m_pendingReadingLock);
+ m_iconsPendingReading.remove(iconRecord.get());
+ }
+
+ // And mark this mapping to be added to the database
+ if (!m_privateBrowsingEnabled) {
+ MutexLocker locker(m_pendingSyncLock);
+ m_pageURLsPendingSync.set(pageURL, pageRecord->snapshot());
+
+ // If the icon is on its last ref, mark it for deletion
+ if (iconRecord && iconRecord->hasOneRef())
+ m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
+ }
+ }
+
+ // Since this mapping is new, send the notification out - but not if we're on the sync thread because that implies this mapping
+ // comes from the initial import which we don't want notifications for
+ if (!IS_ICON_SYNC_THREAD()) {
+ // Start the timer to commit this change - or further delay the timer if it was already started
+ scheduleOrDeferSyncTimer();
+
+ LOG(IconDatabase, "Dispatching notification that we changed an icon mapping for url %s", urlForLogging(pageURL).ascii().data());
+ AutodrainedPool pool;
+ m_client->dispatchDidAddIconForPageURL(pageURL);
+ }
+}
+
+IconLoadDecision IconDatabase::loadDecisionForIconURL(const String& iconURL, DocumentLoader* notificationDocumentLoader)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ if (!isOpen() || iconURL.isEmpty())
+ return IconLoadNo;
+
+ // If we have a IconRecord, it should also have its timeStamp marked because there is only two times when we create the IconRecord:
+ // 1 - When we read the icon urls from disk, getting the timeStamp at the same time
+ // 2 - When we get a new icon from the loader, in which case the timestamp is set at that time
+ {
+ MutexLocker locker(m_urlAndIconLock);
+ if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL)) {
+ LOG(IconDatabase, "Found expiration time on a present icon based on existing IconRecord");
+ return (int)currentTime() - icon->getTimestamp() > iconExpirationTime ? IconLoadYes : IconLoadNo;
+ }
+ }
+
+ // If we don't have a record for it, but we *have* imported all iconURLs from disk, then we should load it now
+ MutexLocker readingLocker(m_pendingReadingLock);
+ if (m_iconURLImportComplete)
+ return IconLoadYes;
+
+ // Otherwise - since we refuse to perform I/O on the main thread to find out for sure - we return the answer that says
+ // "You might be asked to load this later, so flag that"
+ LOG(IconDatabase, "Don't know if we should load %s or not - adding %p to the set of document loaders waiting on a decision", iconURL.ascii().data(), notificationDocumentLoader);
+ m_loadersPendingDecision.add(notificationDocumentLoader);
+
+ return IconLoadUnknown;
+}
+
+bool IconDatabase::iconDataKnownForIconURL(const String& iconURL)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ MutexLocker locker(m_urlAndIconLock);
+ if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
+ return icon->imageDataStatus() != ImageDataStatusUnknown;
+
+ return false;
+}
+
+void IconDatabase::setEnabled(bool enabled)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ if (!enabled && isOpen())
+ close();
+ m_isEnabled = enabled;
+}
+
+bool IconDatabase::isEnabled() const
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ return m_isEnabled;
+}
+
+void IconDatabase::setPrivateBrowsingEnabled(bool flag)
+{
+ m_privateBrowsingEnabled = flag;
+}
+
+bool IconDatabase::isPrivateBrowsingEnabled() const
+{
+ return m_privateBrowsingEnabled;
+}
+
+void IconDatabase::delayDatabaseCleanup()
+{
+ ++databaseCleanupCounter;
+ if (databaseCleanupCounter == 1)
+ LOG(IconDatabase, "Database cleanup is now DISABLED");
+}
+
+void IconDatabase::allowDatabaseCleanup()
+{
+ if (--databaseCleanupCounter < 0)
+ databaseCleanupCounter = 0;
+ if (databaseCleanupCounter == 0)
+ LOG(IconDatabase, "Database cleanup is now ENABLED");
+}
+
+void IconDatabase::checkIntegrityBeforeOpening()
+{
+ checkIntegrityOnOpen = true;
+}
+
+size_t IconDatabase::pageURLMappingCount()
+{
+ MutexLocker locker(m_urlAndIconLock);
+ return m_pageURLToRecordMap.size();
+}
+
+size_t IconDatabase::retainedPageURLCount()
+{
+ MutexLocker locker(m_urlAndIconLock);
+ return m_retainedPageURLs.size();
+}
+
+size_t IconDatabase::iconRecordCount()
+{
+ MutexLocker locker(m_urlAndIconLock);
+ return m_iconURLToRecordMap.size();
+}
+
+size_t IconDatabase::iconRecordCountWithData()
+{
+ MutexLocker locker(m_urlAndIconLock);
+ size_t result = 0;
+
+ HashMap<String, IconRecord*>::iterator i = m_iconURLToRecordMap.begin();
+ HashMap<String, IconRecord*>::iterator end = m_iconURLToRecordMap.end();
+
+ for (; i != end; ++i)
+ result += ((*i).second->imageDataStatus() == ImageDataStatusPresent);
+
+ return result;
+}
+
+IconDatabase::IconDatabase()
+ : m_syncTimer(this, &IconDatabase::syncTimerFired)
+ , m_syncThreadRunning(false)
+ , m_isEnabled(false)
+ , m_privateBrowsingEnabled(false)
+ , m_threadTerminationRequested(false)
+ , m_removeIconsRequested(false)
+ , m_iconURLImportComplete(false)
+ , m_disabledSuddenTerminationForSyncThread(false)
+ , m_initialPruningComplete(false)
+ , m_client(defaultClient())
+ , m_imported(false)
+ , m_isImportedSet(false)
+{
+ ASSERT(isMainThread());
+}
+
+IconDatabase::~IconDatabase()
+{
+ ASSERT_NOT_REACHED();
+}
+
+void IconDatabase::notifyPendingLoadDecisionsOnMainThread(void* context)
+{
+ static_cast<IconDatabase*>(context)->notifyPendingLoadDecisions();
+}
+
+void IconDatabase::notifyPendingLoadDecisions()
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ // This method should only be called upon completion of the initial url import from the database
+ ASSERT(m_iconURLImportComplete);
+ LOG(IconDatabase, "Notifying all DocumentLoaders that were waiting on a load decision for thier icons");
+
+ HashSet<RefPtr<DocumentLoader> >::iterator i = m_loadersPendingDecision.begin();
+ HashSet<RefPtr<DocumentLoader> >::iterator end = m_loadersPendingDecision.end();
+
+ for (; i != end; ++i)
+ if ((*i)->refCount() > 1)
+ (*i)->iconLoadDecisionAvailable();
+
+ m_loadersPendingDecision.clear();
+}
+
+void IconDatabase::wakeSyncThread()
+{
+ 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();
+}
+
+void IconDatabase::scheduleOrDeferSyncTimer()
+{
+ ASSERT_NOT_SYNC_THREAD();
+
+ if (!m_syncTimer.isActive()) {
+ // The following is balanced by the call to enableSuddenTermination in the
+ // syncTimerFired function.
+ disableSuddenTermination();
+ }
+
+ m_syncTimer.startOneShot(updateTimerDelay);
+}
+
+void IconDatabase::syncTimerFired(Timer<IconDatabase>*)
+{
+ ASSERT_NOT_SYNC_THREAD();
+ wakeSyncThread();
+
+ // The following is balanced by the call to disableSuddenTermination in the
+ // scheduleOrDeferSyncTimer function.
+ enableSuddenTermination();
+}
+
+// ******************
+// *** Any Thread ***
+// ******************
+
+bool IconDatabase::isOpen() const
+{
+ MutexLocker locker(m_syncLock);
+ return m_syncDB.isOpen();
+}
+
+String IconDatabase::databasePath() const
+{
+ MutexLocker locker(m_syncLock);
+ return m_completeDatabasePath.threadsafeCopy();
+}
+
+String IconDatabase::defaultDatabaseFilename()
+{
+ DEFINE_STATIC_LOCAL(String, defaultDatabaseFilename, ("WebpageIcons.db"));
+ return defaultDatabaseFilename.threadsafeCopy();
+}
+
+// Unlike getOrCreatePageURLRecord(), getOrCreateIconRecord() does not mark the icon as "interested in import"
+PassRefPtr<IconRecord> IconDatabase::getOrCreateIconRecord(const String& iconURL)
+{
+ // Clients of getOrCreateIconRecord() are required to acquire the m_urlAndIconLock before calling this method
+ ASSERT(!m_urlAndIconLock.tryLock());
+
+ if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
+ return icon;
+
+ RefPtr<IconRecord> newIcon = IconRecord::create(iconURL);
+ m_iconURLToRecordMap.set(iconURL, newIcon.get());
+
+ return newIcon.release();
+}
+
+// This method retrieves the existing PageURLRecord, or creates a new one and marks it as "interested in the import" for later notification
+PageURLRecord* IconDatabase::getOrCreatePageURLRecord(const String& pageURL)
+{
+ // Clients of getOrCreatePageURLRecord() are required to acquire the m_urlAndIconLock before calling this method
+ ASSERT(!m_urlAndIconLock.tryLock());
+
+ if (pageURL.isEmpty())
+ return 0;
+
+ PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
+
+ MutexLocker locker(m_pendingReadingLock);
+ if (!m_iconURLImportComplete) {
+ // If the initial import of all URLs hasn't completed and we have no page record, we assume we *might* know about this later and create a record for it
+ if (!pageRecord) {
+ LOG(IconDatabase, "Creating new PageURLRecord for pageURL %s", urlForLogging(pageURL).ascii().data());
+ pageRecord = new PageURLRecord(pageURL);
+ m_pageURLToRecordMap.set(pageURL, pageRecord);
+ }
+
+ // If the pageRecord for this page does not have an iconRecord attached to it, then it is a new pageRecord still awaiting the initial import
+ // Mark the URL as "interested in the result of the import" then bail
+ if (!pageRecord->iconRecord()) {
+ m_pageURLsPendingImport.add(pageURL);
+ return 0;
+ }
+ }
+
+ // We've done the initial import of all URLs known in the database. If this record doesn't exist now, it never will
+ return pageRecord;
+}
+
+
+// ************************
+// *** Sync Thread Only ***
+// ************************
+
+void IconDatabase::importIconURLForPageURL(const String& iconURL, const String& pageURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ // This function is only for setting actual existing url mappings so assert that neither of these URLs are empty
+ ASSERT(!iconURL.isEmpty() && !pageURL.isEmpty());
+
+ setIconURLForPageURLInSQLDatabase(iconURL, pageURL);
+}
+
+void IconDatabase::importIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String& iconURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ ASSERT(!iconURL.isEmpty());
+
+ writeIconSnapshotToSQLDatabase(IconSnapshot(iconURL, (int)currentTime(), data.get()));
+}
+
+bool IconDatabase::shouldStopThreadActivity() const
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ return m_threadTerminationRequested || m_removeIconsRequested;
+}
+
+void* IconDatabase::iconDatabaseSyncThreadStart(void* vIconDatabase)
+{
+ IconDatabase* iconDB = static_cast<IconDatabase*>(vIconDatabase);
+
+ return iconDB->iconDatabaseSyncThread();
+}
+
+void* IconDatabase::iconDatabaseSyncThread()
+{
+ // The call to create this thread might not complete before the thread actually starts, so we might fail this ASSERT_ICON_SYNC_THREAD() because the pointer
+ // to our thread structure hasn't been filled in yet.
+ // To fix this, the main thread acquires this lock before creating us, then releases the lock after creation is complete. A quick lock/unlock cycle here will
+ // prevent us from running before that call completes
+ m_syncLock.lock();
+ m_syncLock.unlock();
+
+ ASSERT_ICON_SYNC_THREAD();
+
+ LOG(IconDatabase, "(THREAD) IconDatabase sync thread started");
+
+#ifndef NDEBUG
+ double startTime = currentTime();
+#endif
+
+ // Need to create the database path if it doesn't already exist
+ makeAllDirectories(m_databaseDirectory);
+
+ // Existence of a journal file is evidence of a previous crash/force quit and automatically qualifies
+ // us to do an integrity check
+ String journalFilename = m_completeDatabasePath + "-journal";
+ if (!checkIntegrityOnOpen) {
+ AutodrainedPool pool;
+ checkIntegrityOnOpen = fileExists(journalFilename);
+ }
+
+ {
+ MutexLocker locker(m_syncLock);
+ if (!m_syncDB.open(m_completeDatabasePath)) {
+ LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg());
+ return 0;
+ }
+ }
+
+ if (shouldStopThreadActivity())
+ return syncThreadMainLoop();
+
+#ifndef NDEBUG
+ double timeStamp = currentTime();
+ LOG(IconDatabase, "(THREAD) Open took %.4f seconds", timeStamp - startTime);
+#endif
+
+ performOpenInitialization();
+ if (shouldStopThreadActivity())
+ return syncThreadMainLoop();
+
+#ifndef NDEBUG
+ double newStamp = currentTime();
+ LOG(IconDatabase, "(THREAD) performOpenInitialization() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
+ timeStamp = newStamp;
+#endif
+
+ if (!imported()) {
+ LOG(IconDatabase, "(THREAD) Performing Safari2 import procedure");
+ SQLiteTransaction importTransaction(m_syncDB);
+ importTransaction.begin();
+
+ // Commit the transaction only if the import completes (the import should be atomic)
+ if (m_client->performImport()) {
+ setImported(true);
+ importTransaction.commit();
+ } else {
+ LOG(IconDatabase, "(THREAD) Safari 2 import was cancelled");
+ importTransaction.rollback();
+ }
+
+ if (shouldStopThreadActivity())
+ return syncThreadMainLoop();
+
+#ifndef NDEBUG
+ newStamp = currentTime();
+ LOG(IconDatabase, "(THREAD) performImport() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
+ timeStamp = newStamp;
+#endif
+ }
+
+ // Uncomment the following line to simulate a long lasting URL import (*HUGE* icon databases, or network home directories)
+ // while (currentTime() - timeStamp < 10);
+
+ // Read in URL mappings from the database
+ LOG(IconDatabase, "(THREAD) Starting iconURL import");
+ performURLImport();
+
+ if (shouldStopThreadActivity())
+ return syncThreadMainLoop();
+
+#ifndef NDEBUG
+ newStamp = currentTime();
+ LOG(IconDatabase, "(THREAD) performURLImport() took %.4f seconds. Entering main loop %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
+#endif
+
+ LOG(IconDatabase, "(THREAD) Beginning sync");
+ return syncThreadMainLoop();
+}
+
+static int databaseVersionNumber(SQLiteDatabase& db)
+{
+ return SQLiteStatement(db, "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';").getColumnInt(0);
+}
+
+static bool isValidDatabase(SQLiteDatabase& db)
+{
+
+ // These four tables should always exist in a valid db
+ if (!db.tableExists("IconInfo") || !db.tableExists("IconData") || !db.tableExists("PageURL") || !db.tableExists("IconDatabaseInfo"))
+ return false;
+
+ if (databaseVersionNumber(db) < currentDatabaseVersion) {
+ LOG(IconDatabase, "DB version is not found or below expected valid version");
+ return false;
+ }
+
+ return true;
+}
+
+static void createDatabaseTables(SQLiteDatabase& db)
+{
+ if (!db.executeCommand("CREATE TABLE PageURL (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID INTEGER NOT NULL ON CONFLICT FAIL);")) {
+ LOG_ERROR("Could not create PageURL table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+ db.close();
+ return;
+ }
+ if (!db.executeCommand("CREATE INDEX PageURLIndex ON PageURL (url);")) {
+ LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+ db.close();
+ return;
+ }
+ if (!db.executeCommand("CREATE TABLE IconInfo (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, stamp INTEGER);")) {
+ LOG_ERROR("Could not create IconInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+ db.close();
+ return;
+ }
+ if (!db.executeCommand("CREATE INDEX IconInfoIndex ON IconInfo (url, iconID);")) {
+ LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+ db.close();
+ return;
+ }
+ if (!db.executeCommand("CREATE TABLE IconData (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, data BLOB);")) {
+ LOG_ERROR("Could not create IconData table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+ db.close();
+ return;
+ }
+ if (!db.executeCommand("CREATE INDEX IconDataIndex ON IconData (iconID);")) {
+ LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+ db.close();
+ return;
+ }
+ if (!db.executeCommand("CREATE TABLE IconDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
+ LOG_ERROR("Could not create IconDatabaseInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+ db.close();
+ return;
+ }
+ if (!db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) {
+ LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", db.lastError(), db.lastErrorMsg());
+ db.close();
+ return;
+ }
+}
+
+void IconDatabase::performOpenInitialization()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ if (!isOpen())
+ return;
+
+ if (checkIntegrityOnOpen) {
+ checkIntegrityOnOpen = false;
+ if (!checkIntegrity()) {
+ LOG(IconDatabase, "Integrity check was bad - dumping IconDatabase");
+
+ m_syncDB.close();
+
+ {
+ MutexLocker locker(m_syncLock);
+ // Should've been consumed by SQLite, delete just to make sure we don't see it again in the future;
+ deleteFile(m_completeDatabasePath + "-journal");
+ deleteFile(m_completeDatabasePath);
+ }
+
+ // Reopen the write database, creating it from scratch
+ if (!m_syncDB.open(m_completeDatabasePath)) {
+ LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg());
+ return;
+ }
+ }
+ }
+
+ int version = databaseVersionNumber(m_syncDB);
+
+ if (version > currentDatabaseVersion) {
+ LOG(IconDatabase, "Database version number %i is greater than our current version number %i - closing the database to prevent overwriting newer versions", version, currentDatabaseVersion);
+ m_syncDB.close();
+ m_threadTerminationRequested = true;
+ return;
+ }
+
+ if (!isValidDatabase(m_syncDB)) {
+ LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", m_completeDatabasePath.ascii().data());
+ m_syncDB.clearAllTables();
+ createDatabaseTables(m_syncDB);
+ }
+
+ // Reduce sqlite RAM cache size from default 2000 pages (~1.5kB per page). 3MB of cache for icon database is overkill
+ if (!SQLiteStatement(m_syncDB, "PRAGMA cache_size = 200;").executeCommand())
+ LOG_ERROR("SQLite database could not set cache_size");
+}
+
+bool IconDatabase::checkIntegrity()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ SQLiteStatement integrity(m_syncDB, "PRAGMA integrity_check;");
+ if (integrity.prepare() != SQLResultOk) {
+ LOG_ERROR("checkIntegrity failed to execute");
+ return false;
+ }
+
+ int resultCode = integrity.step();
+ if (resultCode == SQLResultOk)
+ return true;
+
+ if (resultCode != SQLResultRow)
+ return false;
+
+ int columns = integrity.columnCount();
+ if (columns != 1) {
+ LOG_ERROR("Received %i columns performing integrity check, should be 1", columns);
+ return false;
+ }
+
+ String resultText = integrity.getColumnText(0);
+
+ // A successful, no-error integrity check will be "ok" - all other strings imply failure
+ if (resultText == "ok")
+ return true;
+
+ LOG_ERROR("Icon database integrity check failed - \n%s", resultText.ascii().data());
+ return false;
+}
+
+void IconDatabase::performURLImport()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ SQLiteStatement query(m_syncDB, "SELECT PageURL.url, IconInfo.url, IconInfo.stamp FROM PageURL INNER JOIN IconInfo ON PageURL.iconID=IconInfo.iconID;");
+
+ if (query.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare icon url import query");
+ return;
+ }
+
+ // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
+ // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up
+ AutodrainedPool pool(25);
+
+ int result = query.step();
+ while (result == SQLResultRow) {
+ String pageURL = query.getColumnText(0);
+ String iconURL = query.getColumnText(1);
+
+ {
+ MutexLocker locker(m_urlAndIconLock);
+
+ PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
+
+ // If the pageRecord doesn't exist in this map, then no one has retained this pageURL
+ // If the s_databaseCleanupCounter count is non-zero, then we're not supposed to be pruning the database in any manner,
+ // so go ahead and actually create a pageURLRecord for this url even though it's not retained.
+ // If database cleanup *is* allowed, we don't want to bother pulling in a page url from disk that noone is actually interested
+ // in - we'll prune it later instead!
+ if (!pageRecord && databaseCleanupCounter && !pageURL.isEmpty()) {
+ pageRecord = new PageURLRecord(pageURL);
+ m_pageURLToRecordMap.set(pageURL, pageRecord);
+ }
+
+ if (pageRecord) {
+ IconRecord* currentIcon = pageRecord->iconRecord();
+
+ if (!currentIcon || currentIcon->iconURL() != iconURL) {
+ pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
+ currentIcon = pageRecord->iconRecord();
+ }
+
+ // Regardless, the time stamp from disk still takes precedence. Until we read this icon from disk, we didn't think we'd seen it before
+ // so we marked the timestamp as "now", but it's really much older
+ currentIcon->setTimestamp(query.getColumnInt(2));
+ }
+ }
+
+ // FIXME: Currently the WebKit API supports 1 type of notification that is sent whenever we get an Icon URL for a Page URL. We might want to re-purpose it to work for
+ // getting the actually icon itself also (so each pageurl would get this notification twice) or we might want to add a second type of notification -
+ // one for the URL and one for the Image itself
+ // Note that WebIconDatabase is not neccessarily API so we might be able to make this change
+ {
+ MutexLocker locker(m_pendingReadingLock);
+ if (m_pageURLsPendingImport.contains(pageURL)) {
+ m_client->dispatchDidAddIconForPageURL(pageURL);
+ m_pageURLsPendingImport.remove(pageURL);
+
+ pool.cycle();
+ }
+ }
+
+ // Stop the import at any time of the thread has been asked to shutdown
+ if (shouldStopThreadActivity()) {
+ LOG(IconDatabase, "IconDatabase asked to terminate during performURLImport()");
+ return;
+ }
+
+ result = query.step();
+ }
+
+ if (result != SQLResultDone)
+ LOG(IconDatabase, "Error reading page->icon url mappings from database");
+
+ // Clear the m_pageURLsPendingImport set - either the page URLs ended up with an iconURL (that we'll notify about) or not,
+ // but after m_iconURLImportComplete is set to true, we don't care about this set anymore
+ Vector<String> urls;
+ {
+ MutexLocker locker(m_pendingReadingLock);
+
+ urls.appendRange(m_pageURLsPendingImport.begin(), m_pageURLsPendingImport.end());
+ m_pageURLsPendingImport.clear();
+ m_iconURLImportComplete = true;
+ }
+
+ Vector<String> urlsToNotify;
+
+ // Loop through the urls pending import
+ // Remove unretained ones if database cleanup is allowed
+ // Keep a set of ones that are retained and pending notification
+
+ {
+ MutexLocker locker(m_urlAndIconLock);
+
+ for (unsigned i = 0; i < urls.size(); ++i) {
+ if (!m_retainedPageURLs.contains(urls[i])) {
+ PageURLRecord* record = m_pageURLToRecordMap.get(urls[i]);
+ if (record && !databaseCleanupCounter) {
+ m_pageURLToRecordMap.remove(urls[i]);
+ IconRecord* iconRecord = record->iconRecord();
+
+ // If this page is the only remaining retainer of its icon, mark that icon for deletion and don't bother
+ // reading anything related to it
+ if (iconRecord && iconRecord->hasOneRef()) {
+ m_iconURLToRecordMap.remove(iconRecord->iconURL());
+
+ {
+ MutexLocker locker(m_pendingReadingLock);
+ m_pageURLsInterestedInIcons.remove(urls[i]);
+ m_iconsPendingReading.remove(iconRecord);
+ }
+ {
+ MutexLocker locker(m_pendingSyncLock);
+ m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
+ }
+ }
+
+ delete record;
+ }
+ } else {
+ urlsToNotify.append(urls[i]);
+ }
+ }
+ }
+
+ 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());
+ m_client->dispatchDidAddIconForPageURL(urlsToNotify[i]);
+ if (shouldStopThreadActivity())
+ return;
+
+ pool.cycle();
+ }
+
+ // Notify all DocumentLoaders that were waiting for an icon load decision on the main thread
+ callOnMainThread(notifyPendingLoadDecisionsOnMainThread, this);
+}
+
+void* IconDatabase::syncThreadMainLoop()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ bool shouldReenableSuddenTermination = false;
+
+ m_syncLock.lock();
+
+ // It's possible thread termination is requested before the main loop even starts - in that case, just skip straight to cleanup
+ while (!m_threadTerminationRequested) {
+ m_syncLock.unlock();
+
+#ifndef NDEBUG
+ double timeStamp = currentTime();
+#endif
+ LOG(IconDatabase, "(THREAD) Main work loop starting");
+
+ // If we should remove all icons, do it now. This is an uninteruptible procedure that we will always do before quitting if it is requested
+ if (m_removeIconsRequested) {
+ removeAllIconsOnThread();
+ m_removeIconsRequested = false;
+ }
+
+ // Then, if the thread should be quitting, quit now!
+ if (m_threadTerminationRequested)
+ break;
+
+ bool didAnyWork = true;
+ while (didAnyWork) {
+ bool didWrite = writeToDatabase();
+ if (shouldStopThreadActivity())
+ break;
+
+ didAnyWork = readFromDatabase();
+ if (shouldStopThreadActivity())
+ break;
+
+ // Prune unretained icons after the first time we sync anything out to the database
+ // This way, pruning won't be the only operation we perform to the database by itself
+ // We also don't want to bother doing this if the thread should be terminating (the user is quitting)
+ // or if private browsing is enabled
+ // We also don't want to prune if the m_databaseCleanupCounter count is non-zero - that means someone
+ // has asked to delay pruning
+ static bool prunedUnretainedIcons = false;
+ if (didWrite && !m_privateBrowsingEnabled && !prunedUnretainedIcons && !databaseCleanupCounter) {
+#ifndef NDEBUG
+ double time = currentTime();
+#endif
+ LOG(IconDatabase, "(THREAD) Starting pruneUnretainedIcons()");
+
+ pruneUnretainedIcons();
+
+ LOG(IconDatabase, "(THREAD) pruneUnretainedIcons() took %.4f seconds", currentTime() - time);
+
+ // If pruneUnretainedIcons() returned early due to requested thread termination, its still okay
+ // to mark prunedUnretainedIcons true because we're about to terminate anyway
+ prunedUnretainedIcons = true;
+ }
+
+ didAnyWork = didAnyWork || didWrite;
+ if (shouldStopThreadActivity())
+ break;
+ }
+
+#ifndef NDEBUG
+ double newstamp = currentTime();
+ LOG(IconDatabase, "(THREAD) Main work loop ran for %.4f seconds, %s requested to terminate", newstamp - timeStamp, shouldStopThreadActivity() ? "was" : "was not");
+#endif
+
+ m_syncLock.lock();
+
+ // There is some condition that is asking us to stop what we're doing now and handle a special case
+ // This is either removing all icons, or shutting down the thread to quit the app
+ // We handle those at the top of this main loop so continue to jump back up there
+ if (shouldStopThreadActivity())
+ continue;
+
+ if (shouldReenableSuddenTermination) {
+ // The following is balanced by the call to disableSuddenTermination in the
+ // wakeSyncThread function. Any time we wait on the condition, we also have
+ // to enableSuddenTermation, after doing the next batch of work.
+ ASSERT(m_disabledSuddenTerminationForSyncThread);
+ enableSuddenTermination();
+ m_disabledSuddenTerminationForSyncThread = false;
+ }
+
+ m_syncCondition.wait(m_syncLock);
+
+ shouldReenableSuddenTermination = true;
+ }
+
+ m_syncLock.unlock();
+
+ // Thread is terminating at this point
+ cleanupSyncThread();
+
+ if (shouldReenableSuddenTermination) {
+ // The following is balanced by the call to disableSuddenTermination in the
+ // wakeSyncThread function. Any time we wait on the condition, we also have
+ // to enableSuddenTermation, after doing the next batch of work.
+ ASSERT(m_disabledSuddenTerminationForSyncThread);
+ enableSuddenTermination();
+ m_disabledSuddenTerminationForSyncThread = false;
+ }
+
+ return 0;
+}
+
+bool IconDatabase::readFromDatabase()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+#ifndef NDEBUG
+ double timeStamp = currentTime();
+#endif
+
+ bool didAnyWork = false;
+
+ // We'll make a copy of the sets of things that need to be read. Then we'll verify at the time of updating the record that it still wants to be updated
+ // This way we won't hold the lock for a long period of time
+ Vector<IconRecord*> icons;
+ {
+ MutexLocker locker(m_pendingReadingLock);
+ icons.appendRange(m_iconsPendingReading.begin(), m_iconsPendingReading.end());
+ }
+
+ // Keep track of icons we actually read to notify them of the new icon
+ HashSet<String> urlsToNotify;
+
+ for (unsigned i = 0; i < icons.size(); ++i) {
+ didAnyWork = true;
+ RefPtr<SharedBuffer> imageData = getImageDataForIconURLFromSQLDatabase(icons[i]->iconURL());
+
+ // Verify this icon still wants to be read from disk
+ {
+ MutexLocker urlLocker(m_urlAndIconLock);
+ {
+ MutexLocker readLocker(m_pendingReadingLock);
+
+ if (m_iconsPendingReading.contains(icons[i])) {
+ // Set the new data
+ icons[i]->setImageData(imageData.get());
+
+ // Remove this icon from the set that needs to be read
+ m_iconsPendingReading.remove(icons[i]);
+
+ // We have a set of all Page URLs that retain this icon as well as all PageURLs waiting for an icon
+ // We want to find the intersection of these two sets to notify them
+ // Check the sizes of these two sets to minimize the number of iterations
+ const HashSet<String>* outerHash;
+ const HashSet<String>* innerHash;
+
+ if (icons[i]->retainingPageURLs().size() > m_pageURLsInterestedInIcons.size()) {
+ outerHash = &m_pageURLsInterestedInIcons;
+ innerHash = &(icons[i]->retainingPageURLs());
+ } else {
+ innerHash = &m_pageURLsInterestedInIcons;
+ outerHash = &(icons[i]->retainingPageURLs());
+ }
+
+ HashSet<String>::const_iterator iter = outerHash->begin();
+ HashSet<String>::const_iterator end = outerHash->end();
+ for (; iter != end; ++iter) {
+ if (innerHash->contains(*iter)) {
+ LOG(IconDatabase, "%s is interesting in the icon we just read. Adding it to the list and removing it from the interested set", urlForLogging(*iter).ascii().data());
+ urlsToNotify.add(*iter);
+ }
+
+ // If we ever get to the point were we've seen every url interested in this icon, break early
+ if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size())
+ break;
+ }
+
+ // We don't need to notify a PageURL twice, so all the ones we're about to notify can be removed from the interested set
+ if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size())
+ m_pageURLsInterestedInIcons.clear();
+ else {
+ iter = urlsToNotify.begin();
+ end = urlsToNotify.end();
+ for (; iter != end; ++iter)
+ m_pageURLsInterestedInIcons.remove(*iter);
+ }
+ }
+ }
+ }
+
+ if (shouldStopThreadActivity())
+ return didAnyWork;
+
+ // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
+ // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up
+ AutodrainedPool pool(25);
+
+ // Now that we don't hold any locks, perform the actual notifications
+ HashSet<String>::iterator iter = urlsToNotify.begin();
+ HashSet<String>::iterator end = urlsToNotify.end();
+ for (unsigned iteration = 0; iter != end; ++iter, ++iteration) {
+ LOG(IconDatabase, "Notifying icon received for pageURL %s", urlForLogging(*iter).ascii().data());
+ m_client->dispatchDidAddIconForPageURL(*iter);
+ if (shouldStopThreadActivity())
+ return didAnyWork;
+
+ pool.cycle();
+ }
+
+ LOG(IconDatabase, "Done notifying %i pageURLs who just received their icons", urlsToNotify.size());
+ urlsToNotify.clear();
+
+ if (shouldStopThreadActivity())
+ return didAnyWork;
+ }
+
+ LOG(IconDatabase, "Reading from database took %.4f seconds", currentTime() - timeStamp);
+
+ return didAnyWork;
+}
+
+bool IconDatabase::writeToDatabase()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+#ifndef NDEBUG
+ double timeStamp = currentTime();
+#endif
+
+ bool didAnyWork = false;
+
+ // We can copy the current work queue then clear it out - If any new work comes in while we're writing out,
+ // we'll pick it up on the next pass. This greatly simplifies the locking strategy for this method and remains cohesive with changes
+ // asked for by the database on the main thread
+ Vector<IconSnapshot> iconSnapshots;
+ Vector<PageURLSnapshot> pageSnapshots;
+ {
+ MutexLocker locker(m_pendingSyncLock);
+
+ iconSnapshots.appendRange(m_iconsPendingSync.begin().values(), m_iconsPendingSync.end().values());
+ m_iconsPendingSync.clear();
+
+ pageSnapshots.appendRange(m_pageURLsPendingSync.begin().values(), m_pageURLsPendingSync.end().values());
+ m_pageURLsPendingSync.clear();
+ }
+
+ if (iconSnapshots.size() || pageSnapshots.size())
+ didAnyWork = true;
+
+ SQLiteTransaction syncTransaction(m_syncDB);
+ syncTransaction.begin();
+
+ for (unsigned i = 0; i < iconSnapshots.size(); ++i) {
+ writeIconSnapshotToSQLDatabase(iconSnapshots[i]);
+ LOG(IconDatabase, "Wrote IconRecord for IconURL %s with timeStamp of %i to the DB", urlForLogging(iconSnapshots[i].iconURL).ascii().data(), iconSnapshots[i].timestamp);
+ }
+
+ for (unsigned i = 0; i < pageSnapshots.size(); ++i) {
+ // If the icon URL is empty, this page is meant to be deleted
+ // ASSERTs are sanity checks to make sure the mappings exist if they should and don't if they shouldn't
+ if (pageSnapshots[i].iconURL.isEmpty())
+ removePageURLFromSQLDatabase(pageSnapshots[i].pageURL);
+ else
+ setIconURLForPageURLInSQLDatabase(pageSnapshots[i].iconURL, pageSnapshots[i].pageURL);
+ LOG(IconDatabase, "Committed IconURL for PageURL %s to database", urlForLogging(pageSnapshots[i].pageURL).ascii().data());
+ }
+
+ syncTransaction.commit();
+
+ // Check to make sure there are no dangling PageURLs - If there are, we want to output one log message but not spam the console potentially every few seconds
+ if (didAnyWork)
+ checkForDanglingPageURLs(false);
+
+ LOG(IconDatabase, "Updating the database took %.4f seconds", currentTime() - timeStamp);
+
+ return didAnyWork;
+}
+
+void IconDatabase::pruneUnretainedIcons()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ if (!isOpen())
+ return;
+
+ // This method should only be called once per run
+ ASSERT(!m_initialPruningComplete);
+
+ // This method relies on having read in all page URLs from the database earlier.
+ ASSERT(m_iconURLImportComplete);
+
+ // Get the known PageURLs from the db, and record the ID of any that are not in the retain count set.
+ Vector<int64_t> pageIDsToDelete;
+
+ SQLiteStatement pageSQL(m_syncDB, "SELECT rowid, url FROM PageURL;");
+ pageSQL.prepare();
+
+ int result;
+ while ((result = pageSQL.step()) == SQLResultRow) {
+ MutexLocker locker(m_urlAndIconLock);
+ if (!m_pageURLToRecordMap.contains(pageSQL.getColumnText(1)))
+ pageIDsToDelete.append(pageSQL.getColumnInt64(0));
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Error reading PageURL table from on-disk DB");
+ pageSQL.finalize();
+
+ // Delete page URLs that were in the table, but not in our retain count set.
+ size_t numToDelete = pageIDsToDelete.size();
+ if (numToDelete) {
+ SQLiteTransaction pruningTransaction(m_syncDB);
+ pruningTransaction.begin();
+
+ SQLiteStatement pageDeleteSQL(m_syncDB, "DELETE FROM PageURL WHERE rowid = (?);");
+ pageDeleteSQL.prepare();
+ for (size_t i = 0; i < numToDelete; ++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)
+#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
+ // finish the rest later (hopefully)
+ if (shouldStopThreadActivity()) {
+ pruningTransaction.commit();
+ return;
+ }
+ }
+ pruningTransaction.commit();
+ pageDeleteSQL.finalize();
+ }
+
+ // Deleting unreferenced icons from the Icon tables has to be atomic -
+ // If the user quits while these are taking place, they might have to wait. Thankfully this will rarely be an issue
+ // A user on a network home directory with a wildly inconsistent database might see quite a pause...
+
+ SQLiteTransaction pruningTransaction(m_syncDB);
+ pruningTransaction.begin();
+
+ // Wipe Icons that aren't retained
+ if (!m_syncDB.executeCommand("DELETE FROM IconData WHERE iconID NOT IN (SELECT iconID FROM PageURL);"))
+ LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk IconData table");
+ if (!m_syncDB.executeCommand("DELETE FROM IconInfo WHERE iconID NOT IN (SELECT iconID FROM PageURL);"))
+ LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk IconInfo table");
+
+ pruningTransaction.commit();
+
+ checkForDanglingPageURLs(true);
+
+ m_initialPruningComplete = true;
+}
+
+void IconDatabase::checkForDanglingPageURLs(bool pruneIfFound)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ // This check can be relatively expensive so we don't do it in a release build unless the caller has asked us to prune any dangling
+ // entries. We also don't want to keep performing this check and reporting this error if it has already found danglers before so we
+ // keep track of whether we've found any. We skip the check in the release build pretending to have already found danglers already.
+#ifndef NDEBUG
+ static bool danglersFound = true;
+#else
+ static bool danglersFound = false;
+#endif
+
+ if ((pruneIfFound || !danglersFound) && SQLiteStatement(m_syncDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM IconInfo) LIMIT 1;").returnsAtLeastOneResult()) {
+ danglersFound = true;
+ LOG(IconDatabase, "Dangling PageURL entries found");
+ if (pruneIfFound && !m_syncDB.executeCommand("DELETE FROM PageURL WHERE iconID NOT IN (SELECT iconID FROM IconInfo);"))
+ LOG(IconDatabase, "Unable to prune dangling PageURLs");
+ }
+}
+
+void IconDatabase::removeAllIconsOnThread()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ LOG(IconDatabase, "Removing all icons on the sync thread");
+
+ // Delete all the prepared statements so they can start over
+ deleteAllPreparedStatements();
+
+ // To reset the on-disk database, we'll wipe all its tables then vacuum it
+ // This is easier and safer than closing it, deleting the file, and recreating from scratch
+ m_syncDB.clearAllTables();
+ m_syncDB.runVacuumCommand();
+ createDatabaseTables(m_syncDB);
+
+ LOG(IconDatabase, "Dispatching notification that we removed all icons");
+ m_client->dispatchDidRemoveAllIcons();
+}
+
+void IconDatabase::deleteAllPreparedStatements()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ m_setIconIDForPageURLStatement.clear();
+ m_removePageURLStatement.clear();
+ m_getIconIDForIconURLStatement.clear();
+ m_getImageDataForIconURLStatement.clear();
+ m_addIconToIconInfoStatement.clear();
+ m_addIconToIconDataStatement.clear();
+ m_getImageDataStatement.clear();
+ m_deletePageURLsForIconURLStatement.clear();
+ m_deleteIconFromIconInfoStatement.clear();
+ m_deleteIconFromIconDataStatement.clear();
+ m_updateIconInfoStatement.clear();
+ m_updateIconDataStatement.clear();
+ m_setIconInfoStatement.clear();
+ m_setIconDataStatement.clear();
+}
+
+void* IconDatabase::cleanupSyncThread()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+#ifndef NDEBUG
+ double timeStamp = currentTime();
+#endif
+
+ // If the removeIcons flag is set, remove all icons from the db.
+ if (m_removeIconsRequested)
+ removeAllIconsOnThread();
+
+ // Sync remaining icons out
+ LOG(IconDatabase, "(THREAD) Doing final writeout and closure of sync thread");
+ writeToDatabase();
+
+ // Close the database
+ MutexLocker locker(m_syncLock);
+
+ m_databaseDirectory = String();
+ m_completeDatabasePath = String();
+ deleteAllPreparedStatements();
+ m_syncDB.close();
+
+#ifndef NDEBUG
+ LOG(IconDatabase, "(THREAD) Final closure took %.4f seconds", currentTime() - timeStamp);
+#endif
+
+ m_syncThreadRunning = false;
+ return 0;
+}
+
+bool IconDatabase::imported()
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ if (m_isImportedSet)
+ return m_imported;
+
+ SQLiteStatement query(m_syncDB, "SELECT IconDatabaseInfo.value FROM IconDatabaseInfo WHERE IconDatabaseInfo.key = \"ImportedSafari2Icons\";");
+ if (query.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare imported statement");
+ return false;
+ }
+
+ int result = query.step();
+ if (result == SQLResultRow)
+ result = query.getColumnInt(0);
+ else {
+ if (result != SQLResultDone)
+ LOG_ERROR("imported statement failed");
+ result = 0;
+ }
+
+ m_isImportedSet = true;
+ return m_imported = result;
+}
+
+void IconDatabase::setImported(bool import)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ m_imported = import;
+ m_isImportedSet = true;
+
+ String queryString = import ?
+ "INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 1);" :
+ "INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 0);";
+
+ SQLiteStatement query(m_syncDB, queryString);
+
+ if (query.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare set imported statement");
+ return;
+ }
+
+ if (query.step() != SQLResultDone)
+ LOG_ERROR("set imported statement failed");
+}
+
+// readySQLiteStatement() handles two things
+// 1 - If the SQLDatabase& argument is different, the statement must be destroyed and remade. This happens when the user
+// switches to and from private browsing
+// 2 - Lazy construction of the Statement in the first place, in case we've never made this query before
+inline void readySQLiteStatement(OwnPtr<SQLiteStatement>& statement, SQLiteDatabase& db, const String& str)
+{
+ if (statement && (statement->database() != &db || statement->isExpired())) {
+ if (statement->isExpired())
+ LOG(IconDatabase, "SQLiteStatement associated with %s is expired", str.ascii().data());
+ statement.set(0);
+ }
+ if (!statement) {
+ statement = adoptPtr(new SQLiteStatement(db, str));
+ if (statement->prepare() != SQLResultOk)
+ LOG_ERROR("Preparing statement %s failed", str.ascii().data());
+ }
+}
+
+void IconDatabase::setIconURLForPageURLInSQLDatabase(const String& iconURL, const String& pageURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ int64_t iconID = getIconIDForIconURLFromSQLDatabase(iconURL);
+
+ if (!iconID)
+ iconID = addIconURLToSQLDatabase(iconURL);
+
+ if (!iconID) {
+ LOG_ERROR("Failed to establish an ID for iconURL %s", urlForLogging(iconURL).ascii().data());
+ ASSERT(false);
+ return;
+ }
+
+ setIconIDForPageURLInSQLDatabase(iconID, pageURL);
+}
+
+void IconDatabase::setIconIDForPageURLInSQLDatabase(int64_t iconID, const String& pageURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ readySQLiteStatement(m_setIconIDForPageURLStatement, m_syncDB, "INSERT INTO PageURL (url, iconID) VALUES ((?), ?);");
+ m_setIconIDForPageURLStatement->bindText(1, pageURL);
+ m_setIconIDForPageURLStatement->bindInt64(2, iconID);
+
+ int result = m_setIconIDForPageURLStatement->step();
+ if (result != SQLResultDone) {
+ ASSERT(false);
+ LOG_ERROR("setIconIDForPageURLQuery failed for url %s", urlForLogging(pageURL).ascii().data());
+ }
+
+ m_setIconIDForPageURLStatement->reset();
+}
+
+void IconDatabase::removePageURLFromSQLDatabase(const String& pageURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ readySQLiteStatement(m_removePageURLStatement, m_syncDB, "DELETE FROM PageURL WHERE url = (?);");
+ m_removePageURLStatement->bindText(1, pageURL);
+
+ if (m_removePageURLStatement->step() != SQLResultDone)
+ LOG_ERROR("removePageURLFromSQLDatabase failed for url %s", urlForLogging(pageURL).ascii().data());
+
+ m_removePageURLStatement->reset();
+}
+
+
+int64_t IconDatabase::getIconIDForIconURLFromSQLDatabase(const String& iconURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ readySQLiteStatement(m_getIconIDForIconURLStatement, m_syncDB, "SELECT IconInfo.iconID FROM IconInfo WHERE IconInfo.url = (?);");
+ m_getIconIDForIconURLStatement->bindText(1, iconURL);
+
+ int64_t result = m_getIconIDForIconURLStatement->step();
+ if (result == SQLResultRow)
+ result = m_getIconIDForIconURLStatement->getColumnInt64(0);
+ else {
+ if (result != SQLResultDone)
+ LOG_ERROR("getIconIDForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data());
+ result = 0;
+ }
+
+ m_getIconIDForIconURLStatement->reset();
+ return result;
+}
+
+int64_t IconDatabase::addIconURLToSQLDatabase(const String& iconURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ // There would be a transaction here to make sure these two inserts are atomic
+ // In practice the only caller of this method is always wrapped in a transaction itself so placing another
+ // here is unnecessary
+
+ readySQLiteStatement(m_addIconToIconInfoStatement, m_syncDB, "INSERT INTO IconInfo (url, stamp) VALUES (?, 0);");
+ m_addIconToIconInfoStatement->bindText(1, iconURL);
+
+ int result = m_addIconToIconInfoStatement->step();
+ m_addIconToIconInfoStatement->reset();
+ if (result != SQLResultDone) {
+ LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconInfo", urlForLogging(iconURL).ascii().data());
+ return 0;
+ }
+ int64_t iconID = m_syncDB.lastInsertRowID();
+
+ readySQLiteStatement(m_addIconToIconDataStatement, m_syncDB, "INSERT INTO IconData (iconID, data) VALUES (?, ?);");
+ m_addIconToIconDataStatement->bindInt64(1, iconID);
+
+ result = m_addIconToIconDataStatement->step();
+ m_addIconToIconDataStatement->reset();
+ if (result != SQLResultDone) {
+ LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconData", urlForLogging(iconURL).ascii().data());
+ return 0;
+ }
+
+ return iconID;
+}
+
+PassRefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(const String& iconURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ RefPtr<SharedBuffer> imageData;
+
+ readySQLiteStatement(m_getImageDataForIconURLStatement, m_syncDB, "SELECT IconData.data FROM IconData WHERE IconData.iconID IN (SELECT iconID FROM IconInfo WHERE IconInfo.url = (?));");
+ m_getImageDataForIconURLStatement->bindText(1, iconURL);
+
+ int result = m_getImageDataForIconURLStatement->step();
+ if (result == SQLResultRow) {
+ Vector<char> data;
+ m_getImageDataForIconURLStatement->getColumnBlobAsVector(0, data);
+ imageData = SharedBuffer::create(data.data(), data.size());
+ } else if (result != SQLResultDone)
+ LOG_ERROR("getImageDataForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data());
+
+ m_getImageDataForIconURLStatement->reset();
+
+ return imageData.release();
+}
+
+void IconDatabase::removeIconFromSQLDatabase(const String& iconURL)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ if (iconURL.isEmpty())
+ return;
+
+ // There would be a transaction here to make sure these removals are atomic
+ // In practice the only caller of this method is always wrapped in a transaction itself so placing another here is unnecessary
+
+ // It's possible this icon is not in the database because of certain rapid browsing patterns (such as a stress test) where the
+ // icon is marked to be added then marked for removal before it is ever written to disk. No big deal, early return
+ int64_t iconID = getIconIDForIconURLFromSQLDatabase(iconURL);
+ if (!iconID)
+ return;
+
+ readySQLiteStatement(m_deletePageURLsForIconURLStatement, m_syncDB, "DELETE FROM PageURL WHERE PageURL.iconID = (?);");
+ m_deletePageURLsForIconURLStatement->bindInt64(1, iconID);
+
+ if (m_deletePageURLsForIconURLStatement->step() != SQLResultDone)
+ LOG_ERROR("m_deletePageURLsForIconURLStatement failed for url %s", urlForLogging(iconURL).ascii().data());
+
+ readySQLiteStatement(m_deleteIconFromIconInfoStatement, m_syncDB, "DELETE FROM IconInfo WHERE IconInfo.iconID = (?);");
+ m_deleteIconFromIconInfoStatement->bindInt64(1, iconID);
+
+ if (m_deleteIconFromIconInfoStatement->step() != SQLResultDone)
+ LOG_ERROR("m_deleteIconFromIconInfoStatement failed for url %s", urlForLogging(iconURL).ascii().data());
+
+ readySQLiteStatement(m_deleteIconFromIconDataStatement, m_syncDB, "DELETE FROM IconData WHERE IconData.iconID = (?);");
+ m_deleteIconFromIconDataStatement->bindInt64(1, iconID);
+
+ if (m_deleteIconFromIconDataStatement->step() != SQLResultDone)
+ LOG_ERROR("m_deleteIconFromIconDataStatement failed for url %s", urlForLogging(iconURL).ascii().data());
+
+ m_deletePageURLsForIconURLStatement->reset();
+ m_deleteIconFromIconInfoStatement->reset();
+ m_deleteIconFromIconDataStatement->reset();
+}
+
+void IconDatabase::writeIconSnapshotToSQLDatabase(const IconSnapshot& snapshot)
+{
+ ASSERT_ICON_SYNC_THREAD();
+
+ if (snapshot.iconURL.isEmpty())
+ return;
+
+ // A nulled out timestamp and data means this icon is destined to be deleted - do that instead of writing it out
+ if (!snapshot.timestamp && !snapshot.data) {
+ LOG(IconDatabase, "Removing %s from on-disk database", urlForLogging(snapshot.iconURL).ascii().data());
+ removeIconFromSQLDatabase(snapshot.iconURL);
+ return;
+ }
+
+ // There would be a transaction here to make sure these removals are atomic
+ // In practice the only caller of this method is always wrapped in a transaction itself so placing another here is unnecessary
+
+ // Get the iconID for this url
+ int64_t iconID = getIconIDForIconURLFromSQLDatabase(snapshot.iconURL);
+
+ // If there is already an iconID in place, update the database.
+ // Otherwise, insert new records
+ if (iconID) {
+ readySQLiteStatement(m_updateIconInfoStatement, m_syncDB, "UPDATE IconInfo SET stamp = ?, url = ? WHERE iconID = ?;");
+ m_updateIconInfoStatement->bindInt64(1, snapshot.timestamp);
+ m_updateIconInfoStatement->bindText(2, snapshot.iconURL);
+ m_updateIconInfoStatement->bindInt64(3, iconID);
+
+ if (m_updateIconInfoStatement->step() != SQLResultDone)
+ LOG_ERROR("Failed to update icon info for url %s", urlForLogging(snapshot.iconURL).ascii().data());
+
+ m_updateIconInfoStatement->reset();
+
+ readySQLiteStatement(m_updateIconDataStatement, m_syncDB, "UPDATE IconData SET data = ? WHERE iconID = ?;");
+ m_updateIconDataStatement->bindInt64(2, iconID);
+
+ // If we *have* image data, bind it to this statement - Otherwise bind "null" for the blob data,
+ // signifying that this icon doesn't have any data
+ if (snapshot.data && snapshot.data->size())
+ m_updateIconDataStatement->bindBlob(1, snapshot.data->data(), snapshot.data->size());
+ else
+ m_updateIconDataStatement->bindNull(1);
+
+ if (m_updateIconDataStatement->step() != SQLResultDone)
+ LOG_ERROR("Failed to update icon data for url %s", urlForLogging(snapshot.iconURL).ascii().data());
+
+ m_updateIconDataStatement->reset();
+ } else {
+ readySQLiteStatement(m_setIconInfoStatement, m_syncDB, "INSERT INTO IconInfo (url,stamp) VALUES (?, ?);");
+ m_setIconInfoStatement->bindText(1, snapshot.iconURL);
+ m_setIconInfoStatement->bindInt64(2, snapshot.timestamp);
+
+ if (m_setIconInfoStatement->step() != SQLResultDone)
+ LOG_ERROR("Failed to set icon info for url %s", urlForLogging(snapshot.iconURL).ascii().data());
+
+ m_setIconInfoStatement->reset();
+
+ int64_t iconID = m_syncDB.lastInsertRowID();
+
+ readySQLiteStatement(m_setIconDataStatement, m_syncDB, "INSERT INTO IconData (iconID, data) VALUES (?, ?);");
+ m_setIconDataStatement->bindInt64(1, iconID);
+
+ // If we *have* image data, bind it to this statement - Otherwise bind "null" for the blob data,
+ // signifying that this icon doesn't have any data
+ if (snapshot.data && snapshot.data->size())
+ m_setIconDataStatement->bindBlob(2, snapshot.data->data(), snapshot.data->size());
+ else
+ m_setIconDataStatement->bindNull(2);
+
+ if (m_setIconDataStatement->step() != SQLResultDone)
+ LOG_ERROR("Failed to set icon data for url %s", urlForLogging(snapshot.iconURL).ascii().data());
+
+ m_setIconDataStatement->reset();
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(ICONDATABASE)
diff --git a/Source/WebCore/loader/icon/IconDatabase.h b/Source/WebCore/loader/icon/IconDatabase.h
new file mode 100644
index 0000000..6146aa6
--- /dev/null
+++ b/Source/WebCore/loader/icon/IconDatabase.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef IconDatabase_h
+#define IconDatabase_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"
+#include <wtf/Threading.h>
+#endif
+
+namespace WebCore {
+
+class DocumentLoader;
+class Image;
+class IntSize;
+class IconDatabaseClient;
+class IconRecord;
+class IconSnapshot;
+class KURL;
+class PageURLRecord;
+class PageURLSnapshot;
+class SharedBuffer;
+
+#if ENABLE(ICONDATABASE)
+class SQLTransaction;
+#endif
+
+enum IconLoadDecision {
+ IconLoadYes,
+ IconLoadNo,
+ IconLoadUnknown
+};
+
+class IconDatabase : public Noncopyable {
+
+// *** Main Thread Only ***
+public:
+ void setClient(IconDatabaseClient*);
+
+ bool open(const String& path);
+ void close();
+
+ void removeAllIcons();
+
+ Image* iconForPageURL(const String&, const IntSize&);
+ void readIconForPageURLFromDisk(const String&);
+ String iconURLForPageURL(const String&);
+ Image* defaultIcon(const IntSize&);
+
+ void retainIconForPageURL(const String&);
+ void releaseIconForPageURL(const String&);
+
+ void setIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String&);
+ void setIconURLForPageURL(const String& iconURL, const String& pageURL);
+
+ IconLoadDecision loadDecisionForIconURL(const String&, DocumentLoader*);
+ bool iconDataKnownForIconURL(const String&);
+
+ void setEnabled(bool enabled);
+ bool isEnabled() const;
+
+ void setPrivateBrowsingEnabled(bool flag);
+ bool isPrivateBrowsingEnabled() const;
+
+ static void delayDatabaseCleanup();
+ static void allowDatabaseCleanup();
+ static void checkIntegrityBeforeOpening();
+
+ // Support for WebCoreStatistics in WebKit
+ size_t pageURLMappingCount();
+ size_t retainedPageURLCount();
+ size_t iconRecordCount();
+ size_t iconRecordCountWithData();
+
+private:
+ IconDatabase();
+ ~IconDatabase();
+ friend IconDatabase* iconDatabase();
+
+#if ENABLE(ICONDATABASE)
+ static void notifyPendingLoadDecisionsOnMainThread(void*);
+ void notifyPendingLoadDecisions();
+
+ void wakeSyncThread();
+ void scheduleOrDeferSyncTimer();
+ void syncTimerFired(Timer<IconDatabase>*);
+
+ Timer<IconDatabase> m_syncTimer;
+ ThreadIdentifier m_syncThread;
+ bool m_syncThreadRunning;
+
+ HashSet<RefPtr<DocumentLoader> > m_loadersPendingDecision;
+
+ RefPtr<IconRecord> m_defaultIconRecord;
+#endif // ENABLE(ICONDATABASE)
+
+// *** Any Thread ***
+public:
+ bool isOpen() const;
+ String databasePath() const;
+ static String defaultDatabaseFilename();
+
+#if ENABLE(ICONDATABASE)
+private:
+ PassRefPtr<IconRecord> getOrCreateIconRecord(const String& iconURL);
+ PageURLRecord* getOrCreatePageURLRecord(const String& pageURL);
+
+ bool m_isEnabled;
+ bool m_privateBrowsingEnabled;
+
+ mutable Mutex m_syncLock;
+ ThreadCondition m_syncCondition;
+ 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;
+ HashMap<String, PageURLRecord*> m_pageURLToRecordMap;
+ HashSet<String> m_retainedPageURLs;
+
+ Mutex m_pendingSyncLock;
+ // Holding m_pendingSyncLock is required when accessing any of the following data structures
+ HashMap<String, PageURLSnapshot> m_pageURLsPendingSync;
+ HashMap<String, IconSnapshot> m_iconsPendingSync;
+
+ Mutex m_pendingReadingLock;
+ // Holding m_pendingSyncLock is required when accessing any of the following data structures - when dealing with IconRecord*s, holding m_urlAndIconLock is also required
+ HashSet<String> m_pageURLsPendingImport;
+ HashSet<String> m_pageURLsInterestedInIcons;
+ HashSet<IconRecord*> m_iconsPendingReading;
+#endif // ENABLE(ICONDATABASE)
+
+// *** Sync Thread Only ***
+public:
+ // Should be used only on the sync thread and only by the Safari 2 Icons import procedure
+ void importIconURLForPageURL(const String& iconURL, const String& pageURL);
+ void importIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String& iconURL);
+
+ bool shouldStopThreadActivity() const;
+
+#if ENABLE(ICONDATABASE)
+private:
+ static void* iconDatabaseSyncThreadStart(void *);
+ void* iconDatabaseSyncThread();
+
+ // The following block of methods are called exclusively by the sync thread to manage i/o to and from the database
+ // Each method should periodically monitor m_threadTerminationRequested when it makes sense to return early on shutdown
+ void performOpenInitialization();
+ bool checkIntegrity();
+ void performURLImport();
+ void* syncThreadMainLoop();
+ bool readFromDatabase();
+ bool writeToDatabase();
+ void pruneUnretainedIcons();
+ void checkForDanglingPageURLs(bool pruneIfFound);
+ void removeAllIconsOnThread();
+ void deleteAllPreparedStatements();
+ void* cleanupSyncThread();
+
+ // Record (on disk) whether or not Safari 2-style icons were imported (once per dataabse)
+ bool imported();
+ void setImported(bool);
+
+ bool m_initialPruningComplete;
+
+ void setIconURLForPageURLInSQLDatabase(const String&, const String&);
+ void setIconIDForPageURLInSQLDatabase(int64_t, const String&);
+ void removePageURLFromSQLDatabase(const String& pageURL);
+ int64_t getIconIDForIconURLFromSQLDatabase(const String& iconURL);
+ int64_t addIconURLToSQLDatabase(const String&);
+ PassRefPtr<SharedBuffer> getImageDataForIconURLFromSQLDatabase(const String& iconURL);
+ void removeIconFromSQLDatabase(const String& iconURL);
+ void writeIconSnapshotToSQLDatabase(const IconSnapshot&);
+
+ // The client is set by the main thread before the thread starts, and from then on is only used by the sync thread
+ IconDatabaseClient* m_client;
+
+ SQLiteDatabase m_syncDB;
+
+ // Track whether the "Safari 2" import is complete and/or set in the database
+ bool m_imported;
+ bool m_isImportedSet;
+
+ OwnPtr<SQLiteStatement> m_setIconIDForPageURLStatement;
+ OwnPtr<SQLiteStatement> m_removePageURLStatement;
+ OwnPtr<SQLiteStatement> m_getIconIDForIconURLStatement;
+ OwnPtr<SQLiteStatement> m_getImageDataForIconURLStatement;
+ OwnPtr<SQLiteStatement> m_addIconToIconInfoStatement;
+ OwnPtr<SQLiteStatement> m_addIconToIconDataStatement;
+ OwnPtr<SQLiteStatement> m_getImageDataStatement;
+ OwnPtr<SQLiteStatement> m_deletePageURLsForIconURLStatement;
+ OwnPtr<SQLiteStatement> m_deleteIconFromIconInfoStatement;
+ OwnPtr<SQLiteStatement> m_deleteIconFromIconDataStatement;
+ OwnPtr<SQLiteStatement> m_updateIconInfoStatement;
+ OwnPtr<SQLiteStatement> m_updateIconDataStatement;
+ OwnPtr<SQLiteStatement> m_setIconInfoStatement;
+ OwnPtr<SQLiteStatement> m_setIconDataStatement;
+#endif // ENABLE(ICONDATABASE)
+};
+
+// Function to obtain the global icon database.
+IconDatabase* iconDatabase();
+
+} // namespace WebCore
+
+#endif // IconDatabase_h
diff --git a/Source/WebCore/loader/icon/IconDatabaseClient.h b/Source/WebCore/loader/icon/IconDatabaseClient.h
new file mode 100644
index 0000000..c210d7d
--- /dev/null
+++ b/Source/WebCore/loader/icon/IconDatabaseClient.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef 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
+// Take appropriate measures
+
+namespace WebCore {
+
+class IconDatabaseClient : public Noncopyable {
+public:
+ virtual ~IconDatabaseClient() { }
+ virtual bool performImport() { return true; }
+ virtual void dispatchDidRemoveAllIcons() { }
+ virtual void dispatchDidAddIconForPageURL(const String& /*pageURL*/) { }
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/loader/icon/IconDatabaseNone.cpp b/Source/WebCore/loader/icon/IconDatabaseNone.cpp
new file mode 100644
index 0000000..7b7cc9f
--- /dev/null
+++ b/Source/WebCore/loader/icon/IconDatabaseNone.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "IconDatabase.h"
+
+#if !ENABLE(ICONDATABASE)
+
+#include "PlatformString.h"
+#include "SharedBuffer.h"
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+static IconDatabase* sharedIconDatabase = 0;
+
+// This version number is in the DB and marks the current generation of the schema
+// Theoretically once the switch is flipped this should never change
+// Currently, an out-of-date schema causes the DB to be wiped and reset. This isn't
+// so bad during development but in the future, we would need to write a conversion
+// function to advance older released schemas to "current"
+const int currentDatabaseVersion = 5;
+
+// Icons expire once a day
+const int iconExpirationTime = 60*60*24;
+// Absent icons are rechecked once a week
+const int missingIconExpirationTime = 60*60*24*7;
+
+const int updateTimerDelay = 5;
+
+String IconDatabase::defaultDatabaseFilename()
+{
+ DEFINE_STATIC_LOCAL(String, defaultDatabaseFilename, ("Icons.db"));
+ return defaultDatabaseFilename.threadsafeCopy();
+}
+
+IconDatabase* iconDatabase()
+{
+ if (!sharedIconDatabase)
+ sharedIconDatabase = new IconDatabase;
+ return sharedIconDatabase;
+}
+
+IconDatabase::IconDatabase()
+{
+}
+
+bool IconDatabase::open(const String& /*databasePath*/)
+{
+ return false;
+}
+
+bool IconDatabase::isOpen() const
+{
+ return false;
+}
+
+void IconDatabase::close()
+{
+}
+
+String IconDatabase::databasePath() const
+{
+ return String();
+}
+
+void IconDatabase::removeAllIcons()
+{
+}
+
+void IconDatabase::setPrivateBrowsingEnabled(bool /*flag*/)
+{
+}
+
+bool IconDatabase::isPrivateBrowsingEnabled() const
+{
+ return false;
+}
+
+void IconDatabase::readIconForPageURLFromDisk(const String&)
+{
+
+}
+
+Image* IconDatabase::iconForPageURL(const String& /*pageURL*/, const IntSize& size)
+{
+ return defaultIcon(size);
+}
+
+
+IconLoadDecision IconDatabase::loadDecisionForIconURL(const String&, DocumentLoader*)
+{
+ return IconLoadNo;
+}
+
+bool IconDatabase::iconDataKnownForIconURL(const String&)
+{
+ return false;
+}
+
+String IconDatabase::iconURLForPageURL(const String& /*pageURL*/)
+{
+ return String();
+}
+
+Image* IconDatabase::defaultIcon(const IntSize& /*size*/)
+{
+ return 0;
+}
+
+void IconDatabase::retainIconForPageURL(const String& /*pageURL*/)
+{
+}
+
+void IconDatabase::releaseIconForPageURL(const String& /*pageURL*/)
+{
+}
+
+void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> /*data*/, const String& /*iconURL*/)
+{
+}
+
+void IconDatabase::setIconURLForPageURL(const String& /*iconURL*/, const String& /*pageURL*/)
+{
+}
+
+void IconDatabase::setEnabled(bool /*enabled*/)
+{
+}
+
+bool IconDatabase::isEnabled() const
+{
+ return false;
+}
+
+IconDatabase::~IconDatabase()
+{
+ ASSERT_NOT_REACHED();
+}
+
+void IconDatabase::checkIntegrityBeforeOpening()
+{
+}
+
+void IconDatabase::delayDatabaseCleanup()
+{
+}
+
+void IconDatabase::allowDatabaseCleanup()
+{
+}
+
+size_t IconDatabase::pageURLMappingCount()
+{
+ return 0;
+}
+
+size_t IconDatabase::retainedPageURLCount()
+{
+ return 0;
+}
+
+size_t IconDatabase::iconRecordCount()
+{
+ return 0;
+}
+
+size_t IconDatabase::iconRecordCountWithData()
+{
+ return 0;
+}
+
+void IconDatabase::setClient(IconDatabaseClient*)
+{
+}
+
+// ************************
+// *** Sync Thread Only ***
+// ************************
+
+void IconDatabase::importIconURLForPageURL(const String&, const String&)
+{
+}
+
+void IconDatabase::importIconDataForIconURL(PassRefPtr<SharedBuffer>, const String&)
+{
+}
+
+bool IconDatabase::shouldStopThreadActivity() const
+{
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // !ENABLE(ICONDATABASE)
diff --git a/Source/WebCore/loader/icon/IconLoader.cpp b/Source/WebCore/loader/icon/IconLoader.cpp
new file mode 100644
index 0000000..24562d0
--- /dev/null
+++ b/Source/WebCore/loader/icon/IconLoader.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "IconLoader.h"
+
+#include "Document.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "IconDatabase.h"
+#include "Logging.h"
+#include "ResourceHandle.h"
+#include "ResourceLoadScheduler.h"
+#include "ResourceResponse.h"
+#include "ResourceRequest.h"
+#include "SharedBuffer.h"
+#include "SubresourceLoader.h"
+#include <wtf/UnusedParam.h>
+#include <wtf/text/CString.h>
+
+using namespace std;
+
+namespace WebCore {
+
+IconLoader::IconLoader(Frame* frame)
+ : m_frame(frame)
+ , m_loadIsInProgress(false)
+{
+}
+
+PassOwnPtr<IconLoader> IconLoader::create(Frame* frame)
+{
+ return adoptPtr(new IconLoader(frame));
+}
+
+IconLoader::~IconLoader()
+{
+}
+
+void IconLoader::startLoading()
+{
+ if (m_resourceLoader)
+ return;
+
+ // Set flag so we can detect the case where the load completes before
+ // SubresourceLoader::create returns.
+ m_loadIsInProgress = true;
+
+ RefPtr<SubresourceLoader> loader = resourceLoadScheduler()->scheduleSubresourceLoad(m_frame, this, m_frame->loader()->iconURL());
+ if (!loader)
+ LOG_ERROR("Failed to start load for icon at url %s", m_frame->loader()->iconURL().string().ascii().data());
+
+ // Store the handle so we can cancel the load if stopLoading is called later.
+ // But only do it if the load hasn't already completed.
+ if (m_loadIsInProgress)
+ m_resourceLoader = loader.release();
+}
+
+void IconLoader::stopLoading()
+{
+ clearLoadingState();
+}
+
+void IconLoader::didReceiveResponse(SubresourceLoader* resourceLoader, const ResourceResponse& response)
+{
+ // If we got a status code indicating an invalid response, then lets
+ // ignore the data and not try to decode the error page as an icon.
+ int status = response.httpStatusCode();
+ LOG(IconDatabase, "IconLoader::didReceiveResponse() - Loader %p, response %i", resourceLoader, status);
+
+ if (status && (status < 200 || status > 299)) {
+ ResourceHandle* handle = resourceLoader->handle();
+ finishLoading(handle ? handle->firstRequest().url() : KURL(), 0);
+ }
+}
+
+void IconLoader::didReceiveData(SubresourceLoader* unusedLoader, const char*, int unusedSize)
+{
+#if LOG_DISABLED
+ UNUSED_PARAM(unusedLoader);
+ UNUSED_PARAM(unusedSize);
+#endif
+ LOG(IconDatabase, "IconLoader::didReceiveData() - Loader %p, number of bytes %i", unusedLoader, unusedSize);
+}
+
+void IconLoader::didFail(SubresourceLoader* resourceLoader, const ResourceError&)
+{
+ LOG(IconDatabase, "IconLoader::didFail() - Loader %p", resourceLoader);
+
+ // Until <rdar://problem/5463392> is resolved and we can properly cancel SubresourceLoaders when they get an error response,
+ // we need to be prepared to receive this call even after we've "finished loading" once.
+ // After it is resolved, we can restore an assertion that the load is in progress if ::didFail() is called
+
+ if (m_loadIsInProgress) {
+ ASSERT(resourceLoader == m_resourceLoader);
+ ResourceHandle* handle = resourceLoader->handle();
+ finishLoading(handle ? handle->firstRequest().url() : KURL(), 0);
+ }
+}
+
+void IconLoader::didReceiveAuthenticationChallenge(SubresourceLoader*, const AuthenticationChallenge&)
+{
+ // We don't ever want to prompt for authentication just for a site icon, so
+ // implement this method to cancel the resource load
+ m_resourceLoader->cancel();
+}
+
+void IconLoader::didFinishLoading(SubresourceLoader* resourceLoader)
+{
+ LOG(IconDatabase, "IconLoader::didFinishLoading() - Loader %p", resourceLoader);
+
+ // Until <rdar://problem/5463392> is resolved and we can properly cancel SubresourceLoaders when they get an error response,
+ // we need to be prepared to receive this call even after we've "finished loading" once.
+ // After it is resolved, we can restore an assertion that the load is in progress if ::didFail() is called
+
+ if (m_loadIsInProgress) {
+ ASSERT(resourceLoader == m_resourceLoader);
+ ResourceHandle* handle = resourceLoader->handle();
+ finishLoading(handle ? handle->firstRequest().url() : KURL(), m_resourceLoader->resourceData());
+ }
+}
+
+void IconLoader::finishLoading(const KURL& iconURL, PassRefPtr<SharedBuffer> data)
+{
+ // When an icon load results in a 404 we commit it to the database here and clear the loading state.
+ // But the SubresourceLoader continues pulling in data in the background for the 404 page if the server sends one.
+ // Once that data finishes loading or if the load is cancelled while that data is being read, finishLoading ends up being called a second time.
+ // We need to change SubresourceLoader to have a mode where it will stop itself after receiving a 404 so this won't happen -
+ // in the meantime, we'll only commit this data to the IconDatabase if it's the first time ::finishLoading() is called
+ // <rdar://problem/5463392> tracks that enhancement
+
+ if (!iconURL.isEmpty() && m_loadIsInProgress) {
+ LOG(IconDatabase, "IconLoader::finishLoading() - Committing iconURL %s to database", iconURL.string().ascii().data());
+ m_frame->loader()->commitIconURLToIconDatabase(iconURL);
+ // Setting the icon data only after committing to the database ensures that the data is
+ // kept in memory (so it does not have to be read from the database asynchronously), since
+ // there is a page URL referencing it.
+ iconDatabase()->setIconDataForIconURL(data, iconURL.string());
+ m_frame->loader()->client()->dispatchDidReceiveIcon();
+ }
+
+ clearLoadingState();
+}
+
+void IconLoader::clearLoadingState()
+{
+ m_resourceLoader = 0;
+ m_loadIsInProgress = false;
+}
+
+}
diff --git a/Source/WebCore/loader/icon/IconLoader.h b/Source/WebCore/loader/icon/IconLoader.h
new file mode 100644
index 0000000..1ebac48
--- /dev/null
+++ b/Source/WebCore/loader/icon/IconLoader.h
@@ -0,0 +1,69 @@
+/*
+ * 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,
+ * 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 IconLoader_h
+#define IconLoader_h
+
+#include "SubresourceLoaderClient.h"
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class Frame;
+class KURL;
+class SharedBuffer;
+
+class IconLoader : private SubresourceLoaderClient, public Noncopyable {
+public:
+ static PassOwnPtr<IconLoader> create(Frame*);
+ ~IconLoader();
+
+ void startLoading();
+ void stopLoading();
+
+private:
+ IconLoader(Frame*);
+
+ virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&);
+ virtual void didReceiveData(SubresourceLoader*, const char*, int);
+ virtual void didFinishLoading(SubresourceLoader*);
+ virtual void didFail(SubresourceLoader*, const ResourceError&);
+
+ virtual void didReceiveAuthenticationChallenge(SubresourceLoader*, const AuthenticationChallenge&);
+
+ void finishLoading(const KURL&, PassRefPtr<SharedBuffer> data);
+ void clearLoadingState();
+
+ Frame* m_frame;
+
+ RefPtr<SubresourceLoader> m_resourceLoader;
+ bool m_loadIsInProgress;
+}; // class IconLoader
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/loader/icon/IconRecord.cpp b/Source/WebCore/loader/icon/IconRecord.cpp
new file mode 100644
index 0000000..7e90d8e
--- /dev/null
+++ b/Source/WebCore/loader/icon/IconRecord.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "IconRecord.h"
+
+#include "BitmapImage.h"
+#include "IconDatabase.h"
+#include "Logging.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include <wtf/text/CString.h>
+
+#include <limits.h>
+
+namespace WebCore {
+
+IconRecord::IconRecord(const String& url)
+ : m_iconURL(url)
+ , m_stamp(0)
+ , m_dataSet(false)
+{
+
+}
+
+IconRecord::~IconRecord()
+{
+ LOG(IconDatabase, "Destroying IconRecord for icon url %s", m_iconURL.ascii().data());
+}
+
+Image* IconRecord::image(const IntSize&)
+{
+ // FIXME rdar://4680377 - For size right now, we are returning our one and only image and the Bridge
+ // is resizing it in place. We need to actually store all the original representations here and return a native
+ // one, or resize the best one to the requested size and cache that result.
+
+ return m_image.get();
+}
+
+void IconRecord::setImageData(PassRefPtr<SharedBuffer> data)
+{
+ // It's okay to delete the raw image here. Any existing clients using this icon will be
+ // managing an image that was created with a copy of this raw image data.
+ m_image = BitmapImage::create();
+
+ // Copy the provided data into the buffer of the new Image object.
+ if (!m_image->setData(data, true)) {
+ LOG(IconDatabase, "Manual image data for iconURL '%s' FAILED - it was probably invalid image data", m_iconURL.ascii().data());
+ m_image.clear();
+ }
+
+ m_dataSet = true;
+}
+
+void IconRecord::loadImageFromResource(const char* resource)
+{
+ if (!resource)
+ return;
+
+ m_image = Image::loadPlatformResource(resource);
+ m_dataSet = true;
+}
+
+ImageDataStatus IconRecord::imageDataStatus()
+{
+ if (!m_dataSet)
+ return ImageDataStatusUnknown;
+ if (!m_image)
+ return ImageDataStatusMissing;
+ return ImageDataStatusPresent;
+}
+
+IconSnapshot IconRecord::snapshot(bool forDeletion) const
+{
+ if (forDeletion)
+ return IconSnapshot(m_iconURL, 0, 0);
+
+ return IconSnapshot(m_iconURL, m_stamp, m_image ? m_image->data() : 0);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/icon/IconRecord.h b/Source/WebCore/loader/icon/IconRecord.h
new file mode 100644
index 0000000..f1fe12f
--- /dev/null
+++ b/Source/WebCore/loader/icon/IconRecord.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef IconRecord_h
+#define IconRecord_h
+
+#include "PageURLRecord.h"
+#include <wtf/RefCounted.h>
+#include "SharedBuffer.h"
+
+#include "PlatformString.h"
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+class IconDataSnapshot;
+class Image;
+class IntSize;
+class SQLDatabase;
+
+enum ImageDataStatus {
+ ImageDataStatusPresent, ImageDataStatusMissing, ImageDataStatusUnknown
+};
+
+class IconSnapshot {
+public:
+ IconSnapshot() : timestamp(0) { }
+
+ IconSnapshot(const String& url, int stamp, SharedBuffer* theData)
+ : iconURL(url)
+ , timestamp(stamp)
+ , data(theData)
+ { }
+
+ String iconURL;
+ int timestamp;
+ RefPtr<SharedBuffer> data;
+};
+
+class IconRecord : public RefCounted<IconRecord> {
+ friend class PageURLRecord;
+public:
+ static PassRefPtr<IconRecord> create(const String& url)
+ {
+ return adoptRef(new IconRecord(url));
+ }
+ ~IconRecord();
+
+ time_t getTimestamp() { return m_stamp; }
+ void setTimestamp(time_t stamp) { m_stamp = stamp; }
+
+ void setImageData(PassRefPtr<SharedBuffer> data);
+ Image* image(const IntSize&);
+
+ String iconURL() { return m_iconURL; }
+
+ void loadImageFromResource(const char*);
+
+ ImageDataStatus imageDataStatus();
+
+ const HashSet<String>& retainingPageURLs() { return m_retainingPageURLs; }
+
+ IconSnapshot snapshot(bool forDeletion = false) const;
+
+private:
+ IconRecord(const String& url);
+
+ String m_iconURL;
+ time_t m_stamp;
+ RefPtr<Image> m_image;
+
+ HashSet<String> m_retainingPageURLs;
+
+ // This allows us to cache whether or not a SiteIcon has had its data set yet
+ // This helps the IconDatabase know if it has to set the data on a new object or not,
+ // and also to determine if the icon is missing data or if it just hasn't been brought
+ // in from the DB yet
+ bool m_dataSet;
+
+ // FIXME - Right now WebCore::Image doesn't have a very good API for accessing multiple representations
+ // Even the NSImage way of doing things that we do in WebKit isn't very clean... once we come up with a
+ // better way of handling that, we'll likely have a map of size-to-images similar to below
+ // typedef HashMap<IntSize, Image*> SizeImageMap;
+ // SizeImageMap m_images;
+};
+
+
+} //namespace WebCore
+
+#endif
diff --git a/Source/WebCore/loader/icon/PageURLRecord.cpp b/Source/WebCore/loader/icon/PageURLRecord.cpp
new file mode 100644
index 0000000..09d649f
--- /dev/null
+++ b/Source/WebCore/loader/icon/PageURLRecord.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PageURLRecord.h"
+
+#include "IconRecord.h"
+
+namespace WebCore {
+
+PageURLRecord::PageURLRecord(const String& pageURL)
+ : m_pageURL(pageURL)
+ , m_retainCount(0)
+{
+}
+
+PageURLRecord::~PageURLRecord()
+{
+ setIconRecord(0);
+}
+
+void PageURLRecord::setIconRecord(PassRefPtr<IconRecord> icon)
+{
+ if (m_iconRecord)
+ m_iconRecord->m_retainingPageURLs.remove(m_pageURL);
+
+ m_iconRecord = icon;
+
+ if (m_iconRecord)
+ m_iconRecord->m_retainingPageURLs.add(m_pageURL);
+}
+
+PageURLSnapshot PageURLRecord::snapshot(bool forDeletion) const
+{
+ return PageURLSnapshot(m_pageURL, (m_iconRecord && !forDeletion) ? m_iconRecord->iconURL() : String());
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/icon/PageURLRecord.h b/Source/WebCore/loader/icon/PageURLRecord.h
new file mode 100644
index 0000000..f7ccb8f
--- /dev/null
+++ b/Source/WebCore/loader/icon/PageURLRecord.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PageURLRecord_h
+#define PageURLRecord_h
+
+#include "PlatformString.h"
+
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class IconRecord;
+
+class PageURLSnapshot {
+public:
+ PageURLSnapshot() { }
+
+ PageURLSnapshot(const String& page, const String& icon)
+ : pageURL(page)
+ , iconURL(icon)
+ { }
+
+ String pageURL;
+ String iconURL;
+};
+
+class PageURLRecord : public Noncopyable {
+public:
+ PageURLRecord(const String& pageURL);
+ ~PageURLRecord();
+
+ inline String url() const { return m_pageURL; }
+
+ void setIconRecord(PassRefPtr<IconRecord>);
+ IconRecord* iconRecord() { return m_iconRecord.get(); }
+
+ PageURLSnapshot snapshot(bool forDeletion = false) const;
+
+ // Returns false if the page wasn't retained beforehand, true if the retain count was already 1 or higher
+ inline bool retain() { return m_retainCount++; }
+
+ // Returns true if the page is still retained after the call. False if the retain count just dropped to 0
+ inline bool release()
+ {
+ ASSERT(m_retainCount > 0);
+ return --m_retainCount;
+ }
+
+ inline int retainCount() const { return m_retainCount; }
+private:
+ String m_pageURL;
+ RefPtr<IconRecord> m_iconRecord;
+ int m_retainCount;
+};
+
+}
+
+#endif // PageURLRecord_h
diff --git a/Source/WebCore/loader/icon/wince/IconDatabaseWinCE.cpp b/Source/WebCore/loader/icon/wince/IconDatabaseWinCE.cpp
new file mode 100644
index 0000000..54a36e5
--- /dev/null
+++ b/Source/WebCore/loader/icon/wince/IconDatabaseWinCE.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2007-2009 Torch Mobile Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "IconDatabase.h"
+
+#include "AutodrainedPool.h"
+#include "DocumentLoader.h"
+#include "FileSystem.h"
+#include "IconDatabaseClient.h"
+#include "IconRecord.h"
+#include "Image.h"
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+// Function to obtain the global icon database.
+IconDatabase* iconDatabase() { return 0; }
+
+IconDatabase::IconDatabase() {}
+IconDatabase::~IconDatabase() {}
+
+void IconDatabase::setClient(IconDatabaseClient*) {}
+
+bool IconDatabase::open(const String& path) { return false; }
+void IconDatabase::close() {}
+
+void IconDatabase::removeAllIcons() {}
+
+Image* IconDatabase::iconForPageURL(const String&, const IntSize&) { return 0; }
+void IconDatabase::readIconForPageURLFromDisk(const String&) {}
+String IconDatabase::iconURLForPageURL(const String&) { return String(); }
+Image* IconDatabase::defaultIcon(const IntSize&) { return 0;}
+
+void IconDatabase::retainIconForPageURL(const String&) {}
+void IconDatabase::releaseIconForPageURL(const String&) {}
+
+void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String&) {}
+void IconDatabase::setIconURLForPageURL(const String& iconURL, const String& pageURL) {}
+
+IconLoadDecision IconDatabase::loadDecisionForIconURL(const String&, DocumentLoader*) { return IconLoadNo; }
+bool IconDatabase::iconDataKnownForIconURL(const String&) { return false; }
+
+void IconDatabase::setEnabled(bool enabled) {}
+bool IconDatabase::isEnabled() const { return false; }
+
+void IconDatabase::setPrivateBrowsingEnabled(bool flag) {}
+bool IconDatabase::isPrivateBrowsingEnabled() const { return false; }
+
+void IconDatabase::delayDatabaseCleanup() {}
+void IconDatabase::allowDatabaseCleanup() {}
+void IconDatabase::checkIntegrityBeforeOpening() {}
+
+// Support for WebCoreStatistics in WebKit
+size_t IconDatabase::pageURLMappingCount() { return 0; }
+size_t IconDatabase::retainedPageURLCount() {return 0; }
+size_t IconDatabase::iconRecordCount() { return 0; }
+size_t IconDatabase::iconRecordCountWithData() { return 0; }
+
+bool IconDatabase::isOpen() const { return false; }
+String IconDatabase::databasePath() const { return String(); }
+String IconDatabase::defaultDatabaseFilename() { return String(); }
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/mac/DocumentLoaderMac.cpp b/Source/WebCore/loader/mac/DocumentLoaderMac.cpp
new file mode 100644
index 0000000..8cc40d2
--- /dev/null
+++ b/Source/WebCore/loader/mac/DocumentLoaderMac.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DocumentLoader.h"
+#include "MainResourceLoader.h"
+#include "ResourceHandle.h"
+#include "ResourceLoader.h"
+#include <wtf/UnusedParam.h>
+
+namespace WebCore {
+
+#ifndef BUILDING_ON_TIGER
+static void scheduleAll(const ResourceLoaderSet& loaders, SchedulePair* pair)
+{
+ const ResourceLoaderSet copy = loaders;
+ ResourceLoaderSet::const_iterator end = copy.end();
+ for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it)
+ if (ResourceHandle* handle = (*it)->handle())
+ handle->schedule(pair);
+}
+
+static void unscheduleAll(const ResourceLoaderSet& loaders, SchedulePair* pair)
+{
+ const ResourceLoaderSet copy = loaders;
+ ResourceLoaderSet::const_iterator end = copy.end();
+ for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it)
+ if (ResourceHandle* handle = (*it)->handle())
+ handle->unschedule(pair);
+}
+#endif
+
+void DocumentLoader::schedule(SchedulePair* pair)
+{
+#ifndef BUILDING_ON_TIGER
+ if (m_mainResourceLoader && m_mainResourceLoader->handle())
+ m_mainResourceLoader->handle()->schedule(pair);
+ scheduleAll(m_subresourceLoaders, pair);
+ scheduleAll(m_plugInStreamLoaders, pair);
+ scheduleAll(m_multipartSubresourceLoaders, pair);
+#else
+ UNUSED_PARAM(pair);
+#endif
+}
+
+void DocumentLoader::unschedule(SchedulePair* pair)
+{
+#ifndef BUILDING_ON_TIGER
+ if (m_mainResourceLoader && m_mainResourceLoader->handle())
+ m_mainResourceLoader->handle()->unschedule(pair);
+ unscheduleAll(m_subresourceLoaders, pair);
+ unscheduleAll(m_plugInStreamLoaders, pair);
+ unscheduleAll(m_multipartSubresourceLoaders, pair);
+#else
+ UNUSED_PARAM(pair);
+#endif
+}
+
+} // namespace
diff --git a/Source/WebCore/loader/mac/LoaderNSURLExtras.h b/Source/WebCore/loader/mac/LoaderNSURLExtras.h
new file mode 100644
index 0000000..ce5a490
--- /dev/null
+++ b/Source/WebCore/loader/mac/LoaderNSURLExtras.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Foundation/Foundation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NSString *suggestedFilenameWithMIMEType(NSURL *url, NSString *MIMEType);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Source/WebCore/loader/mac/LoaderNSURLExtras.mm b/Source/WebCore/loader/mac/LoaderNSURLExtras.mm
new file mode 100644
index 0000000..9a507f5
--- /dev/null
+++ b/Source/WebCore/loader/mac/LoaderNSURLExtras.mm
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2005, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "LoaderNSURLExtras.h"
+
+#import <wtf/Assertions.h>
+#import <wtf/Vector.h>
+#import "KURL.h"
+#import "LocalizedStrings.h"
+#import "MIMETypeRegistry.h"
+#import "PlatformString.h"
+#import "WebCoreNSStringExtras.h"
+
+using namespace WebCore;
+
+static bool vectorContainsString(const Vector<String>& vector, const String& string)
+{
+ int size = vector.size();
+ for (int i = 0; i < size; i++)
+ if (vector[i] == string)
+ return true;
+ return false;
+}
+
+NSString *suggestedFilenameWithMIMEType(NSURL *url, NSString *MIMEType)
+{
+ // Get the filename from the URL. Try the lastPathComponent first.
+ NSString *lastPathComponent = [[url path] lastPathComponent];
+ NSString *filename = filenameByFixingIllegalCharacters(lastPathComponent);
+ NSString *extension = nil;
+
+ if ([filename length] == 0 || [lastPathComponent isEqualToString:@"/"]) {
+ // lastPathComponent is no good, try the host.
+ NSString *host = KURL(url).host();
+ filename = filenameByFixingIllegalCharacters(host);
+ if ([filename length] == 0) {
+ // Can't make a filename using this URL, use "unknown".
+ filename = copyImageUnknownFileLabel();
+ }
+ } else {
+ // Save the extension for later correction. Only correct the extension of the lastPathComponent.
+ // For example, if the filename ends up being the host, we wouldn't want to correct ".com" in "www.apple.com".
+ extension = [filename pathExtension];
+ }
+
+ // No mime type reported. Just return the filename we have now.
+ if (!MIMEType) {
+ return filename;
+ }
+
+ // Do not correct filenames that are reported with a mime type of tar, and
+ // have a filename which has .tar in it or ends in .tgz
+ if (([MIMEType isEqualToString:@"application/tar"] || [MIMEType isEqualToString:@"application/x-tar"])
+ && (hasCaseInsensitiveSubstring(filename, @".tar")
+ || hasCaseInsensitiveSuffix(filename, @".tgz"))) {
+ return filename;
+ }
+
+ // I don't think we need to worry about this for the image case
+ // If the type is known, check the extension and correct it if necessary.
+ if (![MIMEType isEqualToString:@"application/octet-stream"] && ![MIMEType isEqualToString:@"text/plain"]) {
+ Vector<String> extensions = MIMETypeRegistry::getExtensionsForMIMEType(MIMEType);
+
+ if (extensions.isEmpty() || !vectorContainsString(extensions, extension)) {
+ // The extension doesn't match the MIME type. Correct this.
+ NSString *correctExtension = MIMETypeRegistry::getPreferredExtensionForMIMEType(MIMEType);
+ if ([correctExtension length] != 0) {
+ // Append the correct extension.
+ filename = [filename stringByAppendingPathExtension:correctExtension];
+ }
+ }
+ }
+
+ return filename;
+}
diff --git a/Source/WebCore/loader/mac/ResourceLoaderMac.mm b/Source/WebCore/loader/mac/ResourceLoaderMac.mm
new file mode 100644
index 0000000..3835517
--- /dev/null
+++ b/Source/WebCore/loader/mac/ResourceLoaderMac.mm
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ResourceLoader.h"
+
+#if !USE(CFNETWORK)
+
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "ResourceHandle.h"
+
+namespace WebCore {
+
+NSCachedURLResponse* ResourceLoader::willCacheResponse(ResourceHandle*, NSCachedURLResponse* response)
+{
+ if (!m_sendResourceLoadCallbacks)
+ return 0;
+ return frameLoader()->client()->willCacheResponse(documentLoader(), identifier(), response);
+}
+
+}
+
+#endif // !USE(CFNETWORK)
diff --git a/Source/WebCore/loader/win/DocumentLoaderWin.cpp b/Source/WebCore/loader/win/DocumentLoaderWin.cpp
new file mode 100644
index 0000000..bab7de6
--- /dev/null
+++ b/Source/WebCore/loader/win/DocumentLoaderWin.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2006 Don Gibson <dgibson77@gmail.com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DocumentLoader.h"
+
+#include "FrameWin.h"
+#include "PlatformString.h"
+
+namespace WebCore {
+
+void DocumentLoader::setTitle(const String& title)
+{
+ String text = title;
+ text.replace('//', m_frame->backslashAsCurrencySymbol());
+
+ FrameWin* frameWin = static_cast<FrameWin*>(m_frame);
+ if (frameWin->client())
+ frameWin->client()->setTitle(text);
+}
+
+}
diff --git a/Source/WebCore/loader/win/FrameLoaderWin.cpp b/Source/WebCore/loader/win/FrameLoaderWin.cpp
new file mode 100644
index 0000000..66aa6ff
--- /dev/null
+++ b/Source/WebCore/loader/win/FrameLoaderWin.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2006 Don Gibson <dgibson77@gmail.com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FrameLoader.h"
+
+#include "DocumentLoader.h"
+#include "FrameLoadRequest.h"
+#include "FrameWin.h"
+#include "ResourceRequest.h"
+
+namespace WebCore {
+
+void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* /*triggering Event*/)
+{
+ FrameWin* frameWin = static_cast<FrameWin*>(m_frame);
+ if (frameWin->client())
+ frameWin->client()->openURL(request.resourceRequest().url().string(), request.lockHistory());
+}
+
+void FrameLoader::submitForm(const FrameLoadRequest& request, Event*)
+{
+ const ResourceRequest& resourceRequest = request.resourceRequest();
+
+#ifdef MULTIPLE_FORM_SUBMISSION_PROTECTION
+ // FIXME: this is a hack inherited from FrameMac, and should be pushed into Frame
+ if (m_submittedFormURL == resourceRequest.url())
+ return;
+ m_submittedFormURL = resourceRequest.url();
+#endif
+
+ FrameWin* frameWin = static_cast<FrameWin*>(m_frame);
+ if (frameWin->client())
+ frameWin->client()->submitForm(resourceRequest.httpMethod(), resourceRequest.url(), resourceRequest.httpBody());
+
+ clearRecordedFormValues();
+}
+
+}