summaryrefslogtreecommitdiffstats
path: root/WebCore/loader/cache
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/loader/cache')
-rw-r--r--WebCore/loader/cache/CachePolicy.h41
-rw-r--r--WebCore/loader/cache/CachedCSSStyleSheet.cpp151
-rw-r--r--WebCore/loader/cache/CachedCSSStyleSheet.h67
-rw-r--r--WebCore/loader/cache/CachedFont.cpp216
-rw-r--r--WebCore/loader/cache/CachedFont.h89
-rw-r--r--WebCore/loader/cache/CachedImage.cpp393
-rw-r--r--WebCore/loader/cache/CachedImage.h105
-rw-r--r--WebCore/loader/cache/CachedResource.cpp565
-rw-r--r--WebCore/loader/cache/CachedResource.h289
-rw-r--r--WebCore/loader/cache/CachedResourceClient.h71
-rw-r--r--WebCore/loader/cache/CachedResourceClientWalker.cpp53
-rw-r--r--WebCore/loader/cache/CachedResourceClientWalker.h49
-rw-r--r--WebCore/loader/cache/CachedResourceHandle.cpp42
-rw-r--r--WebCore/loader/cache/CachedResourceHandle.h104
-rw-r--r--WebCore/loader/cache/CachedResourceLoader.cpp545
-rw-r--r--WebCore/loader/cache/CachedResourceLoader.h144
-rw-r--r--WebCore/loader/cache/CachedScript.cpp123
-rw-r--r--WebCore/loader/cache/CachedScript.h65
-rw-r--r--WebCore/loader/cache/CachedXSLStyleSheet.cpp99
-rw-r--r--WebCore/loader/cache/CachedXSLStyleSheet.h62
-rw-r--r--WebCore/loader/cache/MemoryCache.cpp788
-rw-r--r--WebCore/loader/cache/MemoryCache.h240
22 files changed, 4301 insertions, 0 deletions
diff --git a/WebCore/loader/cache/CachePolicy.h b/WebCore/loader/cache/CachePolicy.h
new file mode 100644
index 0000000..2639caa
--- /dev/null
+++ b/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,
+ CachePolicyAllowStale
+ };
+
+}
+
+#endif
diff --git a/WebCore/loader/cache/CachedCSSStyleSheet.cpp b/WebCore/loader/cache/CachedCSSStyleSheet.cpp
new file mode 100644
index 0000000..f0016d1
--- /dev/null
+++ b/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 "loader.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()
+{
+ setLoading(false);
+ setErrorOccurred(true);
+ 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/WebCore/loader/cache/CachedCSSStyleSheet.h b/WebCore/loader/cache/CachedCSSStyleSheet.h
new file mode 100644
index 0000000..abcdb85
--- /dev/null
+++ b/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();
+
+ 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/WebCore/loader/cache/CachedFont.cpp b/WebCore/loader/cache/CachedFont.cpp
new file mode 100644
index 0000000..6297ad1
--- /dev/null
+++ b/WebCore/loader/cache/CachedFont.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 "MemoryCache.h"
+#include "CachedResourceClient.h"
+#include "CachedResourceClientWalker.h"
+#include "FontPlatformData.h"
+#include "SharedBuffer.h"
+#include "TextResourceDecoder.h"
+#include "loader.h"
+#include <wtf/Vector.h>
+
+#ifdef STORE_FONT_CUSTOM_PLATFORM_DATA
+#include "FontCustomPlatformData.h"
+#endif
+
+#if ENABLE(SVG_FONTS)
+#include "HTMLNames.h"
+#include "NodeList.h"
+#include "SVGElement.h"
+#include "SVGFontElement.h"
+#include "SVGGElement.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;
+ cache()->loader()->load(dl, 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)
+ setErrorOccurred(true);
+ }
+#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()
+{
+ setLoading(false);
+ setErrorOccurred(true);
+ checkNotify();
+}
+
+}
diff --git a/WebCore/loader/cache/CachedFont.h b/WebCore/loader/cache/CachedFont.h
new file mode 100644
index 0000000..e1a34e8
--- /dev/null
+++ b/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();
+
+ 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/WebCore/loader/cache/CachedImage.cpp b/WebCore/loader/cache/CachedImage.cpp
new file mode 100644
index 0000000..ce1c9a3
--- /dev/null
+++ b/WebCore/loader/cache/CachedImage.cpp
@@ -0,0 +1,393 @@
+/*
+ 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 "Frame.h"
+#include "FrameLoaderTypes.h"
+#include "FrameView.h"
+#include "Request.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+#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)
+ , m_httpStatusCodeErrorOccurred(false)
+{
+ setStatus(Unknown);
+}
+
+CachedImage::CachedImage(Image* image)
+ : CachedResource(String(), ImageResource)
+ , m_image(image)
+ , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
+ , m_httpStatusCodeErrorOccurred(false)
+{
+ 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();
+}
+
+static Image* nullImage()
+{
+ DEFINE_STATIC_LOCAL(RefPtr<BitmapImage>, nullImage, (BitmapImage::create()));
+ return nullImage.get();
+}
+
+Image* CachedImage::image() const
+{
+ ASSERT(!isPurgeable());
+
+ if (errorOccurred())
+ return brokenImage();
+
+ if (m_image)
+ return m_image.get();
+
+ return 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();
+ 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()
+{
+ clear();
+ setErrorOccurred(true);
+ 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/WebCore/loader/cache/CachedImage.h b/WebCore/loader/cache/CachedImage.h
new file mode 100644
index 0000000..313f3f3
--- /dev/null
+++ b/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;
+
+ 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();
+
+ virtual void httpStatusCodeError() { m_httpStatusCodeErrorOccurred = true; }
+ bool httpStatusCodeErrorOccurred() const { return m_httpStatusCodeErrorOccurred; }
+
+ 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;
+ bool m_httpStatusCodeErrorOccurred;
+};
+
+}
+
+#endif
diff --git a/WebCore/loader/cache/CachedResource.cpp b/WebCore/loader/cache/CachedResource.cpp
new file mode 100644
index 0000000..c440ec9
--- /dev/null
+++ b/WebCore/loader/cache/CachedResource.cpp
@@ -0,0 +1,565 @@
+/*
+ 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 "Frame.h"
+#include "FrameLoaderClient.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "PurgeableBuffer.h"
+#include "Request.h"
+#include "ResourceHandle.h"
+#include "SharedBuffer.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
+#include <wtf/RefCountedLeakCounter.h>
+#include <wtf/StdLibExtras.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_request(0)
+ , 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_errorOccurred(false)
+ , 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_cachedResourceLoader(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(url()) != this);
+#ifndef NDEBUG
+ m_deleted = true;
+ cachedResourceLeakCounter.decrement();
+#endif
+
+ if (m_cachedResourceLoader)
+ m_cachedResourceLoader->removeCachedResource(this);
+}
+
+void CachedResource::load(CachedResourceLoader* cachedResourceLoader, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
+{
+ m_sendResourceLoadCallbacks = sendResourceLoadCallbacks;
+ cache()->loader()->load(cachedResourceLoader, this, incremental, securityCheck, sendResourceLoadCallbacks);
+ m_loading = true;
+}
+
+void CachedResource::data(PassRefPtr<SharedBuffer>, bool allDataReceived)
+{
+ if (!allDataReceived)
+ return;
+
+ CachedResourceClientWalker w(m_clients);
+ while (CachedResourceClient* c = w.next())
+ c->notifyFinished(this);
+}
+
+void CachedResource::finish()
+{
+ m_status = Cached;
+}
+
+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(Request* 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"
+ 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 || m_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::mustRevalidate(CachePolicy cachePolicy) const
+{
+ if (m_errorOccurred) {
+ LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_errorOccurred\n", this);
+ return true;
+ }
+
+ if (m_loading)
+ return false;
+
+ if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) {
+ LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this);
+ return true;
+ }
+
+ if (cachePolicy == CachePolicyCache) {
+ if (m_response.cacheControlContainsMustRevalidate() && isExpired()) {
+ LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this);
+ return true;
+ }
+ return false;
+ }
+
+ if (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
+ */
+}
+
+}
diff --git a/WebCore/loader/cache/CachedResource.h b/WebCore/loader/cache/CachedResource.h
new file mode 100644
index 0000000..ba02459
--- /dev/null
+++ b/WebCore/loader/cache/CachedResource.h
@@ -0,0 +1,289 @@
+/*
+ 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 "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 Frame;
+class InspectorResource;
+class PurgeableBuffer;
+class Request;
+
+// A resource that is held in the cache. Classes who want to use this object should derive
+// from CachedResourceClient, to get the function calls in case the requested data has arrived.
+// This class also does the actual communication with the loader to obtain the resource from the network.
+class CachedResource : public Noncopyable {
+ friend class 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
+ };
+
+ 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() { }
+ virtual void httpStatusCodeError() { error(); } // Images keep loading in spite of HTTP errors (for legacy compat with <img>, etc.).
+
+ const String &url() const { return m_url; }
+ Type type() const { return static_cast<Type>(m_type); }
+
+ 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(Request*);
+
+ 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 m_errorOccurred; }
+ void setErrorOccurred(bool b) { m_errorOccurred = b; }
+
+ bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; }
+
+ virtual void destroyDecodedData() { }
+
+ void setCachedResourceLoader(CachedResourceLoader* cachedResourceLoader) { m_cachedResourceLoader = cachedResourceLoader; }
+
+ bool isPreloaded() const { return m_preloadCount; }
+ void increasePreloadCount() { ++m_preloadCount; }
+ void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; }
+
+ void registerHandle(CachedResourceHandleBase* h);
+ void unregisterHandle(CachedResourceHandleBase* h);
+
+ bool canUseCacheValidator() const;
+ bool mustRevalidate(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);
+
+protected:
+ void setEncodedSize(unsigned);
+ void setDecodedSize(unsigned);
+ void didAccessDecodedData(double timeStamp);
+
+ bool isSafeToMakePurgeable() const;
+
+ HashCountedSet<CachedResourceClient*> m_clients;
+
+ String m_url;
+ String m_accept;
+ Request* m_request;
+
+ ResourceResponse m_response;
+ double m_responseTimestamp;
+
+ RefPtr<SharedBuffer> m_data;
+ OwnPtr<PurgeableBuffer> m_purgeableData;
+
+private:
+ void addClientToSet(CachedResourceClient*);
+
+ // These are called by the friendly MemoryCache only
+ void setResourceToRevalidate(CachedResource*);
+ void switchClientsToRevalidatedResource();
+ void clearResourceToRevalidate();
+ void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse);
+ virtual PurgePriority purgePriority() const { return PurgeDefault; }
+
+ double currentAge() const;
+ double freshnessLifetime() const;
+
+ RefPtr<CachedMetadata> m_cachedMetadata;
+
+ double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache
+
+ unsigned m_encodedSize;
+ unsigned m_decodedSize;
+ unsigned m_accessCount;
+ unsigned m_handleCount;
+ unsigned m_preloadCount;
+
+ unsigned m_preloadResult : 2; // PreloadResult
+
+ bool m_inLiveDecodedResourcesList : 1;
+ bool m_requestedFromNetworkingLayer : 1;
+ bool m_sendResourceLoadCallbacks : 1;
+
+ bool m_errorOccurred : 1;
+ bool m_inCache : 1;
+ bool m_loading : 1;
+
+ unsigned m_type : 3; // Type
+ unsigned m_status : 2; // Status
+
+#ifndef NDEBUG
+ bool m_deleted;
+ unsigned m_lruIndex;
+#endif
+
+ CachedResource* m_nextInAllResourcesList;
+ CachedResource* m_prevInAllResourcesList;
+
+ CachedResource* m_nextInLiveResourcesList;
+ CachedResource* m_prevInLiveResourcesList;
+
+ CachedResourceLoader* m_cachedResourceLoader; // 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/WebCore/loader/cache/CachedResourceClient.h b/WebCore/loader/cache/CachedResourceClient.h
new file mode 100644
index 0000000..275d331
--- /dev/null
+++ b/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/WebCore/loader/cache/CachedResourceClientWalker.cpp b/WebCore/loader/cache/CachedResourceClientWalker.cpp
new file mode 100644
index 0000000..142a2a1
--- /dev/null
+++ b/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/WebCore/loader/cache/CachedResourceClientWalker.h b/WebCore/loader/cache/CachedResourceClientWalker.h
new file mode 100644
index 0000000..d079584
--- /dev/null
+++ b/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/WebCore/loader/cache/CachedResourceHandle.cpp b/WebCore/loader/cache/CachedResourceHandle.cpp
new file mode 100644
index 0000000..871292c
--- /dev/null
+++ b/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/WebCore/loader/cache/CachedResourceHandle.h b/WebCore/loader/cache/CachedResourceHandle.h
new file mode 100644
index 0000000..7d485bf
--- /dev/null
+++ b/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/WebCore/loader/cache/CachedResourceLoader.cpp b/WebCore/loader/cache/CachedResourceLoader.cpp
new file mode 100644
index 0000000..29d1204
--- /dev/null
+++ b/WebCore/loader/cache/CachedResourceLoader.cpp
@@ -0,0 +1,545 @@
+/*
+ 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 "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 "MemoryCache.h"
+#include "PingLoader.h"
+#include "SecurityOrigin.h"
+#include "Settings.h"
+#include "loader.h"
+#include <wtf/text/StringConcatenate.h>
+
+#define PRELOAD_DEBUG 0
+
+namespace WebCore {
+
+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_loadInProgress(false)
+ , m_allowStaleResources(false)
+{
+ m_cache->addCachedResourceLoader(this);
+}
+
+CachedResourceLoader::~CachedResourceLoader()
+{
+ if (m_requestCount)
+ m_cache->loader()->cancelRequests(this);
+
+ clearPreloads();
+ DocumentResourceMap::iterator end = m_documentResources.end();
+ for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
+ it->second->setCachedResourceLoader(0);
+ m_cache->removeCachedResourceLoader(this);
+
+ // Make sure no requests still point to this CachedResourceLoader
+ ASSERT(m_requestCount == 0);
+}
+
+Frame* CachedResourceLoader::frame() const
+{
+ return m_document->frame();
+}
+
+void CachedResourceLoader::checkForReload(const KURL& fullURL)
+{
+ if (m_allowStaleResources)
+ return; // Don't reload resources while pasting
+
+ if (fullURL.isEmpty())
+ return;
+
+ if (m_reloadedURLs.contains(fullURL.string()))
+ return;
+
+ CachedResource* existing = cache()->resourceForURL(fullURL.string());
+ if (!existing || existing->isPreloaded())
+ return;
+
+ switch (cachePolicy()) {
+ case CachePolicyVerify:
+ if (!existing->mustRevalidate(CachePolicyVerify))
+ return;
+ cache()->revalidateResource(existing, this);
+ break;
+ case CachePolicyCache:
+ if (!existing->mustRevalidate(CachePolicyCache))
+ return;
+ cache()->revalidateResource(existing, this);
+ break;
+ case CachePolicyReload:
+ cache()->remove(existing);
+ break;
+ case CachePolicyRevalidate:
+ cache()->revalidateResource(existing, this);
+ break;
+ case CachePolicyAllowStale:
+ return;
+ }
+
+ m_reloadedURLs.add(fullURL.string());
+}
+
+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);
+ cache()->loader()->load(this, 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)
+{
+ return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset));
+}
+
+CachedCSSStyleSheet* CachedResourceLoader::requestUserCSSStyleSheet(const String& url, const String& charset)
+{
+ return cache()->requestUserCSSStyleSheet(this, url, charset);
+}
+
+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& url, const String& charset, bool isPreload)
+{
+ KURL fullURL = m_document->completeURL(url);
+
+ if (!fullURL.isValid() || !canRequest(type, fullURL))
+ return 0;
+
+ if (cache()->disabled()) {
+ DocumentResourceMap::iterator it = m_documentResources.find(fullURL.string());
+
+ if (it != m_documentResources.end()) {
+ it->second->setCachedResourceLoader(0);
+ m_documentResources.remove(it);
+ }
+ }
+
+ checkForReload(fullURL);
+
+ CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, isPreload);
+ if (resource) {
+ // Check final URL of resource to catch redirects.
+ // See <https://bugs.webkit.org/show_bug.cgi?id=21963>.
+ if (fullURL != resource->url() && !canRequest(type, KURL(ParsedURLString, resource->url())))
+ return 0;
+
+ m_documentResources.set(resource->url(), resource);
+ checkCacheObjectStatus(resource);
+ }
+ return resource;
+}
+
+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())
+ cache()->loader()->load(this, image, true);
+ }
+ }
+}
+
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+bool CachedResourceLoader::shouldBlockNetworkImage(const String& url) const
+{
+ if (!m_blockNetworkImage)
+ return false;
+
+ KURL kurl = m_doc->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())
+ cache()->loader()->load(this, 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::setLoadInProgress(bool load)
+{
+ m_loadInProgress = load;
+ if (!load && frame())
+ frame()->loader()->loadDone();
+}
+
+void CachedResourceLoader::checkCacheObjectStatus(CachedResource* resource)
+{
+ // Return from the function for objects that we didn't load from the cache or if we don't have a frame.
+ if (!resource || !frame() || 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 (loadInProgress())
+ 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, 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/WebCore/loader/cache/CachedResourceLoader.h b/WebCore/loader/cache/CachedResourceLoader.h
new file mode 100644
index 0000000..eaed52e
--- /dev/null
+++ b/WebCore/loader/cache/CachedResourceLoader.h
@@ -0,0 +1,144 @@
+/*
+ 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 <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 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);
+ 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 { return m_documentResources.get(url).get(); }
+
+ 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 setLoadInProgress(bool);
+ bool loadInProgress() const { return m_loadInProgress; }
+
+ 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, bool isPreload = false);
+ void requestPreload(CachedResource::Type, const String& url, const String& charset);
+
+ void checkForReload(const KURL&);
+ void checkCacheObjectStatus(CachedResource*);
+ bool canRequest(CachedResource::Type, const KURL&);
+
+ MemoryCache* m_cache;
+ HashSet<String> m_reloadedURLs;
+ mutable DocumentResourceMap m_documentResources;
+ Document* m_document;
+
+ 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_loadInProgress : 1;
+ bool m_allowStaleResources : 1;
+};
+
+}
+
+#endif
diff --git a/WebCore/loader/cache/CachedScript.cpp b/WebCore/loader/cache/CachedScript.cpp
new file mode 100644
index 0000000..50a8c17
--- /dev/null
+++ b/WebCore/loader/cache/CachedScript.cpp
@@ -0,0 +1,123 @@
+/*
+ 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()
+{
+ setLoading(false);
+ setErrorOccurred(true);
+ 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/WebCore/loader/cache/CachedScript.h b/WebCore/loader/cache/CachedScript.h
new file mode 100644
index 0000000..7311f9b
--- /dev/null
+++ b/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();
+
+ 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/WebCore/loader/cache/CachedXSLStyleSheet.cpp b/WebCore/loader/cache/CachedXSLStyleSheet.cpp
new file mode 100644
index 0000000..5b30e30
--- /dev/null
+++ b/WebCore/loader/cache/CachedXSLStyleSheet.cpp
@@ -0,0 +1,99 @@
+/*
+ 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()
+{
+ setLoading(false);
+ setErrorOccurred(true);
+ checkNotify();
+}
+
+#endif
+
+}
diff --git a/WebCore/loader/cache/CachedXSLStyleSheet.h b/WebCore/loader/cache/CachedXSLStyleSheet.h
new file mode 100644
index 0000000..8587b0b
--- /dev/null
+++ b/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();
+
+ void checkNotify();
+
+ protected:
+ String m_sheet;
+ RefPtr<TextResourceDecoder> m_decoder;
+ };
+
+#endif
+
+}
+
+#endif
diff --git a/WebCore/loader/cache/MemoryCache.cpp b/WebCore/loader/cache/MemoryCache.cpp
new file mode 100644
index 0000000..25af774
--- /dev/null
+++ b/WebCore/loader/cache/MemoryCache.cpp
@@ -0,0 +1,788 @@
+/*
+ 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>
+
+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)
+{
+}
+
+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
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+CachedResource* MemoryCache::requestResource(CachedResourceLoader* cachedResourceLoader, CachedResource::Type type, const KURL& url, const String& charset, bool requestIsPreload)
+{
+ LOG(ResourceLoading, "MemoryCache::requestResource '%s', charset '%s', preload=%u", url.string().latin1().data(), charset.latin1().data(), requestIsPreload);
+
+ // FIXME: Do we really need to special-case an empty URL?
+ // Would it be better to just go on with the cache code and let it fail later?
+ if (url.isEmpty())
+ return 0;
+
+ // Look up the resource in our map.
+ CachedResource* resource = resourceForURL(url.string());
+
+ if (resource && requestIsPreload && !resource->isPreloaded()) {
+ LOG(ResourceLoading, "MemoryCache::requestResource already has a preload request for this request, and it hasn't been preloaded yet");
+ return 0;
+ }
+
+ if (!cachedResourceLoader->document()->securityOrigin()->canDisplay(url)) {
+ LOG(ResourceLoading, "...URL was not allowed by SecurityOrigin");
+ if (!requestIsPreload)
+ FrameLoader::reportLocalLoadFailed(cachedResourceLoader->document()->frame(), url.string());
+ return 0;
+ }
+
+ if (!resource) {
+ LOG(ResourceLoading, "CachedResource for '%s' wasn't found in cache. Creating it", url.string().latin1().data());
+ // The resource does not exist. Create it.
+ resource = createResource(type, url, charset);
+ ASSERT(resource);
+
+ // 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.
+ resource->setInCache(true);
+
+ resource->load(cachedResourceLoader);
+
+ if (resource->errorOccurred()) {
+ // We don't support immediate loads, but we do support immediate failure.
+ // In that case we should to delete the resource now and return 0 because otherwise
+ // it would leak if no ref/deref was ever done on it.
+ resource->setInCache(false);
+ delete resource;
+ return 0;
+ }
+
+ if (!disabled())
+ m_resources.set(url.string(), resource); // The size will be added in later once the resource is loaded and calls back to us with the new size.
+ else {
+ // Kick the resource out of the cache, because the cache is disabled.
+ resource->setInCache(false);
+ resource->setCachedResourceLoader(cachedResourceLoader);
+ }
+ }
+
+ if (resource->type() != type) {
+ LOG(ResourceLoading, "MemoryCache::requestResource cannot use cached resource for '%s' due to type mismatch", url.string().latin1().data());
+ return 0;
+ }
+
+ if (!disabled()) {
+ // This will move the resource to the front of its LRU list and increase its access count.
+ resourceAccessed(resource);
+ }
+
+ LOG(ResourceLoading, "MemoryCache::requestResource for '%s' returning resource %p\n", url.string().latin1().data(), resource);
+
+ return resource;
+}
+
+CachedCSSStyleSheet* MemoryCache::requestUserCSSStyleSheet(CachedResourceLoader* cachedResourceLoader, const String& url, const String& charset)
+{
+ CachedCSSStyleSheet* userSheet;
+ if (CachedResource* existing = resourceForURL(url)) {
+ if (existing->type() != CachedResource::CSSStyleSheet)
+ return 0;
+ userSheet = static_cast<CachedCSSStyleSheet*>(existing);
+ } else {
+ userSheet = new CachedCSSStyleSheet(url, charset);
+
+ // 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.
+ userSheet->setInCache(true);
+ // Don't load incrementally, skip load checks, don't send resource load callbacks.
+ userSheet->load(cachedResourceLoader, false, SkipSecurityCheck, false);
+ if (!disabled())
+ m_resources.set(url, userSheet);
+ else
+ userSheet->setInCache(false);
+ }
+
+ if (!disabled()) {
+ // This will move the resource to the front of its LRU list and increase its access count.
+ resourceAccessed(userSheet);
+ }
+
+ return userSheet;
+}
+
+void MemoryCache::revalidateResource(CachedResource* resource, CachedResourceLoader* cachedResourceLoader)
+{
+ ASSERT(resource);
+ ASSERT(resource->inCache());
+ ASSERT(resource == m_resources.get(resource->url()));
+ ASSERT(!disabled());
+ if (resource->resourceToRevalidate())
+ return;
+ if (!resource->canUseCacheValidator()) {
+ evict(resource);
+ return;
+ }
+ const String& url = resource->url();
+ CachedResource* newResource = createResource(resource->type(), KURL(ParsedURLString, url), resource->encoding());
+ LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource, resource);
+ newResource->setResourceToRevalidate(resource);
+ evict(resource);
+ m_resources.set(url, newResource);
+ newResource->setInCache(true);
+ resourceAccessed(newResource);
+ newResource->load(cachedResourceLoader);
+}
+
+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 String& url)
+{
+ CachedResource* resource = m_resources.get(url);
+ bool wasPurgeable = MemoryCache::shouldMakeResourcePurgeableOnEviction() && resource && resource->isPurgeable();
+ if (resource && !resource->makePurgeable(false)) {
+ ASSERT(!resource->hasClients());
+ evict(resource);
+ return 0;
+ }
+ // Add the size back since we had subtracted it when we marked the memory as purgeable.
+ if (wasPurgeable)
+ adjustSize(resource->hasClients(), resource->size());
+ return resource;
+}
+
+unsigned 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/WebCore/loader/cache/MemoryCache.h b/WebCore/loader/cache/MemoryCache.h
new file mode 100644
index 0000000..a40f85e
--- /dev/null
+++ b/WebCore/loader/cache/MemoryCache.h
@@ -0,0 +1,240 @@
+/*
+ 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 "loader.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/Vector.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+class CachedCSSStyleSheet;
+class CachedResource;
+class 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;
+ };
+
+ // The loader that fetches resources.
+ Loader* loader() { return &m_loader; }
+
+ // Request resources from the cache. A load will be initiated and a cache object created if the object is not
+ // found in the cache.
+ CachedResource* requestResource(CachedResourceLoader*, CachedResource::Type, const KURL& url, const String& charset, bool isPreload = false);
+
+ CachedCSSStyleSheet* requestUserCSSStyleSheet(CachedResourceLoader*, const String& url, const String& charset);
+
+ void revalidateResource(CachedResource*, CachedResourceLoader*);
+ 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; }
+
+ // Remove an existing cache entry from both the resource map and from the LRU list.
+ void remove(CachedResource* resource) { evict(resource); }
+
+ void addCachedResourceLoader(CachedResourceLoader*);
+ void removeCachedResourceLoader(CachedResourceLoader*);
+
+ CachedResource* resourceForURL(const String&);
+
+ // 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();
+
+#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*);
+ void resourceAccessed(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;
+ Loader m_loader;
+
+ 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