summaryrefslogtreecommitdiffstats
path: root/WebCore/loader/CachedResource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/loader/CachedResource.cpp')
-rw-r--r--WebCore/loader/CachedResource.cpp316
1 files changed, 316 insertions, 0 deletions
diff --git a/WebCore/loader/CachedResource.cpp b/WebCore/loader/CachedResource.cpp
new file mode 100644
index 0000000..6d0af9b
--- /dev/null
+++ b/WebCore/loader/CachedResource.cpp
@@ -0,0 +1,316 @@
+/*
+ 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 "CachedResource.h"
+
+#include "Cache.h"
+#include "CachedResourceHandle.h"
+#include "DocLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "KURL.h"
+#include "Request.h"
+#include "SystemTime.h"
+#include <wtf/RefCountedLeakCounter.h>
+#include <wtf/Vector.h>
+
+using namespace WTF;
+
+namespace WebCore {
+
+#ifndef NDEBUG
+static RefCountedLeakCounter cachedResourceLeakCounter("CachedResource");
+#endif
+
+CachedResource::CachedResource(const String& url, Type type)
+ : m_url(url)
+ , m_lastDecodedAccessTime(0)
+ , m_sendResourceLoadCallbacks(true)
+ , m_preloadCount(0)
+ , m_preloadResult(PreloadNotReferenced)
+ , m_requestedFromNetworkingLayer(false)
+ , m_inCache(false)
+ , m_loading(false)
+ , m_docLoader(0)
+ , m_handleCount(0)
+ , m_resourceToRevalidate(0)
+ , m_isBeingRevalidated(false)
+ , m_expirationDate(0)
+{
+#ifndef NDEBUG
+ cachedResourceLeakCounter.increment();
+#endif
+
+ m_type = type;
+ m_status = Pending;
+ m_encodedSize = 0;
+ m_decodedSize = 0;
+ m_request = 0;
+
+ m_accessCount = 0;
+ m_inLiveDecodedResourcesList = false;
+
+ m_nextInAllResourcesList = 0;
+ m_prevInAllResourcesList = 0;
+
+ m_nextInLiveResourcesList = 0;
+ m_prevInLiveResourcesList = 0;
+
+#ifndef NDEBUG
+ m_deleted = false;
+ m_lruIndex = 0;
+#endif
+ m_errorOccurred = false;
+}
+
+CachedResource::~CachedResource()
+{
+ ASSERT(!inCache());
+ ASSERT(!m_deleted);
+ ASSERT(url().isNull() || cache()->resourceForURL(url()) != this);
+#ifndef NDEBUG
+ m_deleted = true;
+ cachedResourceLeakCounter.decrement();
+#endif
+
+ if (m_resourceToRevalidate)
+ m_resourceToRevalidate->m_isBeingRevalidated = false;
+
+ if (m_docLoader)
+ m_docLoader->removeCachedResource(this);
+}
+
+void CachedResource::load(DocLoader* docLoader, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks)
+{
+ m_sendResourceLoadCallbacks = sendResourceLoadCallbacks;
+ cache()->loader()->load(docLoader, this, incremental, skipCanLoadCheck, sendResourceLoadCallbacks);
+ m_loading = true;
+}
+
+void CachedResource::finish()
+{
+ m_status = Cached;
+}
+
+bool CachedResource::isExpired() const
+{
+ if (!m_expirationDate)
+ return false;
+ time_t now = time(0);
+ return difftime(now, m_expirationDate) >= 0;
+}
+
+void CachedResource::setResponse(const ResourceResponse& response)
+{
+ m_response = response;
+ m_expirationDate = response.expirationDate();
+}
+
+void CachedResource::setRequest(Request* request)
+{
+ if (request && !m_request)
+ m_status = Pending;
+ m_request = request;
+ if (canDelete() && !inCache())
+ delete this;
+}
+
+void CachedResource::addClient(CachedResourceClient *c)
+{
+ 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(c);
+}
+
+void CachedResource::removeClient(CachedResourceClient *c)
+{
+ ASSERT(m_clients.contains(c));
+ m_clients.remove(c);
+ if (canDelete() && !inCache())
+ delete this;
+ else if (!hasClients() && inCache()) {
+ cache()->removeFromLiveResourcesSize(this);
+ cache()->removeFromLiveDecodedResourcesList(this);
+ allClientsRemoved();
+ cache()->prune();
+ }
+}
+
+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.
+ 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(!resource->m_isBeingRevalidated);
+ ASSERT(m_handlesToRevalidate.isEmpty());
+ ASSERT(resource->type() == type());
+ resource->m_isBeingRevalidated = true;
+ m_resourceToRevalidate = resource;
+}
+
+void CachedResource::clearResourceToRevalidate()
+{
+ ASSERT(m_resourceToRevalidate);
+ ASSERT(m_resourceToRevalidate->m_isBeingRevalidated);
+ m_resourceToRevalidate->m_isBeingRevalidated = false;
+ m_resourceToRevalidate->deleteIfPossible();
+ m_handlesToRevalidate.clear();
+ m_resourceToRevalidate = 0;
+ deleteIfPossible();
+}
+
+void CachedResource::switchClientsToRevalidatedResource()
+{
+ ASSERT(m_resourceToRevalidate);
+ ASSERT(!inCache());
+
+ 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->addClient(clientsToMove[n]);
+}
+
+bool CachedResource::canUseCacheValidator() const
+{
+ return !m_loading && (!m_response.httpHeaderField("Last-Modified").isEmpty() || !m_response.httpHeaderField("ETag").isEmpty());
+}
+
+bool CachedResource::mustRevalidate(CachePolicy cachePolicy) const
+{
+ if (m_loading)
+ return false;
+ String cacheControl = m_response.httpHeaderField("Cache-Control");
+ // FIXME: It would be better to tokenize the field.
+ if (cachePolicy == CachePolicyCache)
+ return !cacheControl.isEmpty() && (cacheControl.contains("no-cache", false) || (isExpired() && cacheControl.contains("must-revalidate", false)));
+ return isExpired() || cacheControl.contains("no-cache", false);
+}
+
+}