summaryrefslogtreecommitdiff
path: root/Source/WebCore/loader/cache
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/loader/cache')
-rw-r--r--Source/WebCore/loader/cache/CachePolicy.h26
-rw-r--r--Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp101
-rw-r--r--Source/WebCore/loader/cache/CachedCSSStyleSheet.h68
-rw-r--r--Source/WebCore/loader/cache/CachedFont.cpp155
-rw-r--r--Source/WebCore/loader/cache/CachedFont.h60
-rw-r--r--Source/WebCore/loader/cache/CachedFontClient.h9
-rw-r--r--Source/WebCore/loader/cache/CachedImage.cpp432
-rw-r--r--Source/WebCore/loader/cache/CachedImage.h155
-rw-r--r--Source/WebCore/loader/cache/CachedImageClient.h22
-rw-r--r--Source/WebCore/loader/cache/CachedRawResource.cpp164
-rw-r--r--Source/WebCore/loader/cache/CachedRawResource.h52
-rw-r--r--Source/WebCore/loader/cache/CachedRawResourceClient.h22
-rw-r--r--Source/WebCore/loader/cache/CachedResource.cpp824
-rw-r--r--Source/WebCore/loader/cache/CachedResource.h286
-rw-r--r--Source/WebCore/loader/cache/CachedResourceClient.h20
-rw-r--r--Source/WebCore/loader/cache/CachedResourceClientWalker.h9
-rw-r--r--Source/WebCore/loader/cache/CachedResourceHandle.cpp2
-rw-r--r--Source/WebCore/loader/cache/CachedResourceHandle.h9
-rw-r--r--Source/WebCore/loader/cache/CachedResourceLoader.cpp1162
-rw-r--r--Source/WebCore/loader/cache/CachedResourceLoader.h145
-rw-r--r--Source/WebCore/loader/cache/CachedResourceRequest.cpp247
-rw-r--r--Source/WebCore/loader/cache/CachedResourceRequest.h72
-rw-r--r--Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp3
-rw-r--r--Source/WebCore/loader/cache/CachedResourceRequestInitiators.h11
-rw-r--r--Source/WebCore/loader/cache/CachedSVGDocument.cpp24
-rw-r--r--Source/WebCore/loader/cache/CachedSVGDocument.h24
-rw-r--r--Source/WebCore/loader/cache/CachedSVGDocumentClient.h13
-rw-r--r--Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp18
-rw-r--r--Source/WebCore/loader/cache/CachedSVGDocumentReference.h13
-rw-r--r--Source/WebCore/loader/cache/CachedSVGFont.cpp142
-rw-r--r--Source/WebCore/loader/cache/CachedSVGFont.h61
-rw-r--r--Source/WebCore/loader/cache/CachedScript.cpp83
-rw-r--r--Source/WebCore/loader/cache/CachedScript.h55
-rw-r--r--Source/WebCore/loader/cache/CachedStyleSheetClient.h7
-rw-r--r--Source/WebCore/loader/cache/CachedTextTrack.cpp30
-rw-r--r--Source/WebCore/loader/cache/CachedTextTrack.h23
-rw-r--r--Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp32
-rw-r--r--Source/WebCore/loader/cache/CachedXSLStyleSheet.h50
-rw-r--r--Source/WebCore/loader/cache/MemoryCache.cpp922
-rw-r--r--Source/WebCore/loader/cache/MemoryCache.h209
40 files changed, 3081 insertions, 2681 deletions
diff --git a/Source/WebCore/loader/cache/CachePolicy.h b/Source/WebCore/loader/cache/CachePolicy.h
index 0b9010b63..b8a1013c3 100644
--- a/Source/WebCore/loader/cache/CachePolicy.h
+++ b/Source/WebCore/loader/cache/CachePolicy.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2003, 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,19 +23,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CachePolicy_h
-#define CachePolicy_h
+#pragma once
namespace WebCore {
- enum CachePolicy {
- CachePolicyCache,
- CachePolicyVerify,
- CachePolicyRevalidate,
- CachePolicyReload,
- CachePolicyHistoryBuffer
- };
+enum CachePolicy {
+ CachePolicyVerify,
+ CachePolicyRevalidate,
+ CachePolicyReload,
+ CachePolicyHistoryBuffer
+};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp b/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp
index 69bce8f40..af40cc204 100644
--- a/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp
+++ b/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp
@@ -3,7 +3,7 @@
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.
+ Copyright (C) 2004-2017 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
@@ -19,9 +19,6 @@
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"
@@ -29,24 +26,22 @@
#include "CSSStyleSheet.h"
#include "CachedResourceClientWalker.h"
+#include "CachedResourceRequest.h"
#include "CachedStyleSheetClient.h"
+#include "HTTPHeaderNames.h"
#include "HTTPParsers.h"
#include "MemoryCache.h"
-#include "ResourceBuffer.h"
+#include "SharedBuffer.h"
#include "StyleSheetContents.h"
#include "TextResourceDecoder.h"
#include <wtf/CurrentTime.h>
-#include <wtf/Vector.h>
namespace WebCore {
-CachedCSSStyleSheet::CachedCSSStyleSheet(const ResourceRequest& resourceRequest, const String& charset)
- : CachedResource(resourceRequest, CSSStyleSheet)
- , m_decoder(TextResourceDecoder::create("text/css", charset))
+CachedCSSStyleSheet::CachedCSSStyleSheet(CachedResourceRequest&& request, SessionID sessionID)
+ : CachedResource(WTFMove(request), CSSStyleSheet, sessionID)
+ , m_decoder(TextResourceDecoder::create("text/css", request.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()
@@ -55,16 +50,16 @@ CachedCSSStyleSheet::~CachedCSSStyleSheet()
m_parsedStyleSheetCache->removedFromMemoryCache();
}
-void CachedCSSStyleSheet::didAddClient(CachedResourceClient* c)
+void CachedCSSStyleSheet::didAddClient(CachedResourceClient& client)
{
- ASSERT(c->resourceClientType() == CachedStyleSheetClient::expectedType());
+ ASSERT(client.resourceClientType() == CachedStyleSheetClient::expectedType());
// CachedResource::didAddClient() must be before setCSSStyleSheet(),
// because setCSSStyleSheet() may cause scripts to be executed, which could destroy 'c' if it is an instance of HTMLLinkElement.
// see the comment of HTMLLinkElement::setCSSStyleSheet.
- CachedResource::didAddClient(c);
+ CachedResource::didAddClient(client);
if (!isLoading())
- static_cast<CachedStyleSheetClient*>(c)->setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this);
+ static_cast<CachedStyleSheetClient&>(client).setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this);
}
void CachedCSSStyleSheet::setEncoding(const String& chs)
@@ -76,32 +71,39 @@ 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))
+const String CachedCSSStyleSheet::sheetText(MIMETypeCheck mimeTypeCheck, bool* hasValidMIMEType) const
+{
+ if (!m_data || m_data->isEmpty() || !canUseSheet(mimeTypeCheck, 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.append(m_decoder->flush());
- return sheetText;
+ return m_decoder->decodeAndFlush(m_data->data(), m_data->size());
}
-void CachedCSSStyleSheet::finishLoading(ResourceBuffer* data)
+void CachedCSSStyleSheet::setBodyDataFrom(const CachedResource& resource)
+{
+ ASSERT(resource.type() == type());
+ const CachedCSSStyleSheet& sheet = static_cast<const CachedCSSStyleSheet&>(resource);
+
+ CachedResource::setBodyDataFrom(resource);
+
+ m_decoder = sheet.m_decoder;
+ m_decodedSheetText = sheet.m_decodedSheetText;
+ if (sheet.m_parsedStyleSheetCache)
+ saveParsedStyleSheet(*sheet.m_parsedStyleSheetCache);
+}
+
+void CachedCSSStyleSheet::finishLoading(SharedBuffer* data)
{
m_data = data;
- setEncodedSize(m_data.get() ? m_data->size() : 0);
+ setEncodedSize(data ? 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.append(m_decoder->flush());
- }
+ if (data)
+ m_decodedSheetText = m_decoder->decodeAndFlush(data->data(), data->size());
setLoading(false);
checkNotify();
// Clear the decoded text as it is unlikely to be needed immediately again and is cheap to regenerate.
@@ -118,12 +120,12 @@ void CachedCSSStyleSheet::checkNotify()
c->setCSSStyleSheet(m_resourceRequest.url(), m_response.url(), m_decoder->encoding().name(), this);
}
-bool CachedCSSStyleSheet::canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const
+bool CachedCSSStyleSheet::canUseSheet(MIMETypeCheck mimeTypeCheck, bool* hasValidMIMEType) const
{
if (errorOccurred())
return false;
-
- if (!enforceMIMEType && !hasValidMIMEType)
+
+ if (mimeTypeCheck == MIMETypeCheck::Lax)
return true;
// This check exactly matches Firefox. Note that we grab the Content-Type
@@ -133,12 +135,10 @@ bool CachedCSSStyleSheet::canUseSheet(bool enforceMIMEType, bool* hasValidMIMETy
//
// 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");
+ String mimeType = extractMIMETypeFromMediaType(response().httpHeaderField(HTTPHeaderName::ContentType));
+ bool typeOK = mimeType.isEmpty() || equalLettersIgnoringASCIICase(mimeType, "text/css") || equalLettersIgnoringASCIICase(mimeType, "application/x-unknown-content-type");
if (hasValidMIMEType)
*hasValidMIMEType = typeOK;
- if (!enforceMIMEType)
- return true;
return typeOK;
}
@@ -148,22 +148,19 @@ void CachedCSSStyleSheet::destroyDecodedData()
return;
m_parsedStyleSheetCache->removedFromMemoryCache();
- m_parsedStyleSheetCache.clear();
+ m_parsedStyleSheetCache = nullptr;
setDecodedSize(0);
-
- if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable())
- makePurgeable(true);
}
-PassRefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(const CSSParserContext& context)
+RefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(const CSSParserContext& context, CachePolicy cachePolicy)
{
if (!m_parsedStyleSheetCache)
- return 0;
- if (m_parsedStyleSheetCache->hasFailedOrCanceledSubresources()) {
+ return nullptr;
+ if (!m_parsedStyleSheetCache->subresourcesAllowReuse(cachePolicy)) {
m_parsedStyleSheetCache->removedFromMemoryCache();
- m_parsedStyleSheetCache.clear();
- return 0;
+ m_parsedStyleSheetCache = nullptr;
+ return nullptr;
}
ASSERT(m_parsedStyleSheetCache->isCacheable());
@@ -171,20 +168,20 @@ PassRefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(cons
// Contexts must be identical so we know we would get the same exact result if we parsed again.
if (m_parsedStyleSheetCache->parserContext() != context)
- return 0;
+ return nullptr;
didAccessDecodedData(monotonicallyIncreasingTime());
return m_parsedStyleSheetCache;
}
-void CachedCSSStyleSheet::saveParsedStyleSheet(PassRef<StyleSheetContents> sheet)
+void CachedCSSStyleSheet::saveParsedStyleSheet(Ref<StyleSheetContents>&& sheet)
{
- ASSERT(sheet.get().isCacheable());
+ ASSERT(sheet->isCacheable());
if (m_parsedStyleSheetCache)
m_parsedStyleSheetCache->removedFromMemoryCache();
- m_parsedStyleSheetCache = std::move(sheet);
+ m_parsedStyleSheetCache = WTFMove(sheet);
m_parsedStyleSheetCache->addedToMemoryCache();
setDecodedSize(m_parsedStyleSheetCache->estimatedSizeInBytes());
diff --git a/Source/WebCore/loader/cache/CachedCSSStyleSheet.h b/Source/WebCore/loader/cache/CachedCSSStyleSheet.h
index 19ad61939..c33aa2e40 100644
--- a/Source/WebCore/loader/cache/CachedCSSStyleSheet.h
+++ b/Source/WebCore/loader/cache/CachedCSSStyleSheet.h
@@ -2,7 +2,7 @@
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.
+ Copyright (C) 2004-2017 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
@@ -18,55 +18,53 @@
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
+#pragma once
#include "CachedResource.h"
-#include <wtf/Vector.h>
namespace WebCore {
- class CachedResourceClient;
- class StyleSheetContents;
- class TextResourceDecoder;
- struct CSSParserContext;
+class StyleSheetContents;
+class TextResourceDecoder;
+
+struct CSSParserContext;
+
+class CachedCSSStyleSheet final : public CachedResource {
+public:
+ CachedCSSStyleSheet(CachedResourceRequest&&, SessionID);
+ virtual ~CachedCSSStyleSheet();
- class CachedCSSStyleSheet final : public CachedResource {
- public:
- CachedCSSStyleSheet(const ResourceRequest&, const String& charset);
- virtual ~CachedCSSStyleSheet();
+ enum class MIMETypeCheck { Strict, Lax };
+ const String sheetText(MIMETypeCheck = MIMETypeCheck::Strict, bool* hasValidMIMEType = nullptr) const;
- const String sheetText(bool enforceMIMEType = true, bool* hasValidMIMEType = 0) const;
+ RefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&, CachePolicy);
+ void saveParsedStyleSheet(Ref<StyleSheetContents>&&);
- PassRefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&);
- void saveParsedStyleSheet(PassRef<StyleSheetContents>);
+private:
+ bool canUseSheet(MIMETypeCheck, bool* hasValidMIMEType) const;
+ bool mayTryReplaceEncodedData() const final { return true; }
- private:
- bool canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const;
- virtual PurgePriority purgePriority() const override { return PurgeLast; }
- virtual bool mayTryReplaceEncodedData() const override { return true; }
+ void didAddClient(CachedResourceClient&) final;
- virtual void didAddClient(CachedResourceClient*) override;
+ void setEncoding(const String&) final;
+ String encoding() const final;
+ const TextResourceDecoder* textResourceDecoder() const final { return m_decoder.get(); }
+ void finishLoading(SharedBuffer*) final;
+ void destroyDecodedData() final;
- virtual void setEncoding(const String&) override;
- virtual String encoding() const override;
- virtual void finishLoading(ResourceBuffer*) override;
- virtual void destroyDecodedData() override;
+ void setBodyDataFrom(const CachedResource&) final;
- protected:
- virtual void checkNotify() override;
+protected:
+ void checkNotify() final;
- RefPtr<TextResourceDecoder> m_decoder;
- String m_decodedSheetText;
+ RefPtr<TextResourceDecoder> m_decoder;
+ String m_decodedSheetText;
- RefPtr<StyleSheetContents> m_parsedStyleSheetCache;
- };
+ RefPtr<StyleSheetContents> m_parsedStyleSheetCache;
+};
-}
+} // namespace WebCore
-#endif
+SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedCSSStyleSheet, CachedResource::CSSStyleSheet)
diff --git a/Source/WebCore/loader/cache/CachedFont.cpp b/Source/WebCore/loader/cache/CachedFont.cpp
index fbf658cc0..91929778b 100644
--- a/Source/WebCore/loader/cache/CachedFont.cpp
+++ b/Source/WebCore/loader/cache/CachedFont.cpp
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -31,27 +31,22 @@
#include "CachedResourceClientWalker.h"
#include "CachedResourceLoader.h"
#include "FontCustomPlatformData.h"
+#include "FontDescription.h"
#include "FontPlatformData.h"
-#include "MemoryCache.h"
-#include "ResourceBuffer.h"
#include "SharedBuffer.h"
#include "TextResourceDecoder.h"
+#include "TypedElementDescendantIterator.h"
#include "WOFFFileFormat.h"
#include <wtf/Vector.h>
-#if ENABLE(SVG_FONTS)
-#include "NodeList.h"
-#include "SVGDocument.h"
-#include "SVGElement.h"
-#include "SVGFontElement.h"
-#include "SVGGElement.h"
-#include "SVGNames.h"
+#if USE(DIRECT2D)
+#include <dwrite.h>
#endif
namespace WebCore {
-CachedFont::CachedFont(const ResourceRequest& resourceRequest)
- : CachedResource(resourceRequest, FontResource)
+CachedFont::CachedFont(CachedResourceRequest&& request, SessionID sessionID, Type type)
+ : CachedResource(WTFMove(request), type, sessionID)
, m_loadInitiated(false)
, m_hasCreatedFontDataWrappingResource(false)
{
@@ -61,21 +56,20 @@ CachedFont::~CachedFont()
{
}
-void CachedFont::load(CachedResourceLoader*, const ResourceLoaderOptions& options)
+void CachedFont::load(CachedResourceLoader&)
{
// Don't load the file yet. Wait for an access before triggering the load.
setLoading(true);
- m_options = options;
}
-void CachedFont::didAddClient(CachedResourceClient* c)
+void CachedFont::didAddClient(CachedResourceClient& client)
{
- ASSERT(c->resourceClientType() == CachedFontClient::expectedType());
+ ASSERT(client.resourceClientType() == CachedFontClient::expectedType());
if (!isLoading())
- static_cast<CachedFontClient*>(c)->fontLoaded(this);
+ static_cast<CachedFontClient&>(client).fontLoaded(*this);
}
-void CachedFont::finishLoading(ResourceBuffer* data)
+void CachedFont::finishLoading(SharedBuffer* data)
{
m_data = data;
setEncodedSize(m_data.get() ? m_data->size() : 0);
@@ -83,103 +77,76 @@ void CachedFont::finishLoading(ResourceBuffer* data)
checkNotify();
}
-void CachedFont::beginLoadIfNeeded(CachedResourceLoader* dl)
+void CachedFont::beginLoadIfNeeded(CachedResourceLoader& loader)
{
if (!m_loadInitiated) {
m_loadInitiated = true;
- CachedResource::load(dl, m_options);
+ CachedResource::load(loader);
}
}
-bool CachedFont::ensureCustomFontData()
+bool CachedFont::ensureCustomFontData(const AtomicString&)
{
- if (!m_fontData && !errorOccurred() && !isLoading() && m_data) {
- SharedBuffer* buffer = m_data.get()->sharedBuffer();
- ASSERT(buffer);
-
- RefPtr<SharedBuffer> sfntBuffer;
-
- bool fontIsWOFF = isWOFF(buffer);
- if (fontIsWOFF) {
- Vector<char> sfnt;
- if (convertWOFFToSfnt(buffer, sfnt)) {
- sfntBuffer = SharedBuffer::adoptVector(sfnt);
- buffer = sfntBuffer.get();
- } else
- buffer = nullptr;
- }
-
- m_fontData = buffer ? createFontCustomPlatformData(*buffer) : nullptr;
- if (m_fontData)
- m_hasCreatedFontDataWrappingResource = !fontIsWOFF;
- else
- setStatus(DecodeError);
- }
- return m_fontData.get();
+ return ensureCustomFontData(m_data.get());
}
-FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation orientation, FontWidthVariant widthVariant, FontRenderingMode renderingMode)
+bool CachedFont::ensureCustomFontData(SharedBuffer* data)
{
-#if ENABLE(SVG_FONTS)
- if (m_externalSVGDocument)
- return FontPlatformData(size, bold, italic);
-#endif
- ASSERT(m_fontData);
- return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic, orientation, widthVariant, renderingMode);
-}
-
-#if ENABLE(SVG_FONTS)
-bool CachedFont::ensureSVGFontData()
-{
- if (!m_externalSVGDocument && !errorOccurred() && !isLoading() && m_data) {
- m_externalSVGDocument = SVGDocument::create(0, URL());
-
- RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml");
- String svgSource = decoder->decode(m_data->data(), m_data->size());
- svgSource.append(decoder->flush());
-
- m_externalSVGDocument->setContent(svgSource);
-
- if (decoder->sawError())
- m_externalSVGDocument = 0;
+ if (!m_fontCustomPlatformData && !errorOccurred() && !isLoading() && data) {
+ bool wrapping;
+ m_fontCustomPlatformData = createCustomFontData(*data, wrapping);
+ m_hasCreatedFontDataWrappingResource = m_fontCustomPlatformData && wrapping;
+ if (!m_fontCustomPlatformData)
+ setStatus(DecodeError);
}
- return m_externalSVGDocument;
+ return m_fontCustomPlatformData.get();
}
-SVGFontElement* CachedFont::getSVGFontById(const String& fontName) const
+std::unique_ptr<FontCustomPlatformData> CachedFont::createCustomFontData(SharedBuffer& bytes, bool& wrapping)
{
- 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(isSVGFontElement(list->item(i)));
+ wrapping = true;
+
+#if !PLATFORM(COCOA)
+ if (isWOFF(bytes)) {
+ wrapping = false;
+ Vector<char> convertedFont;
+ if (!convertWOFFToSfnt(bytes, convertedFont))
+ return nullptr;
+
+ auto buffer = SharedBuffer::adoptVector(convertedFont);
+ return createFontCustomPlatformData(buffer);
}
#endif
- if (fontName.isEmpty())
- return toSVGFontElement(list->item(0));
+ return createFontCustomPlatformData(bytes);
+}
- for (unsigned i = 0; i < listLength; ++i) {
- SVGFontElement* element = toSVGFontElement(list->item(i));
- if (element->getIdAttribute() == fontName)
- return element;
- }
+RefPtr<Font> CachedFont::createFont(const FontDescription& fontDescription, const AtomicString&, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings)
+{
+ return Font::create(platformDataFromCustomData(fontDescription, syntheticBold, syntheticItalic, fontFaceFeatures, fontFaceVariantSettings), true);
+}
- return 0;
+FontPlatformData CachedFont::platformDataFromCustomData(const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings)
+{
+ ASSERT(m_fontCustomPlatformData);
+ return platformDataFromCustomData(*m_fontCustomPlatformData, fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings);
}
+
+FontPlatformData CachedFont::platformDataFromCustomData(FontCustomPlatformData& fontCustomPlatformData, const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings)
+{
+#if PLATFORM(COCOA)
+ return fontCustomPlatformData.fontPlatformData(fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings);
+#else
+ UNUSED_PARAM(fontFaceFeatures);
+ UNUSED_PARAM(fontFaceVariantSettings);
+ return fontCustomPlatformData.fontPlatformData(fontDescription, bold, italic);
#endif
+}
void CachedFont::allClientsRemoved()
{
- m_fontData = nullptr;
+ m_fontCustomPlatformData = nullptr;
}
void CachedFont::checkNotify()
@@ -187,9 +154,9 @@ void CachedFont::checkNotify()
if (isLoading())
return;
- CachedResourceClientWalker<CachedFontClient> w(m_clients);
- while (CachedFontClient* c = w.next())
- c->fontLoaded(this);
+ CachedResourceClientWalker<CachedFontClient> walker(m_clients);
+ while (CachedFontClient* client = walker.next())
+ client->fontLoaded(*this);
}
bool CachedFont::mayTryReplaceEncodedData() const
diff --git a/Source/WebCore/loader/cache/CachedFont.h b/Source/WebCore/loader/cache/CachedFont.h
index f19582b96..25d292f0f 100644
--- a/Source/WebCore/loader/cache/CachedFont.h
+++ b/Source/WebCore/loader/cache/CachedFont.h
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,61 +23,63 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CachedFont_h
-#define CachedFont_h
+#pragma once
#include "CachedResource.h"
#include "CachedResourceClient.h"
-#include "FontOrientation.h"
-#include "FontRenderingMode.h"
-#include "FontWidthVariant.h"
+#include "Font.h"
+#include "TextFlags.h"
namespace WebCore {
class CachedResourceLoader;
+class FontDescription;
class FontPlatformData;
class SVGDocument;
class SVGFontElement;
struct FontCustomPlatformData;
-class CachedFont final : public CachedResource {
+template <typename T> class FontTaggedSettings;
+typedef FontTaggedSettings<int> FontFeatureSettings;
+
+class CachedFont : public CachedResource {
public:
- CachedFont(const ResourceRequest&);
+ CachedFont(CachedResourceRequest&&, SessionID, Type = FontResource);
virtual ~CachedFont();
- void beginLoadIfNeeded(CachedResourceLoader* dl);
- virtual bool stillNeedsLoad() const override { return !m_loadInitiated; }
+ void beginLoadIfNeeded(CachedResourceLoader&);
+ bool stillNeedsLoad() const override { return !m_loadInitiated; }
+
+ virtual bool ensureCustomFontData(const AtomicString& remoteURI);
+ static std::unique_ptr<FontCustomPlatformData> createCustomFontData(SharedBuffer&, bool& wrapping);
+ static FontPlatformData platformDataFromCustomData(FontCustomPlatformData&, const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&);
- bool ensureCustomFontData();
- FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode);
+ virtual RefPtr<Font> createFont(const FontDescription&, const AtomicString& remoteURI, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings&, const FontVariantSettings&);
-#if ENABLE(SVG_FONTS)
- bool ensureSVGFontData();
- SVGFontElement* getSVGFontById(const String&) const;
-#endif
+protected:
+ FontPlatformData platformDataFromCustomData(const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&);
+
+ bool ensureCustomFontData(SharedBuffer* data);
private:
- virtual void checkNotify() override;
- virtual bool mayTryReplaceEncodedData() const override;
+ void checkNotify() override;
+ bool mayTryReplaceEncodedData() const override;
- virtual void load(CachedResourceLoader*, const ResourceLoaderOptions&) override;
+ void load(CachedResourceLoader&) override;
+ NO_RETURN_DUE_TO_ASSERT void setBodyDataFrom(const CachedResource&) final { ASSERT_NOT_REACHED(); }
- virtual void didAddClient(CachedResourceClient*) override;
- virtual void finishLoading(ResourceBuffer*) override;
+ void didAddClient(CachedResourceClient&) override;
+ void finishLoading(SharedBuffer*) override;
- virtual void allClientsRemoved() override;
+ void allClientsRemoved() override;
- std::unique_ptr<FontCustomPlatformData> m_fontData;
+ std::unique_ptr<FontCustomPlatformData> m_fontCustomPlatformData;
bool m_loadInitiated;
bool m_hasCreatedFontDataWrappingResource;
-#if ENABLE(SVG_FONTS)
- RefPtr<SVGDocument> m_externalSVGDocument;
-#endif
-
friend class MemoryCache;
};
} // namespace WebCore
-#endif // CachedFont_h
+SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedFont, CachedResource::FontResource)
diff --git a/Source/WebCore/loader/cache/CachedFontClient.h b/Source/WebCore/loader/cache/CachedFontClient.h
index e87040ea2..a1388fb88 100644
--- a/Source/WebCore/loader/cache/CachedFontClient.h
+++ b/Source/WebCore/loader/cache/CachedFontClient.h
@@ -23,8 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CachedFontClient_h
-#define CachedFontClient_h
+#pragma once
#include "CachedResourceClient.h"
@@ -36,10 +35,8 @@ class CachedFontClient : public CachedResourceClient {
public:
virtual ~CachedFontClient() { }
static CachedResourceClientType expectedType() { return FontType; }
- virtual CachedResourceClientType resourceClientType() const override { return expectedType(); }
- virtual void fontLoaded(CachedFont*) { }
+ CachedResourceClientType resourceClientType() const override { return expectedType(); }
+ virtual void fontLoaded(CachedFont&) { }
};
} // namespace WebCore
-
-#endif // CachedFontClient_h
diff --git a/Source/WebCore/loader/cache/CachedImage.cpp b/Source/WebCore/loader/cache/CachedImage.cpp
index 5525fb1f3..65f0bf79c 100644
--- a/Source/WebCore/loader/cache/CachedImage.cpp
+++ b/Source/WebCore/loader/cache/CachedImage.cpp
@@ -35,15 +35,15 @@
#include "FrameLoaderTypes.h"
#include "FrameView.h"
#include "MemoryCache.h"
-#include "Page.h"
#include "RenderElement.h"
-#include "ResourceBuffer.h"
+#include "SVGImage.h"
#include "SecurityOrigin.h"
#include "Settings.h"
+#include "SharedBuffer.h"
#include "SubresourceLoader.h"
#include <wtf/CurrentTime.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
-#include <wtf/Vector.h>
#if PLATFORM(IOS)
#include "SystemMemory.h"
@@ -53,40 +53,28 @@
#include "PDFDocumentImage.h"
#endif
-#if ENABLE(SVG)
-#include "SVGImage.h"
-#endif
-
-#if ENABLE(DISK_IMAGE_CACHE)
-#include "DiskImageCacheIOS.h"
-#endif
-
namespace WebCore {
-CachedImage::CachedImage(const ResourceRequest& resourceRequest)
- : CachedResource(resourceRequest, ImageResource)
- , m_image(0)
- , m_shouldPaintBrokenImage(true)
+CachedImage::CachedImage(CachedResourceRequest&& request, SessionID sessionID)
+ : CachedResource(WTFMove(request), ImageResource, sessionID)
{
setStatus(Unknown);
}
-CachedImage::CachedImage(Image* image)
- : CachedResource(ResourceRequest(), ImageResource)
+CachedImage::CachedImage(Image* image, SessionID sessionID)
+ : CachedResource(URL(), ImageResource, sessionID)
, m_image(image)
- , m_shouldPaintBrokenImage(true)
{
- setStatus(Cached);
- setLoading(false);
}
-CachedImage::CachedImage(const URL& url, Image* image)
- : CachedResource(ResourceRequest(url), ImageResource)
+CachedImage::CachedImage(const URL& url, Image* image, SessionID sessionID)
+ : CachedResource(url, ImageResource, sessionID)
, m_image(image)
- , m_shouldPaintBrokenImage(true)
+ , m_isManuallyCached(true)
{
- setStatus(Cached);
- setLoading(false);
+ // Use the incoming URL in the response field. This ensures that code using the response directly,
+ // such as origin checks for security, actually see something.
+ m_response.setURL(url);
}
CachedImage::~CachedImage()
@@ -94,56 +82,69 @@ CachedImage::~CachedImage()
clearImage();
}
-void CachedImage::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
+void CachedImage::load(CachedResourceLoader& loader)
{
- if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages())
- CachedResource::load(cachedResourceLoader, options);
+ if (loader.shouldPerformImageLoad(url()))
+ CachedResource::load(loader);
else
setLoading(false);
}
-void CachedImage::didAddClient(CachedResourceClient* c)
+void CachedImage::setBodyDataFrom(const CachedResource& resource)
+{
+ ASSERT(resource.type() == type());
+ const CachedImage& image = static_cast<const CachedImage&>(resource);
+
+ CachedResource::setBodyDataFrom(resource);
+
+ m_image = image.m_image;
+ m_imageObserver = image.m_imageObserver;
+ if (m_imageObserver)
+ m_imageObserver->add(*this);
+
+ if (m_image && is<SVGImage>(*m_image))
+ m_svgImageCache = std::make_unique<SVGImageCache>(&downcast<SVGImage>(*m_image));
+}
+
+void CachedImage::didAddClient(CachedResourceClient& client)
{
if (m_data && !m_image && !errorOccurred()) {
createImage();
- m_image->setData(m_data->sharedBuffer(), true);
+ m_image->setData(m_data.copyRef(), true);
}
-
- ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
+
+ ASSERT(client.resourceClientType() == CachedImageClient::expectedType());
if (m_image && !m_image->isNull())
- static_cast<CachedImageClient*>(c)->imageChanged(this);
+ static_cast<CachedImageClient&>(client).imageChanged(this);
- CachedResource::didAddClient(c);
+ CachedResource::didAddClient(client);
}
-void CachedImage::didRemoveClient(CachedResourceClient* c)
+void CachedImage::didRemoveClient(CachedResourceClient& client)
{
- ASSERT(c);
- ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
+ ASSERT(client.resourceClientType() == CachedImageClient::expectedType());
+
+ m_pendingContainerSizeRequests.remove(&static_cast<CachedImageClient&>(client));
- m_pendingContainerSizeRequests.remove(static_cast<CachedImageClient*>(c));
-#if ENABLE(SVG)
if (m_svgImageCache)
- m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(c));
-#endif
+ m_svgImageCache->removeClientFromCache(&static_cast<CachedImageClient&>(client));
- CachedResource::didRemoveClient(c);
+ CachedResource::didRemoveClient(client);
}
void CachedImage::switchClientsToRevalidatedResource()
{
- ASSERT(resourceToRevalidate());
- ASSERT(resourceToRevalidate()->isImage());
+ ASSERT(is<CachedImage>(resourceToRevalidate()));
// Pending container size requests need to be transferred to the revalidated resource.
if (!m_pendingContainerSizeRequests.isEmpty()) {
// A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce().
ContainerSizeRequests switchContainerSizeRequests;
- for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
- switchContainerSizeRequests.set(it->key, it->value);
+ for (auto& request : m_pendingContainerSizeRequests)
+ switchContainerSizeRequests.set(request.key, request.value);
CachedResource::switchClientsToRevalidatedResource();
- CachedImage* revalidatedCachedImage = static_cast<CachedImage*>(resourceToRevalidate());
- for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it)
- revalidatedCachedImage->setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
+ CachedImage& revalidatedCachedImage = downcast<CachedImage>(*resourceToRevalidate());
+ for (auto& request : switchContainerSizeRequests)
+ revalidatedCachedImage.setContainerSizeForRenderer(request.key, request.value.first, request.value.second);
return;
}
@@ -159,12 +160,17 @@ void CachedImage::allClientsRemoved()
std::pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const
{
+ if (deviceScaleFactor >= 3) {
+ static NeverDestroyed<Image*> brokenImageVeryHiRes(Image::loadPlatformResource("missingImage@3x").leakRef());
+ return std::make_pair(brokenImageVeryHiRes, 3);
+ }
+
if (deviceScaleFactor >= 2) {
- DEFINE_STATIC_LOCAL(Image*, brokenImageHiRes, (Image::loadPlatformResource("missingImage@2x").leakRef()));
+ static NeverDestroyed<Image*> brokenImageHiRes(Image::loadPlatformResource("missingImage@2x").leakRef());
return std::make_pair(brokenImageHiRes, 2);
}
- DEFINE_STATIC_LOCAL(Image*, brokenImageLoRes, (Image::loadPlatformResource("missingImage").leakRef()));
+ static NeverDestroyed<Image*> brokenImageLoRes(Image::loadPlatformResource("missingImage").leakRef());
return std::make_pair(brokenImageLoRes, 1);
}
@@ -175,8 +181,6 @@ bool CachedImage::willPaintBrokenImage() const
Image* CachedImage::image()
{
- ASSERT(!isPurgeable());
-
if (errorOccurred() && m_shouldPaintBrokenImage) {
// Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
// deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage()
@@ -192,8 +196,6 @@ Image* CachedImage::image()
Image* CachedImage::imageForRenderer(const RenderObject* renderer)
{
- ASSERT(!isPurgeable());
-
if (errorOccurred() && m_shouldPaintBrokenImage) {
// Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
// deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage()
@@ -204,19 +206,15 @@ Image* CachedImage::imageForRenderer(const RenderObject* renderer)
if (!m_image)
return Image::nullImage();
-#if ENABLE(SVG)
if (m_image->isSVGImage()) {
Image* image = m_svgImageCache->imageForRenderer(renderer);
if (image != Image::nullImage())
return image;
}
-#else
- UNUSED_PARAM(renderer);
-#endif
return m_image.get();
}
-void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const IntSize& containerSize, float containerZoom)
+void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const LayoutSize& containerSize, float containerZoom)
{
if (containerSize.isEmpty())
return;
@@ -226,17 +224,13 @@ void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer,
m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom));
return;
}
-#if ENABLE(SVG)
+
if (!m_image->isSVGImage()) {
m_image->setContainerSize(containerSize);
return;
}
m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom);
-#else
- UNUSED_PARAM(containerZoom);
- m_image->setContainerSize(containerSize);
-#endif
}
bool CachedImage::usesImageContainerSize() const
@@ -263,42 +257,19 @@ bool CachedImage::imageHasRelativeHeight() const
return false;
}
-LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType)
+LayoutSize CachedImage::imageSizeForRenderer(const RenderElement* renderer, float multiplier, SizeType sizeType)
{
- ASSERT(!isPurgeable());
-
if (!m_image)
- return IntSize();
+ return LayoutSize();
- LayoutSize imageSize(m_image->size());
+ LayoutSize imageSize;
-#if ENABLE(CSS_IMAGE_ORIENTATION)
- if (renderer && m_image->isBitmapImage()) {
- ImageOrientationDescription orientationDescription(renderer->shouldRespectImageOrientation(), renderer->style().imageOrientation());
- if (orientationDescription.respectImageOrientation() == RespectImageOrientation)
- imageSize = toBitmapImage(m_image.get())->sizeRespectingOrientation(orientationDescription);
- }
-#else
- if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
-#if !PLATFORM(IOS)
- imageSize = toBitmapImage(m_image.get())->sizeRespectingOrientation();
-#else
- {
- // On iOS, the image may have been subsampled to accommodate our size restrictions. However
- // we should tell the renderer what the original size was.
- imageSize = toBitmapImage(m_image.get())->originalSizeRespectingOrientation();
- } else if (m_image->isBitmapImage())
- imageSize = toBitmapImage(m_image.get())->originalSize();
-#endif // !PLATFORM(IOS)
-#endif // ENABLE(CSS_IMAGE_ORIENTATION)
-
-#if ENABLE(SVG)
- else if (m_image->isSVGImage() && sizeType == UsedSize) {
- imageSize = m_svgImageCache->imageSizeForRenderer(renderer);
- }
-#else
- UNUSED_PARAM(sizeType);
-#endif
+ if (is<BitmapImage>(*m_image) && renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)
+ imageSize = LayoutSize(downcast<BitmapImage>(*m_image).sizeRespectingOrientation());
+ else if (is<SVGImage>(*m_image) && sizeType == UsedSize)
+ imageSize = LayoutSize(m_svgImageCache->imageSizeForRenderer(renderer));
+ else
+ imageSize = LayoutSize(m_image->size());
if (multiplier == 1.0f)
return imageSize;
@@ -331,7 +302,7 @@ void CachedImage::checkShouldPaintBrokenImage()
if (!m_loader || m_loader->reachedTerminalState())
return;
- m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(m_resourceRequest.url());
+ m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(url());
}
void CachedImage::clear()
@@ -347,77 +318,98 @@ inline void CachedImage::createImage()
// Create the image if it doesn't yet exist.
if (m_image)
return;
+
+ m_imageObserver = CachedImageObserver::create(*this);
+
+ if (m_response.mimeType() == "image/svg+xml") {
+ auto svgImage = SVGImage::create(*m_imageObserver, url());
+ m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.ptr());
+ m_image = WTFMove(svgImage);
#if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
- else if (m_response.mimeType() == "application/pdf")
- m_image = PDFDocumentImage::create(this);
+ } else if (m_response.mimeType() == "application/pdf") {
+ m_image = PDFDocumentImage::create(m_imageObserver.get());
#endif
-#if ENABLE(SVG)
- else if (m_response.mimeType() == "image/svg+xml") {
- RefPtr<SVGImage> svgImage = SVGImage::create(this);
- m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.get());
- m_image = svgImage.release();
- }
-#endif
- else
- m_image = BitmapImage::create(this);
+ } else
+ m_image = BitmapImage::create(m_imageObserver.get());
if (m_image) {
// Send queued container size requests.
if (m_image->usesContainerSize()) {
- for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
- setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
+ for (auto& request : m_pendingContainerSizeRequests)
+ setContainerSizeForRenderer(request.key, request.value.first, request.value.second);
}
m_pendingContainerSizeRequests.clear();
}
}
-inline void CachedImage::clearImage()
+CachedImage::CachedImageObserver::CachedImageObserver(CachedImage& image)
{
- // If our Image has an observer, it's always us so we need to clear the back pointer
- // before dropping our reference.
- if (m_image)
- m_image->setImageObserver(0);
- m_image.clear();
+ m_cachedImages.reserveInitialCapacity(1);
+ m_cachedImages.append(&image);
+ if (auto* loader = image.loader()) {
+ m_allowSubsampling = loader->frameLoader()->frame().settings().imageSubsamplingEnabled();
+ m_allowLargeImageAsyncDecoding = loader->frameLoader()->frame().settings().largeImageAsyncDecodingEnabled();
+ m_allowAnimatedImageAsyncDecoding = loader->frameLoader()->frame().settings().animatedImageAsyncDecodingEnabled();
+ m_showDebugBackground = loader->frameLoader()->frame().settings().showDebugBorders();
+ }
}
-bool CachedImage::canBeDrawn() const
+void CachedImage::CachedImageObserver::decodedSizeChanged(const Image* image, long long delta)
{
- if (!m_image || m_image->isNull())
- return false;
+ for (auto cachedImage : m_cachedImages)
+ cachedImage->decodedSizeChanged(image, delta);
+}
- if (!m_loader || m_loader->reachedTerminalState())
- return true;
+void CachedImage::CachedImageObserver::didDraw(const Image* image)
+{
+ for (auto cachedImage : m_cachedImages)
+ cachedImage->didDraw(image);
+}
- size_t estimatedDecodedImageSize = m_image->width() * m_image->height() * 4; // no overflow check
- return estimatedDecodedImageSize <= m_loader->frameLoader()->frame().settings().maximumDecodedImageSize();
+void CachedImage::CachedImageObserver::animationAdvanced(const Image* image)
+{
+ for (auto cachedImage : m_cachedImages)
+ cachedImage->animationAdvanced(image);
}
-void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data)
+void CachedImage::CachedImageObserver::changedInRect(const Image* image, const IntRect* rect)
{
- m_data = data;
- if (!data)
- return;
+ for (auto cachedImage : m_cachedImages)
+ cachedImage->changedInRect(image, rect);
+}
+
+inline void CachedImage::clearImage()
+{
+ if (m_imageObserver) {
+ m_imageObserver->remove(*this);
+ m_imageObserver = nullptr;
+ }
+ m_image = nullptr;
+}
+
+void CachedImage::addIncrementalDataBuffer(SharedBuffer& data)
+{
+ m_data = &data;
createImage();
// 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).
- bool sizeAvailable = m_image->setData(m_data->sharedBuffer(), false);
+ bool sizeAvailable = m_image->setData(&data, false);
if (!sizeAvailable)
return;
- if (!canBeDrawn()) {
- // There's no image to draw or its decoded size is bigger than the maximum allowed.
+ if (m_image->isNull()) {
+ // Image decoding failed. Either we need more image data or the image data is malformed.
error(errorOccurred() ? status() : DecodeError);
if (inCache())
- memoryCache()->remove(this);
+ MemoryCache::singleton().remove(*this);
return;
}
- // Go ahead and tell our observers to try to draw.
- // Each chunk from the network causes observers to repaint, which will
- // force that chunk to decode.
+ // Tell our observers to try to draw.
+ // Each chunk from the network causes observers to repaint, which will force that chunk to decode.
// 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();
@@ -425,32 +417,34 @@ void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data)
setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
}
-void CachedImage::addDataBuffer(ResourceBuffer* data)
+void CachedImage::addDataBuffer(SharedBuffer& data)
{
- ASSERT(m_options.dataBufferingPolicy == BufferData);
+ ASSERT(dataBufferingPolicy() == BufferData);
addIncrementalDataBuffer(data);
+ CachedResource::addDataBuffer(data);
}
void CachedImage::addData(const char* data, unsigned length)
{
- ASSERT(m_options.dataBufferingPolicy == DoNotBufferData);
- addIncrementalDataBuffer(ResourceBuffer::create(data, length).get());
+ ASSERT(dataBufferingPolicy() == DoNotBufferData);
+ addIncrementalDataBuffer(SharedBuffer::create(data, length));
+ CachedResource::addData(data, length);
}
-void CachedImage::finishLoading(ResourceBuffer* data)
+void CachedImage::finishLoading(SharedBuffer* data)
{
m_data = data;
if (!m_image && data)
createImage();
if (m_image)
- m_image->setData(m_data->sharedBuffer(), true);
+ m_image->setData(data, true);
- if (!canBeDrawn()) {
- // There's no image to draw or its decoded size is bigger than the maximum allowed.
+ if (!m_image || m_image->isNull()) {
+ // Image decoding failed; the image data is malformed.
error(errorOccurred() ? status() : DecodeError);
if (inCache())
- memoryCache()->remove(this);
+ MemoryCache::singleton().remove(*this);
return;
}
@@ -460,6 +454,16 @@ void CachedImage::finishLoading(ResourceBuffer* data)
CachedResource::finishLoading(data);
}
+void CachedImage::didReplaceSharedBufferContents()
+{
+ if (m_image) {
+ // Let the Image know that the SharedBuffer has been rejigged, so it can let go of any references to the heap-allocated resource buffer.
+ // FIXME(rdar://problem/24275617): It would be better if we could somehow tell the Image's decoder to swap in the new contents without destroying anything.
+ m_image->destroyDecodedData(true);
+ }
+ CachedResource::didReplaceSharedBufferContents();
+}
+
void CachedImage::error(CachedResource::Status status)
{
checkShouldPaintBrokenImage();
@@ -478,23 +482,20 @@ void CachedImage::responseReceived(const ResourceResponse& response)
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;
+ if (canDeleteImage && !isLoading() && !hasClients()) {
+ m_image = nullptr;
setDecodedSize(0);
- if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
- makePurgeable(true);
} else if (m_image && !errorOccurred())
m_image->destroyDecodedData();
}
-void CachedImage::decodedSizeChanged(const Image* image, int delta)
+void CachedImage::decodedSizeChanged(const Image* image, long long delta)
{
if (!image || image != m_image)
return;
-
- setDecodedSize(decodedSize() + delta);
+
+ ASSERT(delta >= 0 || decodedSize() + delta >= 0);
+ setDecodedSize(static_cast<unsigned>(decodedSize() + delta));
}
void CachedImage::didDraw(const Image* image)
@@ -509,132 +510,47 @@ void CachedImage::didDraw(const Image* image)
CachedResource::didAccessDecodedData(timeStamp);
}
-bool CachedImage::shouldPauseAnimation(const Image* image)
-{
- if (!image || image != m_image)
- return false;
-
- CachedResourceClientWalker<CachedImageClient> w(m_clients);
- while (CachedImageClient* c = w.next()) {
- if (c->willRenderImage(this))
- return false;
- }
-
- return true;
-}
-
void CachedImage::animationAdvanced(const Image* image)
{
if (!image || image != m_image)
return;
- notifyObservers();
+ CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients);
+ while (CachedImageClient* client = clientWalker.next())
+ client->newImageAnimationFrameAvailable(*this);
}
-void CachedImage::changedInRect(const Image* image, const IntRect& rect)
+void CachedImage::changedInRect(const Image* image, const IntRect* rect)
{
if (!image || image != m_image)
return;
- notifyObservers(&rect);
-}
-
-void CachedImage::resumeAnimatingImagesForLoader(CachedResourceLoader* loader)
-{
- const CachedResourceLoader::DocumentResourceMap& resources = loader->allCachedResources();
-
- for (CachedResourceLoader::DocumentResourceMap::const_iterator it = resources.begin(), end = resources.end(); it != end; ++it) {
- const CachedResourceHandle<CachedResource>& resource = it->value;
- if (!resource || !resource->isImage())
- continue;
- CachedImage* cachedImage = static_cast<CachedImage*>(resource.get());
- if (!cachedImage->hasImage())
- continue;
- Image* image = cachedImage->image();
- if (!image->isBitmapImage())
- continue;
- BitmapImage* bitmapImage = toBitmapImage(image);
- if (!bitmapImage->canAnimate())
- continue;
- cachedImage->animationAdvanced(bitmapImage);
- }
+ notifyObservers(rect);
}
bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
{
Image* image = imageForRenderer(renderer);
- if (image->isBitmapImage())
- image->nativeImageForCurrentFrame(); // force decode
return image->currentFrameKnownToBeOpaque();
}
-#if ENABLE(DISK_IMAGE_CACHE)
-bool CachedImage::canUseDiskImageCache() const
-{
- if (isLoading() || errorOccurred())
- return false;
-
- if (!m_data)
- return false;
-
- if (isPurgeable())
- return false;
-
- if (m_data->size() < diskImageCache().minimumImageSize())
- return false;
-
- // "Cache-Control: no-store" resources may be marked as such because they may
- // contain sensitive information. We should not write these resources to disk.
- if (m_response.cacheControlContainsNoStore())
- return false;
-
- // Testing shows that PDF images did not work when memory mapped.
- // However, SVG images and Bitmap images were fine. See:
- // <rdar://problem/8591834> Disk Image Cache should support PDF Images
- if (m_response.mimeType() == "application/pdf")
- return false;
-
- return true;
-}
-
-void CachedImage::useDiskImageCache()
+bool CachedImage::isOriginClean(SecurityOrigin* origin)
{
- ASSERT(canUseDiskImageCache());
- ASSERT(!isUsingDiskImageCache());
- m_data->sharedBuffer()->allowToBeMemoryMapped();
-}
-#endif
-
-bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin)
-{
- if (!image()->hasSingleSecurityOrigin())
- return false;
- if (passesAccessControlCheck(securityOrigin))
- return true;
- return !securityOrigin->taintsCanvas(response().url());
+ ASSERT_UNUSED(origin, origin);
+ ASSERT(this->origin());
+ ASSERT(origin->toString() == this->origin()->toString());
+ return !loadFailedOrCanceled() && isCORSSameOrigin();
}
-#if USE(CF)
-// FIXME: We should look to incorporate the functionality of CachedImageManual
-// into CachedImage or find a better place for this class.
-// FIXME: Remove the USE(CF) once we make MemoryCache::addImageToCache() platform-independent.
-CachedImageManual::CachedImageManual(const URL& url, Image* image)
- : CachedImage(url, image)
- , m_fakeClient(std::make_unique<CachedImageClient>())
+CachedResource::RevalidationDecision CachedImage::makeRevalidationDecision(CachePolicy cachePolicy) const
{
- // Use the incoming URL in the response field. This ensures that code
- // using the response directly, such as origin checks for security,
- // actually see something.
- m_response.setURL(url);
-}
-
-bool CachedImageManual::mustRevalidateDueToCacheHeaders(CachePolicy) const
-{
- // Do not revalidate manually cached images. This mechanism is used as a
- // way to efficiently share an image from the client to content and
- // the URL for that image may not represent a resource that can be
- // retrieved by standard means. If the manual caching SPI is used, it is
- // incumbent on the client to only use valid resources.
- return false;
+ if (UNLIKELY(isManuallyCached())) {
+ // Do not revalidate manually cached images. This mechanism is used as a
+ // way to efficiently share an image from the client to content and
+ // the URL for that image may not represent a resource that can be
+ // retrieved by standard means. If the manual caching SPI is used, it is
+ // incumbent on the client to only use valid resources.
+ return RevalidationDecision::No;
+ }
+ return CachedResource::makeRevalidationDecision(cachePolicy);
}
-#endif
} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedImage.h b/Source/WebCore/loader/cache/CachedImage.h
index 12b77cc46..985aa9749 100644
--- a/Source/WebCore/loader/cache/CachedImage.h
+++ b/Source/WebCore/loader/cache/CachedImage.h
@@ -20,17 +20,16 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef CachedImage_h
-#define CachedImage_h
+#pragma once
#include "CachedResource.h"
+#include "Image.h"
#include "ImageObserver.h"
#include "IntRect.h"
#include "IntSizeHash.h"
#include "LayoutSize.h"
#include "SVGImageCache.h"
#include <wtf/HashMap.h>
-#include <wtf/Vector.h>
namespace WebCore {
@@ -44,124 +43,132 @@ class SecurityOrigin;
struct Length;
-class CachedImage : public CachedResource, public ImageObserver {
+class CachedImage final : public CachedResource {
friend class MemoryCache;
public:
- CachedImage(const ResourceRequest&);
- CachedImage(Image*);
- CachedImage(const URL&, Image*);
+ CachedImage(CachedResourceRequest&&, SessionID);
+ CachedImage(Image*, SessionID);
+ // Constructor to use for manually cached images.
+ CachedImage(const URL&, Image*, SessionID);
virtual ~CachedImage();
- Image* image(); // Returns the nullImage() if the image is not available yet.
- Image* imageForRenderer(const RenderObject*); // Returns the nullImage() if the image is not available yet.
+ WEBCORE_EXPORT Image* image(); // Returns the nullImage() if the image is not available yet.
+ WEBCORE_EXPORT Image* imageForRenderer(const RenderObject*); // Returns the nullImage() if the image is not available yet.
bool hasImage() const { return m_image.get(); }
- bool currentFrameKnownToBeOpaque(const RenderElement*); // Side effect: ensures decoded image is in cache, therefore should only be called when about to draw the image.
+ bool currentFrameKnownToBeOpaque(const RenderElement*);
std::pair<Image*, float> brokenImage(float deviceScaleFactor) const; // Returns an image and the image's resolution scale factor.
- bool willPaintBrokenImage() const;
+ bool willPaintBrokenImage() const;
- bool canRender(const RenderObject* renderer, float multiplier) { return !errorOccurred() && !imageSizeForRenderer(renderer, multiplier).isEmpty(); }
+ bool canRender(const RenderElement* renderer, float multiplier) { return !errorOccurred() && !imageSizeForRenderer(renderer, multiplier).isEmpty(); }
- void setContainerSizeForRenderer(const CachedImageClient*, const IntSize&, float);
+ void setContainerSizeForRenderer(const CachedImageClient*, const LayoutSize&, float);
bool usesImageContainerSize() const;
bool imageHasRelativeWidth() const;
bool imageHasRelativeHeight() const;
- virtual void addDataBuffer(ResourceBuffer*) override;
- virtual void finishLoading(ResourceBuffer*) override;
+ void addDataBuffer(SharedBuffer&) override;
+ void finishLoading(SharedBuffer*) override;
enum SizeType {
UsedSize,
IntrinsicSize
};
// This method takes a zoom multiplier that can be used to increase the natural size of the image by the zoom.
- LayoutSize imageSizeForRenderer(const RenderObject*, float multiplier, SizeType = UsedSize); // returns the size of the complete image.
+ LayoutSize imageSizeForRenderer(const RenderElement*, float multiplier, SizeType = UsedSize); // returns the size of the complete image.
void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio);
-#if USE(CF)
- // FIXME: Remove the USE(CF) once we make MemoryCache::addImageToCache() platform-independent.
- virtual bool isManual() const { return false; }
-#endif
-
- static void resumeAnimatingImagesForLoader(CachedResourceLoader*);
-
-#if ENABLE(DISK_IMAGE_CACHE)
- virtual bool canUseDiskImageCache() const override;
- virtual void useDiskImageCache() override;
-#endif
+ bool isManuallyCached() const { return m_isManuallyCached; }
+ RevalidationDecision makeRevalidationDecision(CachePolicy) const override;
+ void load(CachedResourceLoader&) override;
bool isOriginClean(SecurityOrigin*);
private:
- virtual void load(CachedResourceLoader*, const ResourceLoaderOptions&) override;
-
void clear();
+ CachedImage(CachedImage&, const ResourceRequest&, SessionID);
+
+ void setBodyDataFrom(const CachedResource&) final;
+
void createImage();
void clearImage();
- bool canBeDrawn() const;
// If not null, changeRect is the changed part of the image.
- void notifyObservers(const IntRect* changeRect = 0);
- virtual PurgePriority purgePriority() const override { return PurgeFirst; }
+ void notifyObservers(const IntRect* changeRect = nullptr);
void checkShouldPaintBrokenImage();
- virtual void switchClientsToRevalidatedResource() override;
- virtual bool mayTryReplaceEncodedData() const override { return true; }
+ void switchClientsToRevalidatedResource() final;
+ bool mayTryReplaceEncodedData() const final { return true; }
- virtual void didAddClient(CachedResourceClient*) override;
- virtual void didRemoveClient(CachedResourceClient*) override;
+ void didAddClient(CachedResourceClient&) final;
+ void didRemoveClient(CachedResourceClient&) final;
- virtual void allClientsRemoved() override;
- virtual void destroyDecodedData() override;
+ void allClientsRemoved() override;
+ void destroyDecodedData() override;
- virtual void addData(const char* data, unsigned length) override;
- virtual void error(CachedResource::Status) override;
- virtual void responseReceived(const ResourceResponse&) override;
+ void addData(const char* data, unsigned length) override;
+ void error(CachedResource::Status) override;
+ void responseReceived(const ResourceResponse&) override;
// For compatibility, images keep loading even if there are HTTP errors.
- virtual bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; }
-
- virtual bool isImage() const override { return true; }
- virtual bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); }
+ bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; }
+
+ bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); }
+
+ class CachedImageObserver final : public RefCounted<CachedImageObserver>, public ImageObserver {
+ public:
+ static Ref<CachedImageObserver> create(CachedImage& image) { return adoptRef(*new CachedImageObserver(image)); }
+ void add(CachedImage& image) { m_cachedImages.append(&image); }
+ void remove(CachedImage& image) { m_cachedImages.removeFirst(&image); }
+
+ private:
+ explicit CachedImageObserver(CachedImage&);
+
+ // ImageObserver API
+ URL sourceUrl() const override { return m_cachedImages[0]->url(); }
+ bool allowSubsampling() const final { return m_allowSubsampling; }
+ bool allowLargeImageAsyncDecoding() const override { return m_allowLargeImageAsyncDecoding; }
+ bool allowAnimatedImageAsyncDecoding() const override { return m_allowAnimatedImageAsyncDecoding; }
+ bool showDebugBackground() const final { return m_showDebugBackground; }
+ void decodedSizeChanged(const Image*, long long delta) final;
+ void didDraw(const Image*) final;
+
+ void animationAdvanced(const Image*) final;
+ void changedInRect(const Image*, const IntRect*) final;
+
+ Vector<CachedImage*> m_cachedImages;
+ // The default value of m_allowSubsampling should be the same as defaultImageSubsamplingEnabled in Settings.cpp
+#if PLATFORM(IOS)
+ bool m_allowSubsampling { true };
+#else
+ bool m_allowSubsampling { false };
+#endif
+ bool m_allowLargeImageAsyncDecoding { true };
+ bool m_allowAnimatedImageAsyncDecoding { true };
+ bool m_showDebugBackground { false };
+ };
- // ImageObserver
- virtual void decodedSizeChanged(const Image*, int delta) override;
- virtual void didDraw(const Image*) override;
+ void decodedSizeChanged(const Image*, long long delta);
+ void didDraw(const Image*);
+ void animationAdvanced(const Image*);
+ void changedInRect(const Image*, const IntRect*);
- virtual bool shouldPauseAnimation(const Image*) override;
- virtual void animationAdvanced(const Image*) override;
- virtual void changedInRect(const Image*, const IntRect&) override;
+ void addIncrementalDataBuffer(SharedBuffer&);
- void addIncrementalDataBuffer(ResourceBuffer*);
+ void didReplaceSharedBufferContents() override;
- typedef std::pair<IntSize, float> SizeAndZoom;
+ typedef std::pair<LayoutSize, float> SizeAndZoom;
typedef HashMap<const CachedImageClient*, SizeAndZoom> ContainerSizeRequests;
ContainerSizeRequests m_pendingContainerSizeRequests;
+ RefPtr<CachedImageObserver> m_imageObserver;
RefPtr<Image> m_image;
-#if ENABLE(SVG)
std::unique_ptr<SVGImageCache> m_svgImageCache;
-#endif
- bool m_shouldPaintBrokenImage;
-};
-
-#if USE(CF)
-// FIXME: We should look to incorporate the functionality of CachedImageManual
-// into CachedImage or find a better place for this class.
-// FIXME: Remove the USE(CF) once we make MemoryCache::addImageToCache() platform-independent.
-class CachedImageManual : public CachedImage {
-public:
- CachedImageManual(const URL&, Image*);
- void addFakeClient() { addClient(m_fakeClient.get()); }
- void removeFakeClient() { removeClient(m_fakeClient.get()); }
- virtual bool isManual() const override { return true; }
- virtual bool mustRevalidateDueToCacheHeaders(CachePolicy) const;
-private:
- std::unique_ptr<CachedResourceClient> m_fakeClient;
+ bool m_isManuallyCached { false };
+ bool m_shouldPaintBrokenImage { true };
};
-#endif
-}
+} // namespace WebCore
-#endif
+SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedImage, CachedResource::ImageResource)
diff --git a/Source/WebCore/loader/cache/CachedImageClient.h b/Source/WebCore/loader/cache/CachedImageClient.h
index 1c3fac206..e09621ed8 100644
--- a/Source/WebCore/loader/cache/CachedImageClient.h
+++ b/Source/WebCore/loader/cache/CachedImageClient.h
@@ -20,8 +20,7 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef CachedImageClient_h
-#define CachedImageClient_h
+#pragma once
#include "CachedResourceClient.h"
@@ -34,19 +33,14 @@ class CachedImageClient : public CachedResourceClient {
public:
virtual ~CachedImageClient() { }
static CachedResourceClientType expectedType() { return ImageType; }
- virtual CachedResourceClientType resourceClientType() const override { return expectedType(); }
+ CachedResourceClientType resourceClientType() const override { return expectedType(); }
- // 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 whenever a frame of an image changes because we got more data from the network.
+ // If not null, the IntRect is the changed rect of the image.
+ virtual void imageChanged(CachedImage*, const IntRect* = nullptr) { }
- // 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; }
+ // Called when GIF animation progresses.
+ virtual void newImageAnimationFrameAvailable(CachedImage& image) { imageChanged(&image); }
};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedRawResource.cpp b/Source/WebCore/loader/cache/CachedRawResource.cpp
index a6eff5efb..05b9de128 100644
--- a/Source/WebCore/loader/cache/CachedRawResource.cpp
+++ b/Source/WebCore/loader/cache/CachedRawResource.cpp
@@ -29,23 +29,26 @@
#include "CachedRawResourceClient.h"
#include "CachedResourceClientWalker.h"
#include "CachedResourceLoader.h"
-#include "ResourceBuffer.h"
+#include "HTTPHeaderNames.h"
+#include "SharedBuffer.h"
#include "SubresourceLoader.h"
-#include <wtf/PassRefPtr.h>
+#include <wtf/text/StringView.h>
namespace WebCore {
-CachedRawResource::CachedRawResource(ResourceRequest& resourceRequest, Type type)
- : CachedResource(resourceRequest, type)
+CachedRawResource::CachedRawResource(CachedResourceRequest&& request, Type type, SessionID sessionID)
+ : CachedResource(WTFMove(request), type, sessionID)
, m_identifier(0)
+ , m_allowEncodedDataReplacement(true)
{
+ ASSERT(isMainOrMediaOrRawResource());
}
-const char* CachedRawResource::calculateIncrementalDataChunk(ResourceBuffer* data, unsigned& incrementalDataLength)
+const char* CachedRawResource::calculateIncrementalDataChunk(SharedBuffer* data, unsigned& incrementalDataLength)
{
incrementalDataLength = 0;
if (!data)
- return 0;
+ return nullptr;
unsigned previousDataLength = encodedSize();
ASSERT(data->size() >= previousDataLength);
@@ -53,34 +56,37 @@ const char* CachedRawResource::calculateIncrementalDataChunk(ResourceBuffer* dat
return data->data() + previousDataLength;
}
-void CachedRawResource::addDataBuffer(ResourceBuffer* data)
+void CachedRawResource::addDataBuffer(SharedBuffer& data)
{
- CachedResourceHandle<CachedRawResource> protect(this);
- ASSERT(m_options.dataBufferingPolicy == BufferData);
- m_data = data;
+ CachedResourceHandle<CachedRawResource> protectedThis(this);
+ ASSERT(dataBufferingPolicy() == BufferData);
+ m_data = &data;
unsigned incrementalDataLength;
- const char* incrementalData = calculateIncrementalDataChunk(data, incrementalDataLength);
- if (data)
- setEncodedSize(data->size());
+ const char* incrementalData = calculateIncrementalDataChunk(&data, incrementalDataLength);
+ setEncodedSize(data.size());
notifyClientsDataWasReceived(incrementalData, incrementalDataLength);
- if (m_options.dataBufferingPolicy == DoNotBufferData) {
+ if (dataBufferingPolicy() == DoNotBufferData) {
if (m_loader)
m_loader->setDataBufferingPolicy(DoNotBufferData);
clear();
+ return;
}
+
+ CachedResource::addDataBuffer(data);
}
void CachedRawResource::addData(const char* data, unsigned length)
{
- ASSERT(m_options.dataBufferingPolicy == DoNotBufferData);
+ ASSERT(dataBufferingPolicy() == DoNotBufferData);
notifyClientsDataWasReceived(data, length);
+ CachedResource::addData(data, length);
}
-void CachedRawResource::finishLoading(ResourceBuffer* data)
+void CachedRawResource::finishLoading(SharedBuffer* data)
{
- CachedResourceHandle<CachedRawResource> protect(this);
- DataBufferingPolicy dataBufferingPolicy = m_options.dataBufferingPolicy;
+ CachedResourceHandle<CachedRawResource> protectedThis(this);
+ DataBufferingPolicy dataBufferingPolicy = this->dataBufferingPolicy();
if (dataBufferingPolicy == BufferData) {
m_data = data;
@@ -91,8 +97,12 @@ void CachedRawResource::finishLoading(ResourceBuffer* data)
notifyClientsDataWasReceived(incrementalData, incrementalDataLength);
}
+#if USE(QUICK_LOOK)
+ m_allowEncodedDataReplacement = !m_loader->isQuickLookResource();
+#endif
+
CachedResource::finishLoading(data);
- if (dataBufferingPolicy == BufferData && m_options.dataBufferingPolicy == DoNotBufferData) {
+ if (dataBufferingPolicy == BufferData && this->dataBufferingPolicy() == DoNotBufferData) {
if (m_loader)
m_loader->setDataBufferingPolicy(DoNotBufferData);
clear();
@@ -104,37 +114,45 @@ void CachedRawResource::notifyClientsDataWasReceived(const char* data, unsigned
if (!length)
return;
- CachedResourceHandle<CachedRawResource> protect(this);
+ CachedResourceHandle<CachedRawResource> protectedThis(this);
CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
while (CachedRawResourceClient* c = w.next())
- c->dataReceived(this, data, length);
+ c->dataReceived(*this, data, length);
}
-void CachedRawResource::didAddClient(CachedResourceClient* c)
+void CachedRawResource::didAddClient(CachedResourceClient& c)
{
if (!hasClient(c))
return;
// The calls to the client can result in events running, potentially causing
// this resource to be evicted from the cache and all clients to be removed,
// so a protector is necessary.
- CachedResourceHandle<CachedRawResource> protect(this);
- CachedRawResourceClient* client = static_cast<CachedRawResourceClient*>(c);
+ CachedResourceHandle<CachedRawResource> protectedThis(this);
+ CachedRawResourceClient& client = static_cast<CachedRawResourceClient&>(c);
size_t redirectCount = m_redirectChain.size();
for (size_t i = 0; i < redirectCount; i++) {
RedirectPair redirect = m_redirectChain[i];
ResourceRequest request(redirect.m_request);
- client->redirectReceived(this, request, redirect.m_redirectResponse);
+ client.redirectReceived(*this, request, redirect.m_redirectResponse);
if (!hasClient(c))
return;
}
ASSERT(redirectCount == m_redirectChain.size());
- if (!m_response.isNull())
- client->responseReceived(this, m_response);
+ if (!m_response.isNull()) {
+ ResourceResponse response(m_response);
+ if (validationCompleting())
+ response.setSource(ResourceResponse::Source::MemoryCacheAfterValidation);
+ else {
+ ASSERT(!validationInProgress());
+ response.setSource(ResourceResponse::Source::MemoryCache);
+ }
+ client.responseReceived(*this, response);
+ }
if (!hasClient(c))
return;
if (m_data)
- client->dataReceived(this, m_data->data(), m_data->size());
+ client.dataReceived(*this, m_data->data(), m_data->size());
if (!hasClient(c))
return;
CachedResource::didAddClient(client);
@@ -146,34 +164,51 @@ void CachedRawResource::allClientsRemoved()
m_loader->cancelIfNotFinishing();
}
-void CachedRawResource::willSendRequest(ResourceRequest& request, const ResourceResponse& response)
+void CachedRawResource::redirectReceived(ResourceRequest& request, const ResourceResponse& response)
{
- CachedResourceHandle<CachedRawResource> protect(this);
+ CachedResourceHandle<CachedRawResource> protectedThis(this);
if (!response.isNull()) {
CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
while (CachedRawResourceClient* c = w.next())
- c->redirectReceived(this, request, response);
+ c->redirectReceived(*this, request, response);
m_redirectChain.append(RedirectPair(request, response));
}
- CachedResource::willSendRequest(request, response);
+ CachedResource::redirectReceived(request, response);
}
void CachedRawResource::responseReceived(const ResourceResponse& response)
{
- CachedResourceHandle<CachedRawResource> protect(this);
+ CachedResourceHandle<CachedRawResource> protectedThis(this);
if (!m_identifier)
m_identifier = m_loader->identifier();
CachedResource::responseReceived(response);
CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
while (CachedRawResourceClient* c = w.next())
- c->responseReceived(this, m_response);
+ c->responseReceived(*this, m_response);
+}
+
+bool CachedRawResource::shouldCacheResponse(const ResourceResponse& response)
+{
+ CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
+ while (CachedRawResourceClient* c = w.next()) {
+ if (!c->shouldCacheResponse(*this, response))
+ return false;
+ }
+ return true;
}
void CachedRawResource::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
{
CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
while (CachedRawResourceClient* c = w.next())
- c->dataSent(this, bytesSent, totalBytesToBeSent);
+ c->dataSent(*this, bytesSent, totalBytesToBeSent);
+}
+
+void CachedRawResource::finishedTimingForWorkerLoad(ResourceTiming&& resourceTiming)
+{
+ CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
+ while (CachedRawResourceClient* c = w.next())
+ c->finishedTimingForWorkerLoad(*this, resourceTiming);
}
void CachedRawResource::switchClientsToRevalidatedResource()
@@ -181,7 +216,7 @@ void CachedRawResource::switchClientsToRevalidatedResource()
ASSERT(m_loader);
// If we're in the middle of a successful revalidation, responseReceived() hasn't been called, so we haven't set m_identifier.
ASSERT(!m_identifier);
- static_cast<CachedRawResource*>(resourceToRevalidate())->m_identifier = m_loader->identifier();
+ downcast<CachedRawResource>(*resourceToRevalidate()).m_identifier = m_loader->identifier();
CachedResource::switchClientsToRevalidatedResource();
}
@@ -196,25 +231,26 @@ void CachedRawResource::setDataBufferingPolicy(DataBufferingPolicy dataBuffering
m_options.dataBufferingPolicy = dataBufferingPolicy;
}
-static bool shouldIgnoreHeaderForCacheReuse(AtomicString headerName)
+static bool shouldIgnoreHeaderForCacheReuse(HTTPHeaderName name)
{
+ switch (name) {
// FIXME: This list of headers that don't affect cache policy almost certainly isn't complete.
- DEFINE_STATIC_LOCAL(HashSet<AtomicString>, m_headers, ());
- if (m_headers.isEmpty()) {
- m_headers.add("Accept");
- m_headers.add("Cache-Control");
- m_headers.add("Origin");
- m_headers.add("Pragma");
- m_headers.add("Purpose");
- m_headers.add("Referer");
- m_headers.add("User-Agent");
+ case HTTPHeaderName::Accept:
+ case HTTPHeaderName::CacheControl:
+ case HTTPHeaderName::Pragma:
+ case HTTPHeaderName::Purpose:
+ case HTTPHeaderName::Referer:
+ case HTTPHeaderName::UserAgent:
+ return true;
+
+ default:
+ return false;
}
- return m_headers.contains(headerName);
}
bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const
{
- if (m_options.dataBufferingPolicy == DoNotBufferData)
+ if (dataBufferingPolicy() == DoNotBufferData)
return false;
if (m_resourceRequest.httpMethod() != newRequest.httpMethod())
@@ -226,6 +262,9 @@ bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const
if (m_resourceRequest.allowCookies() != newRequest.allowCookies())
return false;
+ if (newRequest.isConditional())
+ return false;
+
// Ensure most headers match the existing headers before continuing.
// Note that the list of ignored headers includes some headers explicitly related to caching.
// A more detailed check of caching policy will be performed later, this is simply a list of
@@ -233,22 +272,23 @@ bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const
const HTTPHeaderMap& newHeaders = newRequest.httpHeaderFields();
const HTTPHeaderMap& oldHeaders = m_resourceRequest.httpHeaderFields();
- HTTPHeaderMap::const_iterator end = newHeaders.end();
- for (HTTPHeaderMap::const_iterator i = newHeaders.begin(); i != end; ++i) {
- AtomicString headerName = i->key;
- if (!shouldIgnoreHeaderForCacheReuse(headerName) && i->value != oldHeaders.get(headerName))
- return false;
- }
-
- end = oldHeaders.end();
- for (HTTPHeaderMap::const_iterator i = oldHeaders.begin(); i != end; ++i) {
- AtomicString headerName = i->key;
- if (!shouldIgnoreHeaderForCacheReuse(headerName) && i->value != newHeaders.get(headerName))
+ for (const auto& header : newHeaders) {
+ if (header.keyAsHTTPHeaderName) {
+ if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value())
+ && header.value != oldHeaders.commonHeaders().get(header.keyAsHTTPHeaderName.value()))
+ return false;
+ } else if (header.value != oldHeaders.uncommonHeaders().get(header.key))
return false;
}
- for (size_t i = 0; i < m_redirectChain.size(); i++) {
- if (m_redirectChain[i].m_redirectResponse.cacheControlContainsNoStore())
+ // For this second loop, we don't actually need to compare values, checking that the
+ // key is contained in newHeaders is sufficient due to the previous loop.
+ for (const auto& header : oldHeaders) {
+ if (header.keyAsHTTPHeaderName) {
+ if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value())
+ && !newHeaders.commonHeaders().contains(header.keyAsHTTPHeaderName.value()))
+ return false;
+ } else if (!newHeaders.uncommonHeaders().contains(header.key))
return false;
}
@@ -257,7 +297,7 @@ bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const
void CachedRawResource::clear()
{
- m_data.clear();
+ m_data = nullptr;
setEncodedSize(0);
if (m_loader)
m_loader->clearResourceData();
diff --git a/Source/WebCore/loader/cache/CachedRawResource.h b/Source/WebCore/loader/cache/CachedRawResource.h
index 48e04ffec..7d7468c0e 100644
--- a/Source/WebCore/loader/cache/CachedRawResource.h
+++ b/Source/WebCore/loader/cache/CachedRawResource.h
@@ -20,19 +20,19 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef CachedRawResource_h
-#define CachedRawResource_h
+#pragma once
#include "CachedResource.h"
namespace WebCore {
class CachedResourceClient;
+class ResourceTiming;
class SubresourceLoader;
class CachedRawResource final : public CachedResource {
public:
- CachedRawResource(ResourceRequest&, Type);
+ CachedRawResource(CachedResourceRequest&&, Type, SessionID);
// FIXME: AssociatedURLLoader shouldn't be a DocumentThreadableLoader and therefore shouldn't
// use CachedRawResource. However, it is, and it needs to be able to defer loading.
@@ -40,38 +40,44 @@ public:
virtual void setDefersLoading(bool);
virtual void setDataBufferingPolicy(DataBufferingPolicy);
-
- // FIXME: This is exposed for the InpsectorInstrumentation for preflights in DocumentThreadableLoader. It's also really lame.
+
+ // FIXME: This is exposed for the InspectorInstrumentation for preflights in DocumentThreadableLoader. It's also really lame.
unsigned long identifier() const { return m_identifier; }
void clear();
-private:
- virtual void didAddClient(CachedResourceClient*) override;
- virtual void addDataBuffer(ResourceBuffer*) override;
- virtual void addData(const char* data, unsigned length) override;
- virtual void finishLoading(ResourceBuffer*) override;
+ bool canReuse(const ResourceRequest&) const;
+
+ bool wasRedirected() const { return !m_redirectChain.isEmpty(); };
- virtual bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; }
- virtual void allClientsRemoved() override;
+ void finishedTimingForWorkerLoad(ResourceTiming&&);
+
+private:
+ void didAddClient(CachedResourceClient&) final;
+ void addDataBuffer(SharedBuffer&) final;
+ void addData(const char* data, unsigned length) final;
+ void finishLoading(SharedBuffer*) final;
- virtual void willSendRequest(ResourceRequest&, const ResourceResponse&) override;
- virtual void responseReceived(const ResourceResponse&) override;
- virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override;
+ bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; }
+ void allClientsRemoved() override;
- virtual void switchClientsToRevalidatedResource() override;
- virtual bool mayTryReplaceEncodedData() const override { return true; }
+ void redirectReceived(ResourceRequest&, const ResourceResponse&) override;
+ void responseReceived(const ResourceResponse&) override;
+ bool shouldCacheResponse(const ResourceResponse&) override;
+ void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override;
- virtual bool canReuse(const ResourceRequest&) const override;
+ void switchClientsToRevalidatedResource() override;
+ bool mayTryReplaceEncodedData() const override { return m_allowEncodedDataReplacement; }
- const char* calculateIncrementalDataChunk(ResourceBuffer*, unsigned& incrementalDataLength);
+ const char* calculateIncrementalDataChunk(SharedBuffer*, unsigned& incrementalDataLength);
void notifyClientsDataWasReceived(const char* data, unsigned length);
#if USE(SOUP)
- virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize);
+ char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override;
#endif
unsigned long m_identifier;
+ bool m_allowEncodedDataReplacement;
struct RedirectPair {
public:
@@ -88,6 +94,8 @@ private:
Vector<RedirectPair> m_redirectChain;
};
-}
+} // namespace WebCore
-#endif // CachedRawResource_h
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::CachedRawResource)
+ static bool isType(const WebCore::CachedResource& resource) { return resource.isMainOrMediaOrRawResource(); }
+SPECIALIZE_TYPE_TRAITS_END()
diff --git a/Source/WebCore/loader/cache/CachedRawResourceClient.h b/Source/WebCore/loader/cache/CachedRawResourceClient.h
index 3d7fd5e5c..2e9fac591 100644
--- a/Source/WebCore/loader/cache/CachedRawResourceClient.h
+++ b/Source/WebCore/loader/cache/CachedRawResourceClient.h
@@ -20,8 +20,7 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef CachedRawResourceClient_h
-#define CachedRawResourceClient_h
+#pragma once
#include "CachedResourceClient.h"
@@ -30,22 +29,23 @@ namespace WebCore {
class CachedResource;
class ResourceRequest;
class ResourceResponse;
+class ResourceTiming;
class CachedRawResourceClient : public CachedResourceClient {
public:
virtual ~CachedRawResourceClient() { }
static CachedResourceClientType expectedType() { return RawResourceType; }
- virtual CachedResourceClientType resourceClientType() const override { return expectedType(); }
-
- virtual void dataSent(CachedResource*, unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { }
- virtual void responseReceived(CachedResource*, const ResourceResponse&) { }
- virtual void dataReceived(CachedResource*, const char* /* data */, int /* length */) { }
- virtual void redirectReceived(CachedResource*, ResourceRequest&, const ResourceResponse&) { }
+ CachedResourceClientType resourceClientType() const override { return expectedType(); }
+
+ virtual void dataSent(CachedResource&, unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { }
+ virtual void responseReceived(CachedResource&, const ResourceResponse&) { }
+ virtual bool shouldCacheResponse(CachedResource&, const ResourceResponse&) { return true; }
+ virtual void dataReceived(CachedResource&, const char* /* data */, int /* length */) { }
+ virtual void redirectReceived(CachedResource&, ResourceRequest&, const ResourceResponse&) { }
+ virtual void finishedTimingForWorkerLoad(CachedResource&, const ResourceTiming&) { }
#if USE(SOUP)
- virtual char* getOrCreateReadBuffer(CachedResource*, size_t /* requestedSize */, size_t& /* actualSize */) { return 0; }
+ virtual char* getOrCreateReadBuffer(CachedResource&, size_t /* requestedSize */, size_t& /* actualSize */) { return nullptr; }
#endif
};
}
-
-#endif // CachedRawResourceClient_h
diff --git a/Source/WebCore/loader/cache/CachedResource.cpp b/Source/WebCore/loader/cache/CachedResource.cpp
index 5f0bc76fa..8b09d3285 100644
--- a/Source/WebCore/loader/cache/CachedResource.cpp
+++ b/Source/WebCore/loader/cache/CachedResource.cpp
@@ -3,7 +3,7 @@
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, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ Copyright (C) 2004-2011, 2014 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
@@ -29,23 +29,23 @@
#include "CachedResourceHandle.h"
#include "CachedResourceLoader.h"
#include "CrossOriginAccessControl.h"
+#include "DiagnosticLoggingClient.h"
+#include "DiagnosticLoggingKeys.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
+#include "HTTPHeaderNames.h"
#include "InspectorInstrumentation.h"
#include "URL.h"
#include "LoaderStrategy.h"
#include "Logging.h"
+#include "MainFrame.h"
#include "MemoryCache.h"
#include "PlatformStrategies.h"
-#include "PurgeableBuffer.h"
-#include "ResourceBuffer.h"
#include "ResourceHandle.h"
-#include "ResourceLoadScheduler.h"
#include "SchemeRegistry.h"
#include "SecurityOrigin.h"
-#include "SecurityPolicy.h"
#include "SubresourceLoader.h"
#include <wtf/CurrentTime.h>
#include <wtf/MathExtras.h>
@@ -60,139 +60,99 @@
using namespace WTF;
-namespace WebCore {
+#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(cachedResourceLoader.isAlwaysOnLoggingAllowed(), Network, "%p - CachedResource::" fmt, this, ##__VA_ARGS__)
-// These response headers are not copied from a revalidated response to the
-// cached response headers. For compatibility, this list is based on Chromium's
-// net/http/http_response_headers.cc.
-const char* const headersToIgnoreAfterRevalidation[] = {
- "allow",
- "connection",
- "etag",
- "expires",
- "keep-alive",
- "last-modified"
- "proxy-authenticate",
- "proxy-connection",
- "trailer",
- "transfer-encoding",
- "upgrade",
- "www-authenticate",
- "x-frame-options",
- "x-xss-protection",
-};
-
-// Some header prefixes mean "Don't copy this header from a 304 response.".
-// Rather than listing all the relevant headers, we can consolidate them into
-// this list, also grabbed from Chromium's net/http/http_response_headers.cc.
-const char* const headerPrefixesToIgnoreAfterRevalidation[] = {
- "content-",
- "x-content-",
- "x-webkit-"
-};
-
-static inline bool shouldUpdateHeaderAfterRevalidation(const AtomicString& header)
-{
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation); i++) {
- if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i]))
- return false;
- }
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) {
- if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], false))
- return false;
- }
- return true;
-}
+namespace WebCore {
-static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type)
+ResourceLoadPriority CachedResource::defaultPriorityForResourceType(Type type)
{
switch (type) {
case CachedResource::MainResource:
- return ResourceLoadPriorityVeryHigh;
+ return ResourceLoadPriority::VeryHigh;
case CachedResource::CSSStyleSheet:
- return ResourceLoadPriorityHigh;
case CachedResource::Script:
+ return ResourceLoadPriority::High;
+#if ENABLE(SVG_FONTS)
+ case CachedResource::SVGFontResource:
+#endif
+ case CachedResource::MediaResource:
case CachedResource::FontResource:
case CachedResource::RawResource:
- return ResourceLoadPriorityMedium;
+ return ResourceLoadPriority::Medium;
case CachedResource::ImageResource:
- return ResourceLoadPriorityLow;
+ return ResourceLoadPriority::Low;
#if ENABLE(XSLT)
case CachedResource::XSLStyleSheet:
- return ResourceLoadPriorityHigh;
+ return ResourceLoadPriority::High;
#endif
-#if ENABLE(SVG)
case CachedResource::SVGDocumentResource:
- return ResourceLoadPriorityLow;
-#endif
+ return ResourceLoadPriority::Low;
#if ENABLE(LINK_PREFETCH)
case CachedResource::LinkPrefetch:
- return ResourceLoadPriorityVeryLow;
+ return ResourceLoadPriority::VeryLow;
case CachedResource::LinkSubresource:
- return ResourceLoadPriorityVeryLow;
+ return ResourceLoadPriority::VeryLow;
#endif
#if ENABLE(VIDEO_TRACK)
case CachedResource::TextTrackResource:
- return ResourceLoadPriorityLow;
+ return ResourceLoadPriority::Low;
#endif
}
ASSERT_NOT_REACHED();
- return ResourceLoadPriorityLow;
+ return ResourceLoadPriority::Low;
}
-static double deadDecodedDataDeletionIntervalForResourceType(CachedResource::Type type)
+static std::chrono::milliseconds deadDecodedDataDeletionIntervalForResourceType(CachedResource::Type type)
{
if (type == CachedResource::Script)
- return 0;
- return memoryCache()->deadDecodedDataDeletionInterval();
+ return std::chrono::milliseconds { 0 };
+
+ return MemoryCache::singleton().deadDecodedDataDeletionInterval();
}
DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("CachedResource"));
-CachedResource::CachedResource(const ResourceRequest& request, Type type)
- : m_resourceRequest(request)
+CachedResource::CachedResource(CachedResourceRequest&& request, Type type, SessionID sessionID)
+ : m_resourceRequest(request.releaseResourceRequest())
+ , m_options(request.options())
+ , m_decodedDataDeletionTimer(*this, &CachedResource::destroyDecodedData, deadDecodedDataDeletionIntervalForResourceType(type))
+ , m_sessionID(sessionID)
, m_loadPriority(defaultPriorityForResourceType(type))
- , m_responseTimestamp(currentTime())
- , m_decodedDataDeletionTimer(this, &CachedResource::decodedDataDeletionTimerFired, deadDecodedDataDeletionIntervalForResourceType(type))
- , m_lastDecodedAccessTime(0)
- , m_loadFinishTime(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_inCache(false)
- , m_loading(false)
- , m_switchingClientsToRevalidatedResource(false)
+ , m_responseTimestamp(std::chrono::system_clock::now())
+ , m_fragmentIdentifierForRequest(request.releaseFragmentIdentifier())
+ , m_origin(request.releaseOrigin())
+ , m_initiatorName(request.initiatorName())
+ , m_isLinkPreload(request.isLinkPreload())
, m_type(type)
- , m_status(Pending)
+{
+ ASSERT(sessionID.isValid());
+
+ setLoadPriority(request.priority());
#ifndef NDEBUG
- , m_deleted(false)
- , m_lruIndex(0)
+ cachedResourceLeakCounter.increment();
#endif
- , m_nextInAllResourcesList(0)
- , m_prevInAllResourcesList(0)
- , m_nextInLiveResourcesList(0)
- , m_prevInLiveResourcesList(0)
- , m_owningCachedResourceLoader(0)
- , m_resourceToRevalidate(0)
- , m_proxyResource(0)
-{
- ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum.
+
+ // FIXME: We should have a better way of checking for Navigation loads, maybe FetchMode::Options::Navigate.
+ ASSERT(m_origin || m_type == CachedResource::MainResource);
+
+ if (isRequestCrossOrigin(m_origin.get(), m_resourceRequest.url(), m_options))
+ setCrossOrigin();
+}
+
+// FIXME: For this constructor, we should probably mandate that the URL has no fragment identifier.
+CachedResource::CachedResource(const URL& url, Type type, SessionID sessionID)
+ : m_resourceRequest(url)
+ , m_decodedDataDeletionTimer(*this, &CachedResource::destroyDecodedData, deadDecodedDataDeletionIntervalForResourceType(type))
+ , m_sessionID(sessionID)
+ , m_responseTimestamp(std::chrono::system_clock::now())
+ , m_fragmentIdentifierForRequest(CachedResourceRequest::splitFragmentIdentifierFromRequestURL(m_resourceRequest))
+ , m_type(type)
+ , m_status(Cached)
+{
+ ASSERT(sessionID.isValid());
#ifndef NDEBUG
cachedResourceLeakCounter.increment();
#endif
-
- if (!m_resourceRequest.url().hasFragmentIdentifier())
- return;
- URL urlForCache = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url());
- if (urlForCache.hasFragmentIdentifier())
- return;
- m_fragmentIdentifierForRequest = m_resourceRequest.url().fragmentIdentifier();
- m_resourceRequest.setURL(urlForCache);
}
CachedResource::~CachedResource()
@@ -201,7 +161,7 @@ CachedResource::~CachedResource()
ASSERT(canDelete());
ASSERT(!inCache());
ASSERT(!m_deleted);
- ASSERT(url().isNull() || memoryCache()->resourceForRequest(resourceRequest()) != this);
+ ASSERT(url().isNull() || !allowsCaching() || MemoryCache::singleton().resourceForRequest(resourceRequest(), sessionID()) != this);
#ifndef NDEBUG
m_deleted = true;
@@ -209,99 +169,81 @@ CachedResource::~CachedResource()
#endif
if (m_owningCachedResourceLoader)
- m_owningCachedResourceLoader->removeCachedResource(this);
+ m_owningCachedResourceLoader->removeCachedResource(*this);
}
void CachedResource::failBeforeStarting()
{
// FIXME: What if resources in other frames were waiting for this revalidation?
LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data());
- if (m_resourceToRevalidate)
- memoryCache()->revalidationFailed(this);
+ if (allowsCaching() && m_resourceToRevalidate)
+ MemoryCache::singleton().revalidationFailed(*this);
error(CachedResource::LoadError);
}
-void CachedResource::addAdditionalRequestHeaders(CachedResourceLoader* cachedResourceLoader)
+void CachedResource::load(CachedResourceLoader& cachedResourceLoader)
{
- // Note: We skip the Content-Security-Policy check here because we check
- // the Content-Security-Policy at the CachedResourceLoader layer so we can
- // handle different resource types differently.
-
- FrameLoader& frameLoader = cachedResourceLoader->frame()->loader();
- String outgoingReferrer;
- String outgoingOrigin;
- if (m_resourceRequest.httpReferrer().isNull()) {
- outgoingReferrer = frameLoader.outgoingReferrer();
- outgoingOrigin = frameLoader.outgoingOrigin();
- } else {
- outgoingReferrer = m_resourceRequest.httpReferrer();
- outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
- }
-
- outgoingReferrer = SecurityPolicy::generateReferrerHeader(cachedResourceLoader->document()->referrerPolicy(), m_resourceRequest.url(), outgoingReferrer);
- if (outgoingReferrer.isEmpty())
- m_resourceRequest.clearHTTPReferrer();
- else if (!m_resourceRequest.httpReferrer())
- m_resourceRequest.setHTTPReferrer(outgoingReferrer);
- FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
-
- frameLoader.addExtraFieldsToSubresourceRequest(m_resourceRequest);
-}
-
-void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
-{
- if (!cachedResourceLoader->frame()) {
+ if (!cachedResourceLoader.frame()) {
+ RELEASE_LOG_IF_ALLOWED("load: No associated frame");
failBeforeStarting();
return;
}
+ Frame& frame = *cachedResourceLoader.frame();
+
+ // Prevent new loads if we are in the PageCache or being added to the PageCache.
+ // We query the top document because new frames may be created in pagehide event handlers
+ // and their pageCacheState will not reflect the fact that they are about to enter page
+ // cache.
+ if (auto* topDocument = frame.mainFrame().document()) {
+ if (topDocument->pageCacheState() != Document::NotInPageCache) {
+ RELEASE_LOG_IF_ALLOWED("load: Already in page cache or being added to it (frame = %p)", &frame);
+ failBeforeStarting();
+ return;
+ }
+ }
- FrameLoader& frameLoader = cachedResourceLoader->frame()->loader();
- if (options.securityCheck == DoSecurityCheck && (frameLoader.state() == FrameStateProvisional || !frameLoader.activeDocumentLoader() || frameLoader.activeDocumentLoader()->isStopping())) {
+ FrameLoader& frameLoader = frame.loader();
+ if (m_options.securityCheck == DoSecurityCheck && (frameLoader.state() == FrameStateProvisional || !frameLoader.activeDocumentLoader() || frameLoader.activeDocumentLoader()->isStopping())) {
+ if (frameLoader.state() == FrameStateProvisional)
+ RELEASE_LOG_IF_ALLOWED("load: Failed security check -- state is provisional (frame = %p)", &frame);
+ else if (!frameLoader.activeDocumentLoader())
+ RELEASE_LOG_IF_ALLOWED("load: Failed security check -- not active document (frame = %p)", &frame);
+ else if (frameLoader.activeDocumentLoader()->isStopping())
+ RELEASE_LOG_IF_ALLOWED("load: Failed security check -- active loader is stopping (frame = %p)", &frame);
failBeforeStarting();
return;
}
- m_options = options;
m_loading = true;
-#if USE(QUICK_LOOK)
- if (!m_resourceRequest.isNull() && m_resourceRequest.url().protocolIs(QLPreviewProtocol())) {
- // When QuickLook is invoked to convert a document, it returns a unique URL in the
- // NSURLReponse for the main document. To make safeQLURLForDocumentURLAndResourceURL()
- // work, we need to use the QL URL not the original URL.
- const URL& documentURL = cachedResourceLoader->frame() ? cachedResourceLoader->frame()->loader().documentLoader()->response().url() : cachedResourceLoader->document()->url();
- m_resourceRequest.setURL(safeQLURLForDocumentURLAndResourceURL(documentURL, url()));
- }
-#endif
-
- if (!accept().isEmpty())
- m_resourceRequest.setHTTPAccept(accept());
-
if (isCacheValidator()) {
CachedResource* resourceToRevalidate = m_resourceToRevalidate;
ASSERT(resourceToRevalidate->canUseCacheValidator());
ASSERT(resourceToRevalidate->isLoaded());
- const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
- const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
+ const String& lastModified = resourceToRevalidate->response().httpHeaderField(HTTPHeaderName::LastModified);
+ const String& eTag = resourceToRevalidate->response().httpHeaderField(HTTPHeaderName::ETag);
if (!lastModified.isEmpty() || !eTag.isEmpty()) {
- ASSERT(cachedResourceLoader->cachePolicy(type()) != CachePolicyReload);
- if (cachedResourceLoader->cachePolicy(type()) == CachePolicyRevalidate)
- m_resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
+ ASSERT(cachedResourceLoader.cachePolicy(type()) != CachePolicyReload);
+ if (cachedResourceLoader.cachePolicy(type()) == CachePolicyRevalidate)
+ m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
if (!lastModified.isEmpty())
- m_resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
+ m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
if (!eTag.isEmpty())
- m_resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
+ m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
}
}
#if ENABLE(LINK_PREFETCH)
if (type() == CachedResource::LinkPrefetch || type() == CachedResource::LinkSubresource)
- m_resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
+ m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Purpose, "prefetch");
#endif
m_resourceRequest.setPriority(loadPriority());
- if (type() != MainResource)
- addAdditionalRequestHeaders(cachedResourceLoader);
+ // Navigation algorithm is setting up the request before sending it to CachedResourceLoader?CachedResource.
+ // So no need for extra fields for MainResource.
+ if (type() != CachedResource::MainResource)
+ frameLoader.addExtraFieldsToSubresourceRequest(m_resourceRequest);
+
// FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers.
// We should look into removing the expectation of that knowledge from the platform network stacks.
@@ -313,8 +255,9 @@ void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const Reso
m_fragmentIdentifierForRequest = String();
}
- m_loader = platformStrategies()->loaderStrategy()->resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->frame(), this, request, request.priority(), options);
+ m_loader = platformStrategies()->loaderStrategy()->loadResource(frame, *this, request, m_options);
if (!m_loader) {
+ RELEASE_LOG_IF_ALLOWED("load: Unable to create SubresourceLoader (frame = %p)", &frame);
failBeforeStarting();
return;
}
@@ -322,27 +265,55 @@ void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const Reso
m_status = Pending;
}
+void CachedResource::loadFrom(const CachedResource& resource)
+{
+ ASSERT(url() == resource.url());
+ ASSERT(type() == resource.type());
+ ASSERT(resource.status() == Status::Cached);
+
+ if (isCrossOrigin() && m_options.mode == FetchOptions::Mode::Cors) {
+ ASSERT(m_origin);
+ String errorMessage;
+ if (!WebCore::passesAccessControlCheck(resource.response(), m_options.allowCredentials, *m_origin, errorMessage)) {
+ setResourceError(ResourceError(String(), 0, url(), errorMessage, ResourceError::Type::AccessControl));
+ return;
+ }
+ }
+
+ setBodyDataFrom(resource);
+ setStatus(Status::Cached);
+ setLoading(false);
+}
+
+void CachedResource::setBodyDataFrom(const CachedResource& resource)
+{
+ m_data = resource.m_data;
+ m_response = resource.m_response;
+ setDecodedSize(resource.decodedSize());
+ setEncodedSize(resource.encodedSize());
+}
+
void CachedResource::checkNotify()
{
- if (isLoading())
+ if (isLoading() || stillNeedsLoad())
return;
- CachedResourceClientWalker<CachedResourceClient> w(m_clients);
- while (CachedResourceClient* c = w.next())
- c->notifyFinished(this);
+ CachedResourceClientWalker<CachedResourceClient> walker(m_clients);
+ while (CachedResourceClient* client = walker.next())
+ client->notifyFinished(*this);
}
-void CachedResource::addDataBuffer(ResourceBuffer*)
+void CachedResource::addDataBuffer(SharedBuffer&)
{
- ASSERT(m_options.dataBufferingPolicy == BufferData);
+ ASSERT(dataBufferingPolicy() == BufferData);
}
void CachedResource::addData(const char*, unsigned)
{
- ASSERT(m_options.dataBufferingPolicy == DoNotBufferData);
+ ASSERT(dataBufferingPolicy() == DoNotBufferData);
}
-void CachedResource::finishLoading(ResourceBuffer*)
+void CachedResource::finishLoading(SharedBuffer*)
{
setLoading(false);
checkNotify();
@@ -352,7 +323,7 @@ void CachedResource::error(CachedResource::Status status)
{
setStatus(status);
ASSERT(errorOccurred());
- m_data.clear();
+ m_data = nullptr;
setLoading(false);
checkNotify();
@@ -360,7 +331,7 @@ void CachedResource::error(CachedResource::Status status)
void CachedResource::cancelLoad()
{
- if (!isLoading())
+ if (!isLoading() && !stillNeedsLoad())
return;
setStatus(LoadError);
@@ -374,10 +345,30 @@ void CachedResource::finish()
m_status = Cached;
}
-bool CachedResource::passesAccessControlCheck(SecurityOrigin* securityOrigin)
+void CachedResource::setCrossOrigin()
{
- String errorDescription;
- return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription);
+ ASSERT(m_options.mode != FetchOptions::Mode::SameOrigin);
+ m_responseTainting = (m_options.mode == FetchOptions::Mode::Cors) ? ResourceResponse::Tainting::Cors : ResourceResponse::Tainting::Opaque;
+}
+
+bool CachedResource::isCrossOrigin() const
+{
+ return m_responseTainting != ResourceResponse::Tainting::Basic;
+}
+
+bool CachedResource::isCORSSameOrigin() const
+{
+ // Following resource types do not use CORS
+ ASSERT(type() != CachedResource::Type::FontResource);
+#if ENABLE(SVG_FONTS)
+ ASSERT(type() != CachedResource::Type::SVGFontResource);
+#endif
+#if ENABLE(XSLT)
+ ASSERT(type() != CachedResource::XSLStyleSheet);
+#endif
+
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#cors-same-origin
+ return !loadFailedOrCanceled() && m_responseTainting != ResourceResponse::Tainting::Opaque;
}
bool CachedResource::isExpired() const
@@ -385,53 +376,62 @@ bool CachedResource::isExpired() const
if (m_response.isNull())
return false;
- return currentAge() > freshnessLifetime();
+ return computeCurrentAge(m_response, m_responseTimestamp) > freshnessLifetime(m_response);
}
-double CachedResource::currentAge() const
+static inline bool shouldCacheSchemeIndefinitely(StringView scheme)
{
- // RFC2616 13.2.3
- // No compensation for latency as that is not terribly important in practice
- double dateValue = m_response.date();
- double apparentAge = std::isfinite(dateValue) ? std::max(0., m_responseTimestamp - dateValue) : 0;
- double ageValue = m_response.age();
- double correctedReceivedAge = std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge;
- double residentTime = currentTime() - m_responseTimestamp;
- return correctedReceivedAge + residentTime;
+#if PLATFORM(COCOA)
+ if (equalLettersIgnoringASCIICase(scheme, "applewebdata"))
+ return true;
+#endif
+#if USE(SOUP)
+ if (equalLettersIgnoringASCIICase(scheme, "resource"))
+ return true;
+#endif
+ return equalLettersIgnoringASCIICase(scheme, "data");
}
-double CachedResource::freshnessLifetime() const
+std::chrono::microseconds CachedResource::freshnessLifetime(const ResourceResponse& response) const
{
- if (!m_response.url().protocolIsInHTTPFamily()) {
- // Don't cache non-HTTP main resources since we can't check for freshness.
- // FIXME: We should not cache subresources either, but when we tried this
- // it caused performance and flakiness issues in our test infrastructure.
- if (m_type == MainResource && !SchemeRegistry::shouldCacheResponsesFromURLSchemeIndefinitely(m_response.url().protocol()))
- return 0;
+ if (!response.url().protocolIsInHTTPFamily()) {
+ StringView protocol = response.url().protocol();
+ if (!shouldCacheSchemeIndefinitely(protocol)) {
+ // Don't cache non-HTTP main resources since we can't check for freshness.
+ // FIXME: We should not cache subresources either, but when we tried this
+ // it caused performance and flakiness issues in our test infrastructure.
+ if (m_type == MainResource || SchemeRegistry::shouldAlwaysRevalidateURLScheme(protocol.toStringWithoutCopying()))
+ return 0us;
+ }
- return std::numeric_limits<double>::max();
+ return std::chrono::microseconds::max();
}
- // RFC2616 13.2.4
- double maxAgeValue = m_response.cacheControlMaxAge();
- if (std::isfinite(maxAgeValue))
- return maxAgeValue;
- double expiresValue = m_response.expires();
- double dateValue = m_response.date();
- double creationTime = std::isfinite(dateValue) ? dateValue : m_responseTimestamp;
- if (std::isfinite(expiresValue))
- return expiresValue - creationTime;
- double lastModifiedValue = m_response.lastModified();
- if (std::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;
+ return computeFreshnessLifetimeForHTTPFamily(response, m_responseTimestamp);
+}
+
+void CachedResource::redirectReceived(ResourceRequest&, const ResourceResponse& response)
+{
+ m_requestedFromNetworkingLayer = true;
+ if (response.isNull())
+ return;
+
+ updateRedirectChainStatus(m_redirectChainCacheStatus, response);
+}
+
+void CachedResource::setResponse(const ResourceResponse& response)
+{
+ ASSERT(m_response.type() == ResourceResponse::Type::Default);
+ m_response = response;
+ m_response.setRedirected(m_redirectChainCacheStatus.status != RedirectChainCacheStatus::NoRedirection);
+
+ m_varyingHeaderValues = collectVaryingRequestHeaders(m_resourceRequest, m_response, m_sessionID);
}
void CachedResource::responseReceived(const ResourceResponse& response)
{
setResponse(response);
- m_responseTimestamp = currentTime();
+ m_responseTimestamp = std::chrono::system_clock::now();
String encoding = response.textEncodingName();
if (!encoding.isNull())
setEncoding(encoding);
@@ -440,32 +440,30 @@ void CachedResource::responseReceived(const ResourceResponse& response)
void CachedResource::clearLoader()
{
ASSERT(m_loader);
- m_loader = 0;
+ m_identifierForLoadWithoutResourceLoader = m_loader->identifier();
+ m_loader = nullptr;
+ deleteIfPossible();
}
-void CachedResource::addClient(CachedResourceClient* client)
+void CachedResource::addClient(CachedResourceClient& client)
{
if (addClientToSet(client))
didAddClient(client);
}
-void CachedResource::didAddClient(CachedResourceClient* c)
+void CachedResource::didAddClient(CachedResourceClient& client)
{
if (m_decodedDataDeletionTimer.isActive())
m_decodedDataDeletionTimer.stop();
- if (m_clientsAwaitingCallback.contains(c)) {
- m_clients.add(c);
- m_clientsAwaitingCallback.remove(c);
- }
+ if (m_clientsAwaitingCallback.remove(&client))
+ m_clients.add(&client);
if (!isLoading() && !stillNeedsLoad())
- c->notifyFinished(this);
+ client.notifyFinished(*this);
}
-bool CachedResource::addClientToSet(CachedResourceClient* client)
+bool CachedResource::addClientToSet(CachedResourceClient& client)
{
- ASSERT(!isPurgeable());
-
if (m_preloadResult == PreloadNotReferenced) {
if (isLoaded())
m_preloadResult = PreloadReferencedWhileComplete;
@@ -474,78 +472,90 @@ bool CachedResource::addClientToSet(CachedResourceClient* client)
else
m_preloadResult = PreloadReferenced;
}
- if (!hasClients() && inCache())
- memoryCache()->addToLiveResourcesSize(this);
+ if (allowsCaching() && !hasClients() && inCache())
+ MemoryCache::singleton().addToLiveResourcesSize(*this);
if ((m_type == RawResource || m_type == MainResource) && !m_response.isNull() && !m_proxyResource) {
// Certain resources (especially XHRs and main resources) do crazy things if an asynchronous load returns
// synchronously (e.g., scripts may not have set all the state they need to handle the load).
// Therefore, rather than immediately sending callbacks on a cache hit like other CachedResources,
// we schedule the callbacks and ensure we never finish synchronously.
- ASSERT(!m_clientsAwaitingCallback.contains(client));
- m_clientsAwaitingCallback.add(client, CachedResourceCallback::schedule(this, client));
+ ASSERT(!m_clientsAwaitingCallback.contains(&client));
+ m_clientsAwaitingCallback.add(&client, std::make_unique<Callback>(*this, client));
return false;
}
- m_clients.add(client);
+ m_clients.add(&client);
return true;
}
-void CachedResource::removeClient(CachedResourceClient* client)
+void CachedResource::removeClient(CachedResourceClient& client)
{
- OwnPtr<CachedResourceCallback> callback = m_clientsAwaitingCallback.take(client);
+ auto callback = m_clientsAwaitingCallback.take(&client);
if (callback) {
- ASSERT(!m_clients.contains(client));
+ ASSERT(!m_clients.contains(&client));
callback->cancel();
- callback.clear();
+ callback = nullptr;
} else {
- ASSERT(m_clients.contains(client));
- m_clients.remove(client);
+ ASSERT(m_clients.contains(&client));
+ m_clients.remove(&client);
didRemoveClient(client);
}
- bool deleted = deleteIfPossible();
- if (!deleted && !hasClients()) {
- if (inCache()) {
- memoryCache()->removeFromLiveResourcesSize(this);
- memoryCache()->removeFromLiveDecodedResourcesList(this);
- }
- if (!m_switchingClientsToRevalidatedResource)
- allClientsRemoved();
- destroyDecodedDataIfNeeded();
- if (response().cacheControlContainsNoStore()) {
- // RFC2616 14.9.2:
- // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
- // "... History buffers MAY store such responses as part of their normal operation."
- // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
- if (url().protocolIs("https"))
- memoryCache()->remove(this);
- } else
- memoryCache()->prune();
+ if (deleteIfPossible()) {
+ // `this` object is dead here.
+ return;
}
- // This object may be dead here.
+
+ if (hasClients())
+ return;
+
+ auto& memoryCache = MemoryCache::singleton();
+ if (allowsCaching() && inCache()) {
+ memoryCache.removeFromLiveResourcesSize(*this);
+ memoryCache.removeFromLiveDecodedResourcesList(*this);
+ }
+ if (!m_switchingClientsToRevalidatedResource)
+ allClientsRemoved();
+ destroyDecodedDataIfNeeded();
+
+ if (!allowsCaching())
+ return;
+
+ if (response().cacheControlContainsNoStore() && url().protocolIs("https")) {
+ // RFC2616 14.9.2:
+ // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
+ // "... History buffers MAY store such responses as part of their normal operation."
+ // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
+ memoryCache.remove(*this);
+ }
+ memoryCache.pruneSoon();
}
void CachedResource::destroyDecodedDataIfNeeded()
{
if (!m_decodedSize)
return;
- if (!memoryCache()->deadDecodedDataDeletionInterval())
+ if (!MemoryCache::singleton().deadDecodedDataDeletionInterval().count())
return;
m_decodedDataDeletionTimer.restart();
}
-void CachedResource::decodedDataDeletionTimerFired(DeferrableOneShotTimer<CachedResource>&)
+void CachedResource::decodedDataDeletionTimerFired()
{
destroyDecodedData();
}
bool CachedResource::deleteIfPossible()
{
- if (canDelete() && !inCache()) {
- InspectorInstrumentation::willDestroyCachedResource(this);
- delete this;
- return true;
+ if (canDelete()) {
+ if (!inCache()) {
+ InspectorInstrumentation::willDestroyCachedResource(*this);
+ delete this;
+ return true;
+ }
+ if (m_data)
+ m_data->hintMemoryNotNeededSoon();
}
return false;
}
@@ -555,19 +565,19 @@ void CachedResource::setDecodedSize(unsigned size)
if (size == m_decodedSize)
return;
- int delta = size - m_decodedSize;
+ long long delta = static_cast<long long>(size) - m_decodedSize;
+
+ // The object must be moved to a different queue, since its size has been changed.
+ // Remove before updating m_decodedSize, so we find the resource in the correct LRU list.
+ if (allowsCaching() && inCache())
+ MemoryCache::singleton().removeFromLRUList(*this);
- // 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())
- memoryCache()->removeFromLRUList(this);
-
m_decodedSize = size;
- if (inCache()) {
+ if (allowsCaching() && inCache()) {
+ auto& memoryCache = MemoryCache::singleton();
// Now insert into the new LRU list.
- memoryCache()->insertInLRUList(this);
+ memoryCache.insertInLRUList(*this);
// Insert into or remove from the live decoded list if necessary.
// When inserting into the LiveDecodedResourcesList it is possible
@@ -576,13 +586,14 @@ void CachedResource::setDecodedSize(unsigned size)
// 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())
- memoryCache()->insertInLiveDecodedResourcesList(this);
- else if (!m_decodedSize && m_inLiveDecodedResourcesList)
- memoryCache()->removeFromLiveDecodedResourcesList(this);
+ bool inLiveDecodedResourcesList = memoryCache.inLiveDecodedResourcesList(*this);
+ if (m_decodedSize && !inLiveDecodedResourcesList && hasClients())
+ memoryCache.insertInLiveDecodedResourcesList(*this);
+ else if (!m_decodedSize && inLiveDecodedResourcesList)
+ memoryCache.removeFromLiveDecodedResourcesList(*this);
// Update the cache's size totals.
- memoryCache()->adjustSize(hasClients(), delta);
+ memoryCache.adjustSize(hasClients(), delta);
}
}
@@ -591,25 +602,19 @@ 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;
+ long long delta = static_cast<long long>(size) - m_encodedSize;
+
+ // The object must be moved to a different queue, since its size has been changed.
+ // Remove before updating m_encodedSize, so we find the resource in the correct LRU list.
+ if (allowsCaching() && inCache())
+ MemoryCache::singleton().removeFromLRUList(*this);
- // 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())
- memoryCache()->removeFromLRUList(this);
-
m_encodedSize = size;
-
- if (inCache()) {
- // Now insert into the new LRU list.
- memoryCache()->insertInLRUList(this);
-
- // Update the cache's size totals.
- memoryCache()->adjustSize(hasClients(), delta);
+
+ if (allowsCaching() && inCache()) {
+ auto& memoryCache = MemoryCache::singleton();
+ memoryCache.insertInLRUList(*this);
+ memoryCache.adjustSize(hasClients(), delta);
}
}
@@ -617,12 +622,13 @@ void CachedResource::didAccessDecodedData(double timeStamp)
{
m_lastDecodedAccessTime = timeStamp;
- if (inCache()) {
- if (m_inLiveDecodedResourcesList) {
- memoryCache()->removeFromLiveDecodedResourcesList(this);
- memoryCache()->insertInLiveDecodedResourcesList(this);
+ if (allowsCaching() && inCache()) {
+ auto& memoryCache = MemoryCache::singleton();
+ if (memoryCache.inLiveDecodedResourcesList(*this)) {
+ memoryCache.removeFromLiveDecodedResourcesList(*this);
+ memoryCache.insertInLiveDecodedResourcesList(*this);
}
- memoryCache()->prune();
+ memoryCache.pruneSoon();
}
}
@@ -633,31 +639,27 @@ void CachedResource::setResourceToRevalidate(CachedResource* resource)
ASSERT(resource != this);
ASSERT(m_handlesToRevalidate.isEmpty());
ASSERT(resource->type() == type());
+ ASSERT(!resource->m_proxyResource);
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);
+ ASSERT(m_resourceToRevalidate->m_proxyResource == this);
+
if (m_switchingClientsToRevalidatedResource)
return;
- // 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_resourceToRevalidate->m_proxyResource = nullptr;
+ m_resourceToRevalidate->deleteIfPossible();
+
m_handlesToRevalidate.clear();
- m_resourceToRevalidate = 0;
+ m_resourceToRevalidate = nullptr;
deleteIfPossible();
}
@@ -670,9 +672,7 @@ void CachedResource::switchClientsToRevalidatedResource()
LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate);
m_switchingClientsToRevalidatedResource = true;
- HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end();
- for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) {
- CachedResourceHandleBase* handle = *it;
+ for (auto& handle : m_handlesToRevalidate) {
handle->m_resource = m_resourceToRevalidate;
m_resourceToRevalidate->registerHandle(handle);
--m_handleCount;
@@ -681,52 +681,37 @@ void CachedResource::switchClientsToRevalidatedResource()
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->key;
- unsigned count = it->value;
+ for (auto& entry : m_clients) {
+ CachedResourceClient* client = entry.key;
+ unsigned count = entry.value;
while (count) {
clientsToMove.append(client);
--count;
}
}
- unsigned moveCount = clientsToMove.size();
- for (unsigned n = 0; n < moveCount; ++n)
- removeClient(clientsToMove[n]);
+ for (auto& client : clientsToMove)
+ removeClient(*client);
ASSERT(m_clients.isEmpty());
- for (unsigned n = 0; n < moveCount; ++n)
- m_resourceToRevalidate->addClientToSet(clientsToMove[n]);
- for (unsigned n = 0; n < moveCount; ++n) {
+ for (auto& client : clientsToMove)
+ m_resourceToRevalidate->addClientToSet(*client);
+ for (auto& client : clientsToMove) {
// Calling didAddClient may do anything, including trying to cancel revalidation.
// Assert that it didn't succeed.
ASSERT(m_resourceToRevalidate);
// 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]);
+ if (m_resourceToRevalidate->m_clients.contains(client))
+ m_resourceToRevalidate->didAddClient(*client);
}
m_switchingClientsToRevalidatedResource = false;
}
void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse)
{
- m_responseTimestamp = currentTime();
-
- // 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) {
- // Entity headers should not be sent by servers when generating a 304
- // response; misconfigured servers send them anyway. We shouldn't allow
- // such headers to update the original request. We'll base this on the
- // list defined by RFC2616 7.1, with a few additions for extension headers
- // we care about.
- if (!shouldUpdateHeaderAfterRevalidation(it->key))
- continue;
- m_response.setHTTPHeaderField(it->key, it->value);
- }
+ m_responseTimestamp = std::chrono::system_clock::now();
+
+ updateResponseHeadersAfterRevalidation(m_response, validatingResponse);
}
void CachedResource::registerHandle(CachedResourceHandleBase* h)
@@ -758,95 +743,50 @@ bool CachedResource::canUseCacheValidator() const
return m_response.hasCacheValidatorFields();
}
-bool CachedResource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const
+CachedResource::RevalidationDecision CachedResource::makeRevalidationDecision(CachePolicy cachePolicy) const
{
- ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify);
-
- if (cachePolicy == CachePolicyRevalidate)
- return true;
-
- if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) {
- LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this);
- return true;
- }
-
- if (cachePolicy == CachePolicyCache) {
- if (m_response.cacheControlContainsMustRevalidate() && isExpired()) {
- LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this);
- return true;
- }
- return false;
- }
-
- // CachePolicyVerify
- if (isExpired()) {
- LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this);
- return true;
- }
-
- return false;
-}
-
-bool CachedResource::isSafeToMakePurgeable() const
-{
-#if ENABLE(DISK_IMAGE_CACHE)
- // It does not make sense to have a resource in the disk image cache
- // (memory mapped on disk) and purgeable (in memory). So do not allow
- // disk image cached resources to be purgeable.
- if (isUsingDiskImageCache())
- return false;
-#endif
-
- return !hasClients() && !m_proxyResource && !m_resourceToRevalidate;
-}
-
-bool CachedResource::makePurgeable(bool purgeable)
-{
- if (purgeable) {
- ASSERT(isSafeToMakePurgeable());
-
- if (m_purgeableData) {
- ASSERT(!m_data);
- return true;
+ switch (cachePolicy) {
+ case CachePolicyHistoryBuffer:
+ return RevalidationDecision::No;
+
+ case CachePolicyReload:
+ return RevalidationDecision::YesDueToCachePolicy;
+
+ case CachePolicyRevalidate:
+ if (m_response.cacheControlContainsImmutable() && m_response.url().protocolIs("https")) {
+ if (isExpired())
+ return RevalidationDecision::YesDueToExpired;
+ return RevalidationDecision::No;
}
- 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;
+ return RevalidationDecision::YesDueToCachePolicy;
- m_data->createPurgeableBuffer();
- if (!m_data->hasPurgeableBuffer())
- return false;
+ case CachePolicyVerify:
+ if (m_response.cacheControlContainsNoCache())
+ return RevalidationDecision::YesDueToNoCache;
+ // FIXME: Cache-Control:no-store should prevent storing, not reuse.
+ if (m_response.cacheControlContainsNoStore())
+ return RevalidationDecision::YesDueToNoStore;
- m_purgeableData = m_data->releasePurgeableBuffer();
- m_purgeableData->setPurgePriority(purgePriority());
- m_purgeableData->makePurgeable(true);
- m_data.clear();
- return true;
- }
-
- if (!m_purgeableData)
- return true;
- ASSERT(!m_data);
- ASSERT(!hasClients());
+ if (isExpired())
+ return RevalidationDecision::YesDueToExpired;
- if (!m_purgeableData->makePurgeable(false))
- return false;
-
- m_data = ResourceBuffer::adoptSharedBuffer(SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release()));
- return true;
+ return RevalidationDecision::No;
+ };
+ ASSERT_NOT_REACHED();
+ return RevalidationDecision::No;
}
-bool CachedResource::isPurgeable() const
+bool CachedResource::redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot reuseExpiredRedirection) const
{
- return m_purgeableData && m_purgeableData->isPurgeable();
+ return WebCore::redirectChainAllowsReuse(m_redirectChainCacheStatus, reuseExpiredRedirection);
}
-bool CachedResource::wasPurged() const
+bool CachedResource::varyHeaderValuesMatch(const ResourceRequest& request)
{
- return m_purgeableData && m_purgeableData->wasPurged();
+ if (m_varyingHeaderValues.isEmpty())
+ return true;
+
+ return verifyVaryingRequestHeaders(m_varyingHeaderValues, request, m_sessionID);
}
unsigned CachedResource::overheadSize() const
@@ -855,59 +795,65 @@ unsigned CachedResource::overheadSize() const
return sizeof(CachedResource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2;
}
-void CachedResource::setLoadPriority(ResourceLoadPriority loadPriority)
+bool CachedResource::areAllClientsXMLHttpRequests() const
{
- if (loadPriority == ResourceLoadPriorityUnresolved)
- loadPriority = defaultPriorityForResourceType(type());
- if (loadPriority == m_loadPriority)
- return;
- m_loadPriority = loadPriority;
- if (m_loader)
- m_loader->didChangePriority(loadPriority);
+ if (type() != RawResource)
+ return false;
+
+ for (auto& client : m_clients) {
+ if (!client.key->isXMLHttpRequest())
+ return false;
+ }
+ return true;
}
-CachedResource::CachedResourceCallback::CachedResourceCallback(CachedResource* resource, CachedResourceClient* client)
- : m_resource(resource)
- , m_client(client)
- , m_callbackTimer(this, &CachedResourceCallback::timerFired)
+void CachedResource::setLoadPriority(const std::optional<ResourceLoadPriority>& loadPriority)
{
- m_callbackTimer.startOneShot(0);
+ if (loadPriority)
+ m_loadPriority = loadPriority.value();
+ else
+ m_loadPriority = defaultPriorityForResourceType(type());
}
-void CachedResource::CachedResourceCallback::cancel()
+inline CachedResource::Callback::Callback(CachedResource& resource, CachedResourceClient& client)
+ : m_resource(resource)
+ , m_client(client)
+ , m_timer(*this, &Callback::timerFired)
{
- if (m_callbackTimer.isActive())
- m_callbackTimer.stop();
+ m_timer.startOneShot(0);
}
-void CachedResource::CachedResourceCallback::timerFired(Timer<CachedResourceCallback>&)
+inline void CachedResource::Callback::cancel()
{
- m_resource->didAddClient(m_client);
+ if (m_timer.isActive())
+ m_timer.stop();
}
-#if ENABLE(DISK_IMAGE_CACHE)
-bool CachedResource::isUsingDiskImageCache() const
+void CachedResource::Callback::timerFired()
{
- return m_data && m_data->isUsingDiskImageCache();
+ m_resource.didAddClient(m_client);
}
-#endif
-#if PLATFORM(MAC)
-void CachedResource::tryReplaceEncodedData(PassRefPtr<SharedBuffer> newBuffer)
+#if USE(FOUNDATION) || USE(SOUP)
+
+void CachedResource::tryReplaceEncodedData(SharedBuffer& newBuffer)
{
if (!m_data)
return;
if (!mayTryReplaceEncodedData())
return;
-
- // Because the disk cache is asynchronous and racey with regards to the data we might be asked to replace,
- // we need to verify that the new buffer has the same contents as our old buffer.
- if (m_data->size() != newBuffer->size() || memcmp(m_data->data(), newBuffer->data(), m_data->size()))
+
+ // We have to do the memcmp because we can't tell if the replacement file backed data is for the
+ // same resource or if we made a second request with the same URL which gave us a different
+ // resource. We have seen this happen for cached POST resources.
+ if (m_data->size() != newBuffer.size() || memcmp(m_data->data(), newBuffer.data(), m_data->size()))
return;
- m_data->tryReplaceSharedBufferContents(newBuffer.get());
+ if (m_data->tryReplaceContentsWithPlatformBuffer(newBuffer))
+ didReplaceSharedBufferContents();
}
+
#endif
}
diff --git a/Source/WebCore/loader/cache/CachedResource.h b/Source/WebCore/loader/cache/CachedResource.h
index 8de69cdbf..6f4e0e1b2 100644
--- a/Source/WebCore/loader/cache/CachedResource.h
+++ b/Source/WebCore/loader/cache/CachedResource.h
@@ -20,37 +20,37 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef CachedResource_h
-#define CachedResource_h
+#pragma once
#include "CachePolicy.h"
+#include "CacheValidation.h"
#include "FrameLoaderTypes.h"
-#include "PurgePriority.h"
#include "ResourceError.h"
#include "ResourceLoadPriority.h"
#include "ResourceLoaderOptions.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
+#include "SessionID.h"
#include "Timer.h"
#include <time.h>
#include <wtf/HashCountedSet.h>
#include <wtf/HashSet.h>
-#include <wtf/OwnPtr.h>
+#include <wtf/TypeCasts.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-class MemoryCache;
class CachedResourceClient;
class CachedResourceHandleBase;
class CachedResourceLoader;
-class InspectorResource;
-class PurgeableBuffer;
-class ResourceBuffer;
+class CachedResourceRequest;
+class LoadTiming;
+class MemoryCache;
class SecurityOrigin;
class SharedBuffer;
class SubresourceLoader;
+class TextResourceDecoder;
// 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.
@@ -58,8 +58,7 @@ class SubresourceLoader;
class CachedResource {
WTF_MAKE_NONCOPYABLE(CachedResource); WTF_MAKE_FAST_ALLOCATED;
friend class MemoryCache;
- friend class InspectorResource;
-
+
public:
enum Type {
MainResource,
@@ -67,10 +66,12 @@ public:
CSSStyleSheet,
Script,
FontResource,
- RawResource
-#if ENABLE(SVG)
- , SVGDocumentResource
+#if ENABLE(SVG_FONTS)
+ SVGFontResource,
#endif
+ MediaResource,
+ RawResource,
+ SVGDocumentResource
#if ENABLE(XSLT)
, XSLStyleSheet
#endif
@@ -91,16 +92,17 @@ public:
DecodeError
};
- CachedResource(const ResourceRequest&, Type);
+ CachedResource(CachedResourceRequest&&, Type, SessionID);
virtual ~CachedResource();
- virtual void load(CachedResourceLoader*, const ResourceLoaderOptions&);
+ virtual void load(CachedResourceLoader&);
virtual void setEncoding(const String&) { }
virtual String encoding() const { return String(); }
- virtual void addDataBuffer(ResourceBuffer*);
+ virtual const TextResourceDecoder* textResourceDecoder() const { return nullptr; }
+ virtual void addDataBuffer(SharedBuffer&);
virtual void addData(const char* data, unsigned length);
- virtual void finishLoading(ResourceBuffer*);
+ virtual void finishLoading(SharedBuffer*);
virtual void error(CachedResource::Status);
void setResourceError(const ResourceError& error) { m_error = error; }
@@ -108,20 +110,20 @@ public:
virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return false; }
+ const ResourceRequest& resourceRequest() const { return m_resourceRequest; }
ResourceRequest& resourceRequest() { return m_resourceRequest; }
const URL& url() const { return m_resourceRequest.url();}
-#if ENABLE(CACHE_PARTITIONING)
const String& cachePartition() const { return m_resourceRequest.cachePartition(); }
-#endif
- Type type() const { return static_cast<Type>(m_type); }
-
+ SessionID sessionID() const { return m_sessionID; }
+ Type type() const { return m_type; }
+
ResourceLoadPriority loadPriority() const { return m_loadPriority; }
- void setLoadPriority(ResourceLoadPriority);
+ void setLoadPriority(const std::optional<ResourceLoadPriority>&);
- void addClient(CachedResourceClient*);
- void removeClient(CachedResourceClient*);
+ WEBCORE_EXPORT void addClient(CachedResourceClient&);
+ WEBCORE_EXPORT void removeClient(CachedResourceClient&);
bool hasClients() const { return !m_clients.isEmpty() || !m_clientsAwaitingCallback.isEmpty(); }
- bool hasClient(CachedResourceClient* client) { return m_clients.contains(client) || m_clientsAwaitingCallback.contains(client); }
+ bool hasClient(CachedResourceClient& client) { return m_clients.contains(&client) || m_clientsAwaitingCallback.contains(&client); }
bool deleteIfPossible();
enum PreloadResult {
@@ -132,8 +134,8 @@ public:
};
PreloadResult preloadResult() const { return static_cast<PreloadResult>(m_preloadResult); }
- virtual void didAddClient(CachedResourceClient*);
- virtual void didRemoveClient(CachedResourceClient*) { }
+ virtual void didAddClient(CachedResourceClient&);
+ virtual void didRemoveClient(CachedResourceClient&) { }
virtual void allClientsRemoved() { }
void destroyDecodedDataIfNeeded();
@@ -146,7 +148,7 @@ public:
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; }
@@ -155,10 +157,15 @@ public:
SubresourceLoader* loader() { return m_loader.get(); }
- virtual bool isImage() const { return false; }
+ bool areAllClientsXMLHttpRequests() const;
+
+ bool isImage() const { return type() == ImageResource; }
+ // FIXME: CachedRawResource could be a main resource, an audio/video resource, or a raw XHR/icon resource.
+ bool isMainOrMediaOrRawResource() const { return type() == MainResource || type() == MediaResource || type() == RawResource; }
bool ignoreForRequestCount() const
{
- return type() == MainResource
+ return m_resourceRequest.ignoreForRequestCount()
+ || type() == MainResource
#if ENABLE(LINK_PREFETCH)
|| type() == LinkPrefetch
|| type() == LinkSubresource
@@ -169,205 +176,220 @@ public:
unsigned accessCount() const { return m_accessCount; }
void increaseAccessCount() { m_accessCount++; }
- // Computes the status of an object after loading.
+ // Computes the status of an object after loading.
// Updates the expire date on the cache entry file
void finish();
- bool passesAccessControlCheck(SecurityOrigin*);
-
// 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; }
-
- bool inLiveDecodedResourcesList() { return m_inLiveDecodedResourcesList; }
-
+
void clearLoader();
- ResourceBuffer* resourceBuffer() const { ASSERT(!m_purgeableData); return m_data.get(); }
+ SharedBuffer* resourceBuffer() const { return m_data.get(); }
- virtual void willSendRequest(ResourceRequest&, const ResourceResponse&) { m_requestedFromNetworkingLayer = true; }
+ virtual void redirectReceived(ResourceRequest&, const ResourceResponse&);
virtual void responseReceived(const ResourceResponse&);
- void setResponse(const ResourceResponse& response) { m_response = response; }
+ virtual bool shouldCacheResponse(const ResourceResponse&) { return true; }
+ void setResponse(const ResourceResponse&);
const ResourceResponse& response() const { return m_response; }
+ void setCrossOrigin();
+ bool isCrossOrigin() const;
+ bool isCORSSameOrigin() const;
+ ResourceResponse::Tainting responseTainting() const { return m_responseTainting; }
+
+ void loadFrom(const CachedResource&);
+
+ SecurityOrigin* origin() const { return m_origin.get(); }
+ AtomicString initiatorName() const { return m_initiatorName; }
+
bool canDelete() const { return !hasClients() && !m_loader && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_proxyResource; }
bool hasOneHandle() const { return m_handleCount == 1; }
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; }
-
void cancelLoad();
bool wasCanceled() const { return m_error.isCancellation(); }
bool errorOccurred() const { return m_status == LoadError || m_status == DecodeError; }
- bool loadFailedOrCanceled() { return !m_error.isNull(); }
+ bool loadFailedOrCanceled() const { return !m_error.isNull(); }
bool shouldSendResourceLoadCallbacks() const { return m_options.sendLoadCallbacks == SendCallbacks; }
DataBufferingPolicy dataBufferingPolicy() const { return m_options.dataBufferingPolicy; }
-
+
+ bool allowsCaching() const { return m_options.cachingPolicy == CachingPolicy::AllowCaching; }
+ const FetchOptions& options() const { return m_options; }
+
virtual void destroyDecodedData() { }
void setOwningCachedResourceLoader(CachedResourceLoader* cachedResourceLoader) { m_owningCachedResourceLoader = cachedResourceLoader; }
-
+
bool isPreloaded() const { return m_preloadCount; }
void increasePreloadCount() { ++m_preloadCount; }
void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; }
-
- void registerHandle(CachedResourceHandleBase* h);
- void unregisterHandle(CachedResourceHandleBase* h);
-
+ bool isLinkPreload() { return m_isLinkPreload; }
+ void setLinkPreload() { m_isLinkPreload = true; }
+
+ void registerHandle(CachedResourceHandleBase*);
+ WEBCORE_EXPORT void unregisterHandle(CachedResourceHandleBase*);
+
bool canUseCacheValidator() const;
- virtual bool mustRevalidateDueToCacheHeaders(CachePolicy) const;
+ enum class RevalidationDecision { No, YesDueToCachePolicy, YesDueToNoStore, YesDueToNoCache, YesDueToExpired };
+ virtual RevalidationDecision makeRevalidationDecision(CachePolicy) const;
+ bool redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot) const;
+ bool hasRedirections() const { return m_redirectChainCacheStatus.status != RedirectChainCacheStatus::Status::NoRedirection; }
+
+ bool varyHeaderValuesMatch(const ResourceRequest&);
bool isCacheValidator() const { return m_resourceToRevalidate; }
CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; }
-
- bool isPurgeable() const;
- bool wasPurged() const;
-
- // This is used by the archive machinery to get at a purged resource without
- // triggering a load. We should make it protected again if we can find a
- // better way to handle the archive case.
- bool makePurgeable(bool purgeable);
-
+
// HTTP revalidation support methods for CachedResourceLoader.
void setResourceToRevalidate(CachedResource*);
virtual void switchClientsToRevalidatedResource();
void clearResourceToRevalidate();
void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse);
-
+ bool validationInProgress() const { return m_proxyResource; }
+ bool validationCompleting() const { return m_proxyResource && m_proxyResource->m_switchingClientsToRevalidatedResource; }
+
virtual void didSendData(unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { }
+ virtual void didRetrieveDerivedDataFromCache(const String& /* type */, SharedBuffer&) { }
+
void setLoadFinishTime(double finishTime) { m_loadFinishTime = finishTime; }
double loadFinishTime() const { return m_loadFinishTime; }
-#if ENABLE(DISK_IMAGE_CACHE)
- bool isUsingDiskImageCache() const;
- virtual bool canUseDiskImageCache() const { return false; }
- virtual void useDiskImageCache() { ASSERT(canUseDiskImageCache()); }
-#endif
-
- virtual bool canReuse(const ResourceRequest&) const { return true; }
-
-#if PLATFORM(MAC)
- void tryReplaceEncodedData(PassRefPtr<SharedBuffer>);
+#if USE(FOUNDATION) || USE(SOUP)
+ WEBCORE_EXPORT void tryReplaceEncodedData(SharedBuffer&);
#endif
#if USE(SOUP)
- virtual char* getOrCreateReadBuffer(size_t /* requestedSize */, size_t& /* actualSize */) { return 0; }
+ virtual char* getOrCreateReadBuffer(size_t /* requestedSize */, size_t& /* actualSize */) { return nullptr; }
#endif
+ unsigned long identifierForLoadWithoutResourceLoader() const { return m_identifierForLoadWithoutResourceLoader; }
+ static ResourceLoadPriority defaultPriorityForResourceType(Type);
+
protected:
- virtual void checkNotify();
+ // CachedResource constructor that may be used when the CachedResource can already be filled with response data.
+ CachedResource(const URL&, Type, SessionID);
void setEncodedSize(unsigned);
void setDecodedSize(unsigned);
void didAccessDecodedData(double timeStamp);
- bool isSafeToMakePurgeable() const;
-
- HashCountedSet<CachedResourceClient*> m_clients;
+ virtual void didReplaceSharedBufferContents() { }
- class CachedResourceCallback {
- public:
- static PassOwnPtr<CachedResourceCallback> schedule(CachedResource* resource, CachedResourceClient* client) { return adoptPtr(new CachedResourceCallback(resource, client)); }
- void cancel();
- private:
- CachedResourceCallback(CachedResource*, CachedResourceClient*);
- void timerFired(Timer<CachedResourceCallback>&);
-
- CachedResource* m_resource;
- CachedResourceClient* m_client;
- Timer<CachedResourceCallback> m_callbackTimer;
- };
- HashMap<CachedResourceClient*, OwnPtr<CachedResourceCallback>> m_clientsAwaitingCallback;
+ virtual void setBodyDataFrom(const CachedResource&);
+ // FIXME: Make the rest of these data members private and use functions in derived classes instead.
+ HashCountedSet<CachedResourceClient*> m_clients;
ResourceRequest m_resourceRequest;
- String m_accept;
RefPtr<SubresourceLoader> m_loader;
ResourceLoaderOptions m_options;
- ResourceLoadPriority m_loadPriority;
-
ResourceResponse m_response;
- double m_responseTimestamp;
-
- RefPtr<ResourceBuffer> m_data;
- OwnPtr<PurgeableBuffer> m_purgeableData;
- DeferrableOneShotTimer<CachedResource> m_decodedDataDeletionTimer;
+ ResourceResponse::Tainting m_responseTainting { ResourceResponse::Tainting::Basic };
+ RefPtr<SharedBuffer> m_data;
+ DeferrableOneShotTimer m_decodedDataDeletionTimer;
private:
- bool addClientToSet(CachedResourceClient*);
+ class Callback;
+
+ bool addClientToSet(CachedResourceClient&);
- void decodedDataDeletionTimerFired(DeferrableOneShotTimer<CachedResource>&);
+ void decodedDataDeletionTimerFired();
- virtual PurgePriority purgePriority() const { return PurgeDefault; }
+ virtual void checkNotify();
virtual bool mayTryReplaceEncodedData() const { return false; }
- double currentAge() const;
- double freshnessLifetime() const;
+ std::chrono::microseconds freshnessLifetime(const ResourceResponse&) const;
- void addAdditionalRequestHeaders(CachedResourceLoader*);
+ void addAdditionalRequestHeaders(CachedResourceLoader&);
void failBeforeStarting();
+ HashMap<CachedResourceClient*, std::unique_ptr<Callback>> m_clientsAwaitingCallback;
+ SessionID m_sessionID;
+ ResourceLoadPriority m_loadPriority;
+ std::chrono::system_clock::time_point m_responseTimestamp;
+
String m_fragmentIdentifierForRequest;
ResourceError m_error;
+ RefPtr<SecurityOrigin> m_origin;
+ AtomicString m_initiatorName;
- double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache
- double m_loadFinishTime;
+ double m_lastDecodedAccessTime { 0 }; // Used as a "thrash guard" in the cache
+ double m_loadFinishTime { 0 };
- unsigned m_encodedSize;
- unsigned m_decodedSize;
- unsigned m_accessCount;
- unsigned m_handleCount;
- unsigned m_preloadCount;
+ unsigned m_encodedSize { 0 };
+ unsigned m_decodedSize { 0 };
+ unsigned m_accessCount { 0 };
+ unsigned m_handleCount { 0 };
+ unsigned m_preloadCount { 0 };
- unsigned m_preloadResult : 2; // PreloadResult
+ PreloadResult m_preloadResult { PreloadNotReferenced };
- bool m_inLiveDecodedResourcesList : 1;
- bool m_requestedFromNetworkingLayer : 1;
+ bool m_requestedFromNetworkingLayer { false };
- bool m_inCache : 1;
- bool m_loading : 1;
+ bool m_inCache { false };
+ bool m_loading { false };
+ bool m_isLinkPreload { false };
- bool m_switchingClientsToRevalidatedResource : 1;
+ bool m_switchingClientsToRevalidatedResource { false };
- unsigned m_type : 4; // Type
- unsigned m_status : 3; // Status
+ Type m_type; // Type
+ unsigned m_status { Pending }; // Status
#ifndef NDEBUG
- bool m_deleted;
- unsigned m_lruIndex;
+ bool m_deleted { false };
+ unsigned m_lruIndex { 0 };
#endif
- CachedResource* m_nextInAllResourcesList;
- CachedResource* m_prevInAllResourcesList;
-
- CachedResource* m_nextInLiveResourcesList;
- CachedResource* m_prevInLiveResourcesList;
+ CachedResourceLoader* m_owningCachedResourceLoader { nullptr }; // only non-null for resources that are not in the cache
- CachedResourceLoader* m_owningCachedResourceLoader; // only non-0 for resources that are not in the cache
-
// If this field is non-null we are using the resource as a proxy for checking whether an existing resource is still up to date
// using HTTP If-Modified-Since/If-None-Match headers. If the response is 304 all clients of this resource are moved
// to to be clients of m_resourceToRevalidate and the resource is deleted. If not, the field is zeroed and this
// resources becomes normal resource load.
- CachedResource* m_resourceToRevalidate;
+ CachedResource* m_resourceToRevalidate { nullptr };
// 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;
+ CachedResource* m_proxyResource { nullptr };
// These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response.
HashSet<CachedResourceHandleBase*> m_handlesToRevalidate;
-};
-}
+ RedirectChainCacheStatus m_redirectChainCacheStatus;
+
+ Vector<std::pair<String, String>> m_varyingHeaderValues;
+
+ unsigned long m_identifierForLoadWithoutResourceLoader { 0 };
+};
+class CachedResource::Callback {
+#if !COMPILER(MSVC)
+ WTF_MAKE_FAST_ALLOCATED;
#endif
+public:
+ Callback(CachedResource&, CachedResourceClient&);
+
+ void cancel();
+
+private:
+ void timerFired();
+
+ CachedResource& m_resource;
+ CachedResourceClient& m_client;
+ Timer m_timer;
+};
+
+} // namespace WebCore
+
+#define SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(ToClassName, CachedResourceType) \
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \
+ static bool isType(const WebCore::CachedResource& resource) { return resource.type() == WebCore::CachedResourceType; } \
+SPECIALIZE_TYPE_TRAITS_END()
diff --git a/Source/WebCore/loader/cache/CachedResourceClient.h b/Source/WebCore/loader/cache/CachedResourceClient.h
index 402ff91a1..17c361d71 100644
--- a/Source/WebCore/loader/cache/CachedResourceClient.h
+++ b/Source/WebCore/loader/cache/CachedResourceClient.h
@@ -21,39 +21,33 @@
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/FastMalloc.h>
+#pragma once
namespace WebCore {
+
class CachedResource;
class CachedResourceClient {
- WTF_MAKE_FAST_ALLOCATED;
public:
enum CachedResourceClientType {
BaseResourceType,
ImageType,
FontType,
StyleSheetType,
-#if ENABLE(SVG)
SVGDocumentType,
-#endif
RawResourceType
};
virtual ~CachedResourceClient() { }
- virtual void notifyFinished(CachedResource*) { }
- virtual void deprecatedDidReceiveCachedResource(CachedResource*) { }
-
+ virtual void notifyFinished(CachedResource&) { }
+ virtual void deprecatedDidReceiveCachedResource(CachedResource&) { }
+ virtual bool isXMLHttpRequest() const { return false; }
+
static CachedResourceClientType expectedType() { return BaseResourceType; }
virtual CachedResourceClientType resourceClientType() const { return expectedType(); }
protected:
CachedResourceClient() { }
};
-}
-#endif
+}
diff --git a/Source/WebCore/loader/cache/CachedResourceClientWalker.h b/Source/WebCore/loader/cache/CachedResourceClientWalker.h
index de352f037..39ee0234f 100644
--- a/Source/WebCore/loader/cache/CachedResourceClientWalker.h
+++ b/Source/WebCore/loader/cache/CachedResourceClientWalker.h
@@ -22,8 +22,7 @@
pages from the web. It has a memory cache for these objects.
*/
-#ifndef CachedResourceClientWalker_h
-#define CachedResourceClientWalker_h
+#pragma once
#include "CachedResourceClient.h"
#include <wtf/HashCountedSet.h>
@@ -56,7 +55,7 @@ public:
}
}
- return 0;
+ return nullptr;
}
private:
const HashCountedSet<CachedResourceClient*>& m_clientSet;
@@ -64,6 +63,4 @@ private:
size_t m_index;
};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedResourceHandle.cpp b/Source/WebCore/loader/cache/CachedResourceHandle.cpp
index 07c84ed70..097276e93 100644
--- a/Source/WebCore/loader/cache/CachedResourceHandle.cpp
+++ b/Source/WebCore/loader/cache/CachedResourceHandle.cpp
@@ -31,7 +31,7 @@
namespace WebCore {
CachedResourceHandleBase::CachedResourceHandleBase()
- : m_resource(0)
+ : m_resource(nullptr)
{
}
diff --git a/Source/WebCore/loader/cache/CachedResourceHandle.h b/Source/WebCore/loader/cache/CachedResourceHandle.h
index 5b6b42540..3dbbda6c1 100644
--- a/Source/WebCore/loader/cache/CachedResourceHandle.h
+++ b/Source/WebCore/loader/cache/CachedResourceHandle.h
@@ -23,9 +23,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CachedResourceHandle_h
-#define CachedResourceHandle_h
+#pragma once
+#include "PlatformExportMacros.h"
#include <wtf/Forward.h>
namespace WebCore {
@@ -49,7 +49,7 @@ protected:
CachedResourceHandleBase(CachedResource*);
CachedResourceHandleBase(const CachedResourceHandleBase&);
- void setResource(CachedResource*);
+ WEBCORE_EXPORT void setResource(CachedResource*);
private:
CachedResourceHandleBase& operator=(const CachedResourceHandleBase&) { return *this; }
@@ -68,6 +68,7 @@ public:
R* get() const { return reinterpret_cast<R*>(CachedResourceHandleBase::get()); }
R* operator->() const { return get(); }
+ R& operator*() const { ASSERT(get()); return *get(); }
CachedResourceHandle& operator=(R* res) { setResource(res); return *this; }
CachedResourceHandle& operator=(const CachedResourceHandle& o) { setResource(o.get()); return *this; }
@@ -95,5 +96,3 @@ template <class R, class RR> bool operator!=(const RR* res, const CachedResource
}
} // namespace WebCore
-
-#endif // CachedResourceHandle
diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.cpp b/Source/WebCore/loader/cache/CachedResourceLoader.cpp
index bd38fe823..baf827eb7 100644
--- a/Source/WebCore/loader/cache/CachedResourceLoader.cpp
+++ b/Source/WebCore/loader/cache/CachedResourceLoader.cpp
@@ -2,7 +2,7 @@
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, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ Copyright (C) 2004-2016 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
@@ -28,18 +28,22 @@
#include "CachedResourceLoader.h"
#include "CachedCSSStyleSheet.h"
-#include "CachedSVGDocument.h"
#include "CachedFont.h"
#include "CachedImage.h"
#include "CachedRawResource.h"
#include "CachedResourceRequest.h"
+#include "CachedSVGDocument.h"
+#include "CachedSVGFont.h"
#include "CachedScript.h"
#include "CachedXSLStyleSheet.h"
#include "Chrome.h"
#include "ChromeClient.h"
-#include "Console.h"
+#include "ContentExtensionError.h"
+#include "ContentExtensionRule.h"
#include "ContentSecurityPolicy.h"
#include "DOMWindow.h"
+#include "DiagnosticLoggingClient.h"
+#include "DiagnosticLoggingKeys.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "Frame.h"
@@ -48,16 +52,26 @@
#include "HTMLElement.h"
#include "HTMLFrameOwnerElement.h"
#include "LoaderStrategy.h"
+#include "LocalizedStrings.h"
#include "Logging.h"
+#include "MainFrame.h"
#include "MemoryCache.h"
#include "Page.h"
#include "PingLoader.h"
#include "PlatformStrategies.h"
#include "RenderElement.h"
-#include "ResourceLoadScheduler.h"
+#include "ResourceLoadInfo.h"
+#include "ResourceTiming.h"
+#include "RuntimeEnabledFeatures.h"
#include "ScriptController.h"
#include "SecurityOrigin.h"
+#include "SecurityPolicy.h"
+#include "SessionID.h"
#include "Settings.h"
+#include "StyleSheetContents.h"
+#include "SubresourceLoader.h"
+#include "UserContentController.h"
+#include "UserStyleSheet.h"
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
@@ -65,56 +79,57 @@
#include "CachedTextTrack.h"
#endif
-#if ENABLE(RESOURCE_TIMING)
-#include "Performance.h"
-#endif
-
#define PRELOAD_DEBUG 0
+#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - CachedResourceLoader::" fmt, this, ##__VA_ARGS__)
+
namespace WebCore {
-static CachedResource* createResource(CachedResource::Type type, ResourceRequest& request, const String& charset)
+static CachedResource* createResource(CachedResource::Type type, CachedResourceRequest&& request, SessionID sessionID)
{
switch (type) {
case CachedResource::ImageResource:
- return new CachedImage(request);
+ return new CachedImage(WTFMove(request), sessionID);
case CachedResource::CSSStyleSheet:
- return new CachedCSSStyleSheet(request, charset);
+ return new CachedCSSStyleSheet(WTFMove(request), sessionID);
case CachedResource::Script:
- return new CachedScript(request, charset);
-#if ENABLE(SVG)
+ return new CachedScript(WTFMove(request), sessionID);
case CachedResource::SVGDocumentResource:
- return new CachedSVGDocument(request);
+ return new CachedSVGDocument(WTFMove(request), sessionID);
+#if ENABLE(SVG_FONTS)
+ case CachedResource::SVGFontResource:
+ return new CachedSVGFont(WTFMove(request), sessionID);
#endif
case CachedResource::FontResource:
- return new CachedFont(request);
+ return new CachedFont(WTFMove(request), sessionID);
+ case CachedResource::MediaResource:
case CachedResource::RawResource:
case CachedResource::MainResource:
- return new CachedRawResource(request, type);
+ return new CachedRawResource(WTFMove(request), type, sessionID);
#if ENABLE(XSLT)
case CachedResource::XSLStyleSheet:
- return new CachedXSLStyleSheet(request);
+ return new CachedXSLStyleSheet(WTFMove(request), sessionID);
#endif
#if ENABLE(LINK_PREFETCH)
case CachedResource::LinkPrefetch:
- return new CachedResource(request, CachedResource::LinkPrefetch);
+ return new CachedResource(WTFMove(request), CachedResource::LinkPrefetch, sessionID);
case CachedResource::LinkSubresource:
- return new CachedResource(request, CachedResource::LinkSubresource);
+ return new CachedResource(WTFMove(request), CachedResource::LinkSubresource, sessionID);
#endif
#if ENABLE(VIDEO_TRACK)
case CachedResource::TextTrackResource:
- return new CachedTextTrack(request);
+ return new CachedTextTrack(WTFMove(request), sessionID);
#endif
}
ASSERT_NOT_REACHED();
- return 0;
+ return nullptr;
}
CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader)
- : m_document(0)
+ : m_document(nullptr)
, m_documentLoader(documentLoader)
, m_requestCount(0)
- , m_garbageCollectDocumentResourcesTimer(this, &CachedResourceLoader::garbageCollectDocumentResourcesTimerFired)
+ , m_garbageCollectDocumentResourcesTimer(*this, &CachedResourceLoader::garbageCollectDocumentResources)
, m_autoLoadImages(true)
, m_imagesEnabled(true)
, m_allowStaleResources(false)
@@ -123,157 +138,233 @@ CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader)
CachedResourceLoader::~CachedResourceLoader()
{
- m_documentLoader = 0;
- m_document = 0;
+ m_documentLoader = nullptr;
+ m_document = nullptr;
- clearPreloads();
- DocumentResourceMap::iterator end = m_documentResources.end();
- for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
- it->value->setOwningCachedResourceLoader(0);
+ clearPreloads(ClearPreloadsMode::ClearAllPreloads);
+ for (auto& resource : m_documentResources.values())
+ resource->setOwningCachedResourceLoader(nullptr);
// Make sure no requests still point to this CachedResourceLoader
ASSERT(m_requestCount == 0);
}
-CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
+CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
{
- URL url = m_document->completeURL(resourceURL);
- return cachedResource(url);
+ ASSERT(!resourceURL.isNull());
+ return cachedResource(MemoryCache::removeFragmentIdentifierIfNeeded(m_document->completeURL(resourceURL)));
}
-CachedResource* CachedResourceLoader::cachedResource(const URL& resourceURL) const
+CachedResource* CachedResourceLoader::cachedResource(const URL& url) const
{
- URL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
- return m_documentResources.get(url).get();
+ ASSERT(!MemoryCache::shouldRemoveFragmentIdentifier(url));
+ return m_documentResources.get(url).get();
}
Frame* CachedResourceLoader::frame() const
{
- return m_documentLoader ? m_documentLoader->frame() : 0;
+ return m_documentLoader ? m_documentLoader->frame() : nullptr;
+}
+
+SessionID CachedResourceLoader::sessionID() const
+{
+ SessionID sessionID = SessionID::defaultSessionID();
+
+ if (Frame* f = frame())
+ sessionID = f->page()->sessionID();
+
+ return sessionID;
}
-CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest& request)
+CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest&& request)
{
- if (Frame* f = frame()) {
- if (f->loader().pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) {
+ if (Frame* frame = this->frame()) {
+ if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None) {
+ if (Document* document = frame->document())
+ request.upgradeInsecureRequestIfNeeded(*document);
URL requestURL = request.resourceRequest().url();
- if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request.options(), request.forPreload()))
- PingLoader::loadImage(f, requestURL);
- return 0;
+ if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request, ForPreload::No))
+ PingLoader::loadImage(*frame, requestURL);
+ return nullptr;
}
}
- request.setDefer(clientDefersImage(request.resourceRequest().url()) ? CachedResourceRequest::DeferredByClient : CachedResourceRequest::NoDefer);
- return static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, request).get());
+
+ auto defer = clientDefersImage(request.resourceRequest().url()) ? DeferOption::DeferredByClient : DeferOption::NoDefer;
+ return downcast<CachedImage>(requestResource(CachedResource::ImageResource, WTFMove(request), ForPreload::No, defer).get());
}
-CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest& request)
+CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest&& request, bool isSVG)
{
- return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, request).get());
+#if ENABLE(SVG_FONTS)
+ if (isSVG)
+ return downcast<CachedSVGFont>(requestResource(CachedResource::SVGFontResource, WTFMove(request)).get());
+#else
+ UNUSED_PARAM(isSVG);
+#endif
+ return downcast<CachedFont>(requestResource(CachedResource::FontResource, WTFMove(request)).get());
}
#if ENABLE(VIDEO_TRACK)
-CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest& request)
+CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest&& request)
{
- return static_cast<CachedTextTrack*>(requestResource(CachedResource::TextTrackResource, request).get());
+ return downcast<CachedTextTrack>(requestResource(CachedResource::TextTrackResource, WTFMove(request)).get());
}
#endif
-CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest& request)
+CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest&& request)
{
- return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, request).get());
+ return downcast<CachedCSSStyleSheet>(requestResource(CachedResource::CSSStyleSheet, WTFMove(request)).get());
}
-CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest& request)
+CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest&& request)
{
- URL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url());
-
-#if ENABLE(CACHE_PARTITIONING)
- request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition());
-#endif
-
- if (CachedResource* existing = memoryCache()->resourceForRequest(request.resourceRequest())) {
- if (existing->type() == CachedResource::CSSStyleSheet)
- return static_cast<CachedCSSStyleSheet*>(existing);
- memoryCache()->remove(existing);
+ ASSERT(document());
+ request.setDomainForCachePartition(*document());
+
+ auto& memoryCache = MemoryCache::singleton();
+ if (request.allowsCaching()) {
+ if (CachedResource* existing = memoryCache.resourceForRequest(request.resourceRequest(), sessionID())) {
+ if (is<CachedCSSStyleSheet>(*existing))
+ return downcast<CachedCSSStyleSheet>(existing);
+ memoryCache.remove(*existing);
+ }
}
- if (url.string() != request.resourceRequest().url())
- request.mutableResourceRequest().setURL(url);
- CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request.resourceRequest(), request.charset());
+ request.removeFragmentIdentifierIfNeeded();
+
+ CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(WTFMove(request), sessionID());
- memoryCache()->add(userSheet.get());
+ if (userSheet->allowsCaching())
+ memoryCache.add(*userSheet);
// FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too?
- userSheet->load(this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType));
-
+ userSheet->load(*this);
return userSheet;
}
-CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest& request)
+CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest&& request)
{
- return static_cast<CachedScript*>(requestResource(CachedResource::Script, request).get());
+ return downcast<CachedScript>(requestResource(CachedResource::Script, WTFMove(request)).get());
}
#if ENABLE(XSLT)
-CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest& request)
+CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest&& request)
{
- return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, request).get());
+ return downcast<CachedXSLStyleSheet>(requestResource(CachedResource::XSLStyleSheet, WTFMove(request)).get());
}
#endif
-#if ENABLE(SVG)
-CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest& request)
+CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest&& request)
{
- return static_cast<CachedSVGDocument*>(requestResource(CachedResource::SVGDocumentResource, request).get());
+ return downcast<CachedSVGDocument>(requestResource(CachedResource::SVGDocumentResource, WTFMove(request)).get());
}
-#endif
#if ENABLE(LINK_PREFETCH)
-CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest& request)
+CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest&& request)
{
ASSERT(frame());
ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource);
- return requestResource(type, request);
+ return requestResource(type, WTFMove(request));
}
#endif
-CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest& request)
+CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMedia(CachedResourceRequest&& request)
{
- return static_cast<CachedRawResource*>(requestResource(CachedResource::RawResource, request).get());
+ return downcast<CachedRawResource>(requestResource(CachedResource::MediaResource, WTFMove(request)).get());
}
-CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest& request)
+CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest&& request)
{
- return static_cast<CachedRawResource*>(requestResource(CachedResource::MainResource, request).get());
+ return downcast<CachedRawResource>(requestResource(CachedResource::RawResource, WTFMove(request)).get());
+}
+
+CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest&& request)
+{
+ return downcast<CachedRawResource>(requestResource(CachedResource::MainResource, WTFMove(request)).get());
+}
+
+static MixedContentChecker::ContentType contentTypeFromResourceType(CachedResource::Type type)
+{
+ switch (type) {
+ // https://w3c.github.io/webappsec-mixed-content/#category-optionally-blockable
+ // Editor's Draft, 11 February 2016
+ // 3.1. Optionally-blockable Content
+ case CachedResource::ImageResource:
+ case CachedResource::MediaResource:
+ return MixedContentChecker::ContentType::ActiveCanWarn;
+
+ case CachedResource::CSSStyleSheet:
+ case CachedResource::Script:
+ case CachedResource::FontResource:
+ return MixedContentChecker::ContentType::Active;
+
+#if ENABLE(SVG_FONTS)
+ case CachedResource::SVGFontResource:
+ return MixedContentChecker::ContentType::Active;
+#endif
+
+ case CachedResource::RawResource:
+ case CachedResource::SVGDocumentResource:
+ return MixedContentChecker::ContentType::Active;
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+ return MixedContentChecker::ContentType::Active;
+#endif
+
+#if ENABLE(LINK_PREFETCH)
+ case CachedResource::LinkPrefetch:
+ case CachedResource::LinkSubresource:
+ return MixedContentChecker::ContentType::Active;
+#endif
+
+#if ENABLE(VIDEO_TRACK)
+ case CachedResource::TextTrackResource:
+ return MixedContentChecker::ContentType::Active;
+#endif
+ default:
+ ASSERT_NOT_REACHED();
+ return MixedContentChecker::ContentType::Active;
+ }
}
bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const URL& url) const
{
+ if (!canRequestInContentDispositionAttachmentSandbox(type, url))
+ return false;
+
switch (type) {
case CachedResource::Script:
#if ENABLE(XSLT)
case CachedResource::XSLStyleSheet:
#endif
-#if ENABLE(SVG)
case CachedResource::SVGDocumentResource:
-#endif
case CachedResource::CSSStyleSheet:
// These resource can inject script into the current document (Script,
// XSL) or exfiltrate the content of the current document (CSS).
- if (Frame* f = frame())
- if (!f->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url))
+ if (Frame* frame = this->frame()) {
+ if (!frame->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url))
return false;
+ Frame& top = frame->tree().top();
+ if (&top != frame && !top.loader().mixedContentChecker().canRunInsecureContent(top.document()->securityOrigin(), url))
+ return false;
+ }
break;
#if ENABLE(VIDEO_TRACK)
case CachedResource::TextTrackResource:
#endif
+ case CachedResource::MediaResource:
case CachedResource::RawResource:
case CachedResource::ImageResource:
+#if ENABLE(SVG_FONTS)
+ case CachedResource::SVGFontResource:
+#endif
case CachedResource::FontResource: {
// These resources can corrupt only the frame's pixels.
- if (Frame* f = frame()) {
- Frame& topFrame = f->tree().top();
- if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), url))
+ if (Frame* frame = this->frame()) {
+ if (!frame->loader().mixedContentChecker().canDisplayInsecureContent(m_document->securityOrigin(), contentTypeFromResourceType(type), url, MixedContentChecker::AlwaysDisplayInNonStrictMode::Yes))
+ return false;
+ Frame& topFrame = frame->tree().top();
+ if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), contentTypeFromResourceType(type), url))
return false;
}
break;
@@ -289,306 +380,602 @@ bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const
return true;
}
-bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, bool forPreload)
+bool CachedResourceLoader::allowedByContentSecurityPolicy(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, ContentSecurityPolicy::RedirectResponseReceived redirectResponseReceived) const
{
- if (document() && !document()->securityOrigin()->canDisplay(url)) {
- if (!forPreload)
- FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength());
- LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
- return 0;
- }
-
- // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
- bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy());
+ if (options.contentSecurityPolicyImposition == ContentSecurityPolicyImposition::SkipPolicyCheck)
+ return true;
- // 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::MainResource:
- case CachedResource::ImageResource:
- case CachedResource::CSSStyleSheet:
- case CachedResource::Script:
- case CachedResource::FontResource:
- case CachedResource::RawResource:
-#if ENABLE(LINK_PREFETCH)
- case CachedResource::LinkPrefetch:
- case CachedResource::LinkSubresource:
-#endif
-#if ENABLE(VIDEO_TRACK)
- case CachedResource::TextTrackResource:
-#endif
- if (options.requestOriginPolicy == RestrictToSameOrigin && !m_document->securityOrigin()->canRequest(url)) {
- printAccessDeniedMessage(url);
- return false;
- }
- break;
-#if ENABLE(SVG)
- case CachedResource::SVGDocumentResource:
-#endif
-#if ENABLE(XSLT)
- case CachedResource::XSLStyleSheet:
- if (!m_document->securityOrigin()->canRequest(url)) {
- printAccessDeniedMessage(url);
- return false;
- }
-#endif
- break;
- }
+ ASSERT(m_document);
+ ASSERT(m_document->contentSecurityPolicy());
switch (type) {
#if ENABLE(XSLT)
case CachedResource::XSLStyleSheet:
- if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
- return false;
- break;
#endif
case CachedResource::Script:
- if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
+ if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url, redirectResponseReceived))
return false;
-
- if (frame()) {
- if (!frame()->loader().client().allowScriptFromSource(frame()->settings().isScriptEnabled(), url))
- return false;
- }
break;
case CachedResource::CSSStyleSheet:
- if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
+ if (!m_document->contentSecurityPolicy()->allowStyleFromSource(url, redirectResponseReceived))
return false;
break;
-#if ENABLE(SVG)
case CachedResource::SVGDocumentResource:
-#endif
case CachedResource::ImageResource:
- if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
- return false;
- break;
- case CachedResource::FontResource: {
- if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
+ if (!m_document->contentSecurityPolicy()->allowImageFromSource(url, redirectResponseReceived))
return false;
break;
- }
- case CachedResource::MainResource:
- case CachedResource::RawResource:
-#if ENABLE(LINK_PREFETCH)
- case CachedResource::LinkPrefetch:
- case CachedResource::LinkSubresource:
+#if ENABLE(SVG_FONTS)
+ case CachedResource::SVGFontResource:
#endif
+ case CachedResource::FontResource:
+ if (!m_document->contentSecurityPolicy()->allowFontFromSource(url, redirectResponseReceived))
+ return false;
break;
+ case CachedResource::MediaResource:
#if ENABLE(VIDEO_TRACK)
case CachedResource::TextTrackResource:
- if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url))
+#endif
+ if (!m_document->contentSecurityPolicy()->allowMediaFromSource(url, redirectResponseReceived))
return false;
break;
-#endif
+ case CachedResource::RawResource:
+ return true;
+ default:
+ ASSERT_NOT_REACHED();
}
+ return true;
+}
+
+static inline bool isSameOriginDataURL(const URL& url, const ResourceLoaderOptions& options)
+{
+ // FIXME: Remove same-origin data URL flag since it was removed from fetch spec (https://github.com/whatwg/fetch/issues/381).
+ return url.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set;
+}
+
+bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const CachedResourceRequest& request, ForPreload forPreload)
+{
+ auto& options = request.options();
+
+ if (document() && !document()->securityOrigin().canDisplay(url)) {
+ if (forPreload == ForPreload::No)
+ FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength());
+ LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
+ return false;
+ }
+
+ if (options.mode == FetchOptions::Mode::SameOrigin && !m_document->securityOrigin().canRequest(url) && !isSameOriginDataURL(url, options)) {
+ printAccessDeniedMessage(url);
+ return false;
+ }
+
+ if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::No))
+ return false;
+
// SVG Images have unique security rules that prevent all subresource requests except for data urls.
if (type != CachedResource::MainResource && frame() && frame()->page()) {
if (frame()->page()->chrome().client().isSVGImageChromeClient() && !url.protocolIsData())
return false;
}
- // Last of all, check for insecure content. We do this last so that when
- // folks block insecure content with a CSP policy, they don't get a warning.
+ // Last of all, check for insecure content. We do this last so that when folks block insecure content with a CSP policy, they don't get a warning.
// They'll still get a warning in the console about CSP blocking the load.
- // FIXME: Should we consider forPreload here?
+ // FIXME: Should we consider whether the request is for preload here?
+ if (!checkInsecureContent(type, url))
+ return false;
+
+ return true;
+}
+
+// FIXME: Should we find a way to know whether the redirection is for a preload request like we do for CachedResourceLoader::canRequest?
+bool CachedResourceLoader::canRequestAfterRedirection(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options) const
+{
+ if (document() && !document()->securityOrigin().canDisplay(url)) {
+ FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength());
+ LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
+ return false;
+ }
+
+ // FIXME: According to https://fetch.spec.whatwg.org/#http-redirect-fetch, we should check that the URL is HTTP(s) except if in navigation mode.
+ // But we currently allow at least data URLs to be loaded.
+
+ if (options.mode == FetchOptions::Mode::SameOrigin && !m_document->securityOrigin().canRequest(url)) {
+ printAccessDeniedMessage(url);
+ return false;
+ }
+
+ if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::Yes))
+ return false;
+
+ // Last of all, check for insecure content. We do this last so that when folks block insecure content with a CSP policy, they don't get a warning.
+ // They'll still get a warning in the console about CSP blocking the load.
if (!checkInsecureContent(type, url))
return false;
return true;
}
-bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(CachedResource* resource)
+bool CachedResourceLoader::updateRequestAfterRedirection(CachedResource::Type type, ResourceRequest& request, const ResourceLoaderOptions& options)
+{
+ ASSERT(m_documentLoader);
+ if (auto* document = m_documentLoader->cachedResourceLoader().document())
+ upgradeInsecureResourceRequestIfNeeded(request, *document);
+
+ // FIXME: We might want to align the checks done here with the ones done in CachedResourceLoader::requestResource, content extensions blocking in particular.
+
+ return canRequestAfterRedirection(type, request.url(), options);
+}
+
+bool CachedResourceLoader::canRequestInContentDispositionAttachmentSandbox(CachedResource::Type type, const URL& url) const
+{
+ Document* document;
+
+ // FIXME: Do we want to expand this to all resource types that the mixed content checker would consider active content?
+ switch (type) {
+ case CachedResource::MainResource:
+ if (auto ownerElement = frame() ? frame()->ownerElement() : nullptr) {
+ document = &ownerElement->document();
+ break;
+ }
+ return true;
+ case CachedResource::CSSStyleSheet:
+ document = m_document;
+ break;
+ default:
+ return true;
+ }
+
+ if (!document->shouldEnforceContentDispositionAttachmentSandbox() || document->securityOrigin().canRequest(url))
+ return true;
+
+ String message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from document with Content-Disposition: attachment at URL " + document->url().stringCenterEllipsizedToLength() + ".";
+ document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
+ return false;
+}
+
+bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest& request, CachedResource* resource)
{
if (!resource || !frame() || resource->status() != CachedResource::Cached)
return true;
- ResourceRequest newRequest;
+ ResourceRequest newRequest = ResourceRequest(resource->url());
+ newRequest.setInitiatorIdentifier(request.resourceRequest().initiatorIdentifier());
+ if (request.resourceRequest().hiddenFromInspector())
+ newRequest.setHiddenFromInspector(true);
frame()->loader().loadedResourceFromMemoryCache(resource, newRequest);
-
+
// FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's
// URL, it is no longer appropriate to use this CachedResource.
return !newRequest.isNull();
}
-CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request)
+bool CachedResourceLoader::shouldUpdateCachedResourceWithCurrentRequest(const CachedResource& resource, const CachedResourceRequest& request)
+{
+ // WebKit is not supporting CORS for fonts (https://bugs.webkit.org/show_bug.cgi?id=86817), no need to update the resource before reusing it.
+ if (resource.type() == CachedResource::Type::FontResource)
+ return false;
+
+#if ENABLE(SVG_FONTS)
+ if (resource.type() == CachedResource::Type::SVGFontResource)
+ return false;
+#endif
+
+#if ENABLE(XSLT)
+ // Load is same-origin, we do not check for CORS.
+ if (resource.type() == CachedResource::XSLStyleSheet)
+ return false;
+#endif
+
+ // FIXME: We should enable resource reuse for these resource types
+ switch (resource.type()) {
+ case CachedResource::SVGDocumentResource:
+ return false;
+ case CachedResource::MainResource:
+ return false;
+#if ENABLE(LINK_PREFETCH)
+ case CachedResource::LinkPrefetch:
+ return false;
+ case CachedResource::LinkSubresource:
+ return false;
+#endif
+ default:
+ break;
+ }
+
+ if (resource.options().mode != request.options().mode || !originsMatch(request.origin(), resource.origin()))
+ return true;
+
+ if (resource.options().redirect != request.options().redirect && resource.hasRedirections())
+ return true;
+
+ return false;
+}
+
+static inline bool isResourceSuitableForDirectReuse(const CachedResource& resource, const CachedResourceRequest& request)
+{
+ // FIXME: For being loaded requests, the response tainting may not be correctly computed if the fetch mode is not the same.
+ // Even if the fetch mode is the same, we are not sure that the resource can be reused (Vary: Origin header for instance).
+ // We should find a way to improve this.
+ if (resource.status() != CachedResource::Cached)
+ return false;
+
+ // If the cached resource has not followed redirections, it is incomplete and we should not use it.
+ // Let's make sure the memory cache has no such resource.
+ ASSERT(resource.response().type() != ResourceResponse::Type::Opaqueredirect);
+
+ // We could support redirect modes other than Follow in case of a redirected resource.
+ // This case is rare and is not worth optimizing currently.
+ if (request.options().redirect != FetchOptions::Redirect::Follow && resource.hasRedirections())
+ return false;
+
+ // FIXME: Implement reuse of cached raw resources.
+ if (resource.type() == CachedResource::Type::RawResource || resource.type() == CachedResource::Type::MediaResource)
+ return false;
+
+ return true;
+}
+
+CachedResourceHandle<CachedResource> CachedResourceLoader::updateCachedResourceWithCurrentRequest(const CachedResource& resource, CachedResourceRequest&& request)
+{
+ if (!isResourceSuitableForDirectReuse(resource, request)) {
+ request.setCachingPolicy(CachingPolicy::DisallowCaching);
+ return loadResource(resource.type(), WTFMove(request));
+ }
+
+ auto resourceHandle = createResource(resource.type(), WTFMove(request), sessionID());
+ resourceHandle->loadFrom(resource);
+ return resourceHandle;
+}
+
+static inline void logMemoryCacheResourceRequest(Frame* frame, const String& key, const String& description)
{
+ if (!frame || !frame->page())
+ return;
+ frame->page()->diagnosticLoggingClient().logDiagnosticMessage(key, description, ShouldSample::Yes);
+}
+
+void CachedResourceLoader::prepareFetch(CachedResource::Type type, CachedResourceRequest& request)
+{
+ // Implementing step 1 to 7 of https://fetch.spec.whatwg.org/#fetching
+
+ if (!request.origin() && document())
+ request.setOrigin(document()->securityOrigin());
+
+ request.setAcceptHeaderIfNone(type);
+
+ // Accept-Language value is handled in underlying port-specific code.
+ // FIXME: Decide whether to support client hints
+}
+
+void CachedResourceLoader::updateHTTPRequestHeaders(CachedResource::Type type, CachedResourceRequest& request)
+{
+ // Implementing steps 7 to 12 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
+
+ // FIXME: We should reconcile handling of MainResource with other resources.
+ if (type != CachedResource::Type::MainResource) {
+ // In some cases we may try to load resources in frameless documents. Such loads always fail.
+ // FIXME: We shouldn't need to do the check on frame.
+ if (auto* frame = this->frame())
+ request.updateReferrerOriginAndUserAgentHeaders(frame->loader(), document() ? document()->referrerPolicy() : ReferrerPolicy::Default);
+ }
+
+ request.updateAccordingCacheMode();
+}
+
+CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, DeferOption defer)
+{
+ if (Document* document = this->document())
+ request.upgradeInsecureRequestIfNeeded(*document);
+
URL url = request.resourceRequest().url();
-
- LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload());
-
- // If only the fragment identifiers differ, it is the same resource.
- url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
- if (!url.isValid())
- return 0;
+ LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority() ? static_cast<int>(request.priority().value()) : -1, forPreload == ForPreload::Yes);
- if (!canRequest(type, url, request.options(), request.forPreload()))
- return 0;
+ if (!url.isValid()) {
+ RELEASE_LOG_IF_ALLOWED("requestResource: URL is invalid (frame = %p)", frame());
+ return nullptr;
+ }
- if (Frame* f = frame())
- f->loader().client().dispatchWillRequestResource(&request);
+ prepareFetch(type, request);
+
+ // We are passing url as well as request, as request url may contain a fragment identifier.
+ if (!canRequest(type, url, request, forPreload)) {
+ RELEASE_LOG_IF_ALLOWED("requestResource: Not allowed to request resource (frame = %p)", frame());
+ return nullptr;
+ }
- if (memoryCache()->disabled()) {
+#if ENABLE(CONTENT_EXTENSIONS)
+ if (frame() && frame()->mainFrame().page() && m_documentLoader) {
+ const auto& resourceRequest = request.resourceRequest();
+ auto blockedStatus = frame()->mainFrame().page()->userContentProvider().processContentExtensionRulesForLoad(resourceRequest.url(), toResourceType(type), *m_documentLoader);
+ request.applyBlockedStatus(blockedStatus);
+ if (blockedStatus.blockedLoad) {
+ RELEASE_LOG_IF_ALLOWED("requestResource: Resource blocked by content blocker (frame = %p)", frame());
+ if (type == CachedResource::Type::MainResource) {
+ auto resource = createResource(type, WTFMove(request), sessionID());
+ ASSERT(resource);
+ resource->error(CachedResource::Status::LoadError);
+ resource->setResourceError(ResourceError(ContentExtensions::WebKitContentBlockerDomain, 0, resourceRequest.url(), WEB_UI_STRING("The URL was blocked by a content blocker", "WebKitErrorBlockedByContentBlocker description")));
+ return resource;
+ }
+ return nullptr;
+ }
+ if (blockedStatus.madeHTTPS
+ && type == CachedResource::Type::MainResource
+ && m_documentLoader->isLoadingMainResource()) {
+ // This is to make sure the correct 'new' URL shows in the location bar.
+ m_documentLoader->frameLoader()->client().dispatchDidChangeProvisionalURL();
+ }
+ url = request.resourceRequest().url(); // The content extension could have changed it from http to https.
+ url = MemoryCache::removeFragmentIdentifierIfNeeded(url); // Might need to remove fragment identifier again.
+ }
+#endif
+
+#if ENABLE(WEB_TIMING)
+ LoadTiming loadTiming;
+ loadTiming.markStartTimeAndFetchStart();
+ InitiatorContext initiatorContext = request.options().initiatorContext;
+#endif
+
+ if (request.resourceRequest().url().protocolIsInHTTPFamily())
+ updateHTTPRequestHeaders(type, request);
+
+ auto& memoryCache = MemoryCache::singleton();
+ if (request.allowsCaching() && memoryCache.disabled()) {
DocumentResourceMap::iterator it = m_documentResources.find(url.string());
if (it != m_documentResources.end()) {
- it->value->setOwningCachedResourceLoader(0);
+ it->value->setOwningCachedResourceLoader(nullptr);
m_documentResources.remove(it);
}
}
// See if we can use an existing resource from the cache.
CachedResourceHandle<CachedResource> resource;
-#if ENABLE(CACHE_PARTITIONING)
if (document())
- request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition());
-#endif
+ request.setDomainForCachePartition(*document());
+
+ if (request.allowsCaching())
+ resource = memoryCache.resourceForRequest(request.resourceRequest(), sessionID());
+
+ if (resource && request.isLinkPreload() && !resource->isLinkPreload())
+ resource->setLinkPreload();
- resource = memoryCache()->resourceForRequest(request.resourceRequest());
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheUsageKey(), resource ? DiagnosticLoggingKeys::inMemoryCacheKey() : DiagnosticLoggingKeys::notInMemoryCacheKey());
- const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
+ RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, defer);
switch (policy) {
case Reload:
- memoryCache()->remove(resource.get());
+ memoryCache.remove(*resource);
FALLTHROUGH;
case Load:
- resource = loadResource(type, request, request.charset());
+ if (resource)
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::unusedKey());
+ resource = loadResource(type, WTFMove(request));
break;
case Revalidate:
- resource = revalidateResource(request, resource.get());
+ if (resource)
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::revalidatingKey());
+ resource = revalidateResource(WTFMove(request), *resource);
break;
case Use:
- if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(resource.get()))
- return 0;
- memoryCache()->resourceAccessed(resource.get());
+ ASSERT(resource);
+ if (shouldUpdateCachedResourceWithCurrentRequest(*resource, request)) {
+ resource = updateCachedResourceWithCurrentRequest(*resource, WTFMove(request));
+ if (resource->status() != CachedResource::Status::Cached)
+ policy = Load;
+ } else {
+ if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(request, resource.get()))
+ return nullptr;
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::usedKey());
+ memoryCache.resourceAccessed(*resource);
+#if ENABLE(WEB_TIMING)
+ loadTiming.setResponseEnd(MonotonicTime::now());
+
+ if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) {
+ ResourceTiming resourceTiming = ResourceTiming::fromCache(url, request.initiatorName(), loadTiming);
+ if (initiatorContext == InitiatorContext::Worker) {
+ ASSERT(is<CachedRawResource>(resource.get()));
+ downcast<CachedRawResource>(resource.get())->finishedTimingForWorkerLoad(WTFMove(resourceTiming));
+ } else if (document()) {
+ ASSERT(initiatorContext == InitiatorContext::Document);
+ m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, request.initiatorName(), frame());
+ m_resourceTimingInfo.addResourceTiming(*resource.get(), *document(), WTFMove(resourceTiming));
+ }
+ }
+#endif
+ if (forPreload == ForPreload::No)
+ resource->setLoadPriority(request.priority());
+ }
break;
}
if (!resource)
- return 0;
+ return nullptr;
- if (!request.forPreload() || policy != Use)
- resource->setLoadPriority(request.priority());
+ if (forPreload == ForPreload::No && resource->loader() && resource->resourceRequest().ignoreForRequestCount()) {
+ resource->resourceRequest().setIgnoreForRequestCount(false);
+ incrementRequestCount(*resource);
+ }
- if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) {
- resource->load(this, request.options());
+ if ((policy != Use || resource->stillNeedsLoad()) && defer == DeferOption::NoDefer) {
+ resource->load(*this);
// We don't support immediate loads, but we do support immediate failure.
if (resource->errorOccurred()) {
- if (resource->inCache())
- memoryCache()->remove(resource.get());
- return 0;
+ if (resource->allowsCaching() && resource->inCache())
+ memoryCache.remove(*resource);
+ return nullptr;
}
}
- if (!request.resourceRequest().url().protocolIsData())
- m_validatedURLs.add(request.resourceRequest().url());
+ if (document() && !document()->loadEventFinished() && !resource->resourceRequest().url().protocolIsData())
+ m_validatedURLs.add(resource->resourceRequest().url());
ASSERT(resource->url() == url.string());
m_documentResources.set(resource->url(), resource);
return resource;
}
-CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(const CachedResourceRequest& request, CachedResource* resource)
+void CachedResourceLoader::documentDidFinishLoadEvent()
{
- ASSERT(resource);
- ASSERT(resource->inCache());
- ASSERT(!memoryCache()->disabled());
- ASSERT(resource->canUseCacheValidator());
- ASSERT(!resource->resourceToRevalidate());
-
- // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below.
- String url = resource->url();
- CachedResourceHandle<CachedResource> newResource = createResource(resource->type(), resource->resourceRequest(), resource->encoding());
-
- LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
- newResource->setResourceToRevalidate(resource);
-
- memoryCache()->remove(resource);
- memoryCache()->add(newResource.get());
-#if ENABLE(RESOURCE_TIMING)
- storeResourceTimingInitiatorInformation(resource, request);
-#else
- UNUSED_PARAM(request);
+ m_validatedURLs.clear();
+}
+
+CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(CachedResourceRequest&& request, CachedResource& resource)
+{
+ ASSERT(resource.inCache());
+ auto& memoryCache = MemoryCache::singleton();
+ ASSERT(!memoryCache.disabled());
+ ASSERT(resource.canUseCacheValidator());
+ ASSERT(!resource.resourceToRevalidate());
+ ASSERT(resource.sessionID() == sessionID());
+ ASSERT(resource.allowsCaching());
+
+ CachedResourceHandle<CachedResource> newResource = createResource(resource.type(), WTFMove(request), resource.sessionID());
+
+ LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), &resource);
+ newResource->setResourceToRevalidate(&resource);
+
+ memoryCache.remove(resource);
+ memoryCache.add(*newResource);
+#if ENABLE(WEB_TIMING)
+ if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
+ m_resourceTimingInfo.storeResourceTimingInitiatorInformation(newResource, newResource->initiatorName(), frame());
#endif
return newResource;
}
-CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
+CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest&& request)
{
- ASSERT(!memoryCache()->resourceForRequest(request.resourceRequest()));
+ auto& memoryCache = MemoryCache::singleton();
+ ASSERT(!request.allowsCaching() || !memoryCache.resourceForRequest(request.resourceRequest(), sessionID())
+ || request.resourceRequest().cachePolicy() == DoNotUseAnyCache || request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData || request.resourceRequest().cachePolicy() == RefreshAnyCacheData);
LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data());
- CachedResourceHandle<CachedResource> resource = createResource(type, request.mutableResourceRequest(), charset);
+ CachedResourceHandle<CachedResource> resource = createResource(type, WTFMove(request), sessionID());
- if (!memoryCache()->add(resource.get()))
+ if (resource->allowsCaching() && !memoryCache.add(*resource))
resource->setOwningCachedResourceLoader(this);
-#if ENABLE(RESOURCE_TIMING)
- storeResourceTimingInitiatorInformation(resource, request);
+#if ENABLE(WEB_TIMING)
+ if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
+ m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, resource->initiatorName(), frame());
#endif
return resource;
}
-#if ENABLE(RESOURCE_TIMING)
-void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request)
+static void logRevalidation(const String& reason, DiagnosticLoggingClient& logClient)
{
- if (resource->type() == CachedResource::MainResource) {
- // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations.
- if (frame()->ownerElement() && m_documentLoader->frameLoader()->stateMachine()->committingFirstRealLoad()) {
- InitiatorInfo info = { frame()->ownerElement()->localName(), monotonicallyIncreasingTime() };
- m_initiatorMap.add(resource.get(), info);
- }
- } else {
- InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime() };
- m_initiatorMap.add(resource.get(), info);
+ logClient.logDiagnosticMessage(DiagnosticLoggingKeys::cachedResourceRevalidationReasonKey(), reason, ShouldSample::Yes);
+}
+
+static void logResourceRevalidationDecision(CachedResource::RevalidationDecision reason, const Frame* frame)
+{
+ if (!frame || !frame->page())
+ return;
+ auto& logClient = frame->page()->diagnosticLoggingClient();
+ switch (reason) {
+ case CachedResource::RevalidationDecision::No:
+ break;
+ case CachedResource::RevalidationDecision::YesDueToExpired:
+ logRevalidation(DiagnosticLoggingKeys::isExpiredKey(), logClient);
+ break;
+ case CachedResource::RevalidationDecision::YesDueToNoStore:
+ logRevalidation(DiagnosticLoggingKeys::noStoreKey(), logClient);
+ break;
+ case CachedResource::RevalidationDecision::YesDueToNoCache:
+ logRevalidation(DiagnosticLoggingKeys::noCacheKey(), logClient);
+ break;
+ case CachedResource::RevalidationDecision::YesDueToCachePolicy:
+ logRevalidation(DiagnosticLoggingKeys::reloadKey(), logClient);
+ break;
}
}
-#endif // ENABLE(RESOURCE_TIMING)
-CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption defer) const
+CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, DeferOption defer) const
{
+ auto& request = cachedResourceRequest.resourceRequest();
+
if (!existingResource)
return Load;
+ if (request.cachePolicy() == DoNotUseAnyCache || request.cachePolicy() == ReloadIgnoringCacheData)
+ return Load;
+
+ if (request.cachePolicy() == RefreshAnyCacheData)
+ return Reload;
+
// We already have a preload going for this URL.
- if (forPreload && existingResource->isPreloaded())
+ if (forPreload == ForPreload::Yes && existingResource->isPreloaded())
return Use;
// If the same URL has been loaded as a different type, we need to reload.
if (existingResource->type() != type) {
LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonTypeMismatchKey());
return Reload;
}
- if (!existingResource->canReuse(request))
+ if (!existingResource->varyHeaderValuesMatch(request))
return Reload;
+ auto* textDecoder = existingResource->textResourceDecoder();
+ if (textDecoder && !textDecoder->hasEqualEncodingForCharset(cachedResourceRequest.charset()))
+ return Reload;
+
+ // FIXME: We should use the same cache policy for all resource types. The raw resource policy is overly strict
+ // while the normal subresource policy is too loose.
+ if (existingResource->isMainOrMediaOrRawResource() && frame()) {
+ bool strictPolicyDisabled = frame()->loader().isStrictRawResourceValidationPolicyDisabledForTesting();
+ bool canReuseRawResource = strictPolicyDisabled || downcast<CachedRawResource>(*existingResource).canReuse(request);
+ if (!canReuseRawResource)
+ return Reload;
+ }
+
// Conditional requests should have failed canReuse check.
ASSERT(!request.isConditional());
- // Do not load from cache if images are not enabled. The load for this image will be blocked
- // in CachedImage::load.
- if (CachedResourceRequest::DeferredByClient == defer)
+ // Do not load from cache if images are not enabled. The load for this image will be blocked in CachedImage::load.
+ if (defer == DeferOption::DeferredByClient)
return Reload;
-
- // Don't reload resources while pasting.
- if (m_allowStaleResources)
+
+ // Don't reload resources while pasting or if cache mode allows stale resources.
+ if (m_allowStaleResources || cachedResourceRequest.options().cache == FetchOptions::Cache::ForceCache || cachedResourceRequest.options().cache == FetchOptions::Cache::OnlyIfCached)
return Use;
-
- // Alwaus use preloads.
+
+ ASSERT(cachedResourceRequest.options().cache == FetchOptions::Cache::Default || cachedResourceRequest.options().cache == FetchOptions::Cache::NoCache);
+
+ // Always use preloads.
if (existingResource->isPreloaded())
return Use;
-
- // CachePolicyHistoryBuffer uses the cache no matter what.
- if (cachePolicy(type) == CachePolicyHistoryBuffer)
+
+ // We can find resources that are being validated from cache only when validation is just successfully completing.
+ if (existingResource->validationCompleting())
return Use;
+ ASSERT(!existingResource->validationInProgress());
+
+ // Validate the redirect chain.
+ bool cachePolicyIsHistoryBuffer = cachePolicy(type) == CachePolicyHistoryBuffer;
+ if (!existingResource->redirectChainAllowsReuse(cachePolicyIsHistoryBuffer ? ReuseExpiredRedirection : DoNotReuseExpiredRedirection)) {
+ LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to not cached or expired redirections.");
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonRedirectChainKey());
+ return Reload;
+ }
+
+ // CachePolicyHistoryBuffer uses the cache except if this is a main resource with "cache-control: no-store".
+ if (cachePolicyIsHistoryBuffer) {
+ // FIXME: Ignoring "cache-control: no-cache" for sub-resources on history navigation but not the main
+ // resource is inconsistent. We should probably harmonize this.
+ if (!existingResource->response().cacheControlContainsNoStore() || type != CachedResource::MainResource)
+ return Use;
+ }
// Don't reuse resources with Cache-control: no-store.
if (existingResource->response().cacheControlContainsNoStore()) {
LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.");
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonNoStoreKey());
return Reload;
}
@@ -600,6 +987,7 @@ CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalida
// client's requests are made without CORS and some with.
if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings.");
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonCredentialSettingsKey());
return Reload;
}
@@ -610,27 +998,40 @@ CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalida
// CachePolicyReload always reloads
if (cachePolicy(type) == CachePolicyReload) {
LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonReloadKey());
return Reload;
}
// We'll try to reload the resource if it failed last time.
if (existingResource->errorOccurred()) {
LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonErrorKey());
return Reload;
}
-
- // For resources that are not yet loaded we ignore the cache policy.
- if (existingResource->isLoading())
+
+ if (existingResource->isLoading()) {
+ // Do not use cached main resources that are still loading because sharing
+ // loading CachedResources in this case causes issues with regards to cancellation.
+ // If one of the DocumentLoader clients decides to cancel the load, then the load
+ // would be cancelled for all other DocumentLoaders as well.
+ if (type == CachedResource::Type::MainResource)
+ return Reload;
+ // For cached subresources that are still loading we ignore the cache policy.
return Use;
+ }
+
+ auto revalidationDecision = existingResource->makeRevalidationDecision(cachePolicy(type));
+ logResourceRevalidationDecision(revalidationDecision, frame());
// Check if the cache headers requires us to revalidate (cache expiration for example).
- if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy(type))) {
+ if (revalidationDecision != CachedResource::RevalidationDecision::No) {
// See if the resource has usable ETag or Last-modified headers.
if (existingResource->canUseCacheValidator())
return Revalidate;
// No, must reload.
- LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
+ LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
+ logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonMustRevalidateNoValidatorKey());
return Reload;
}
@@ -651,7 +1052,7 @@ void CachedResourceLoader::printAccessDeniedMessage(const URL& url) const
else
message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from frame with URL " + m_document->url().stringCenterEllipsizedToLength() + ". Domains, protocols and ports must match.\n";
- frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
+ frame()->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
}
void CachedResourceLoader::setAutoLoadImages(bool enable)
@@ -680,72 +1081,72 @@ void CachedResourceLoader::setImagesEnabled(bool enable)
reloadImagesIfNotDeferred();
}
-bool CachedResourceLoader::clientDefersImage(const URL& url) const
+bool CachedResourceLoader::clientDefersImage(const URL&) const
+{
+ return !m_imagesEnabled;
+}
+
+bool CachedResourceLoader::shouldPerformImageLoad(const URL& url) const
{
- return frame() && !frame()->loader().client().allowImage(m_imagesEnabled, url);
+ return m_autoLoadImages || url.protocolIsData();
}
bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const
{
- return clientDefersImage(url) || !m_autoLoadImages;
+ return clientDefersImage(url) || !shouldPerformImageLoad(url);
}
void CachedResourceLoader::reloadImagesIfNotDeferred()
{
- DocumentResourceMap::iterator end = m_documentResources.end();
- for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
- CachedResource* resource = it->value.get();
- if (resource->type() == CachedResource::ImageResource && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
- const_cast<CachedResource*>(resource)->load(this, defaultCachedResourceOptions());
+ for (auto& resource : m_documentResources.values()) {
+ if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
+ downcast<CachedImage>(*resource).load(*this);
}
}
CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type) const
{
- if (!frame())
+ Frame* frame = this->frame();
+ if (!frame)
return CachePolicyVerify;
if (type != CachedResource::MainResource)
- return frame()->loader().subresourceCachePolicy();
-
- if (frame()->loader().loadType() == FrameLoadTypeReloadFromOrigin || frame()->loader().loadType() == FrameLoadTypeReload)
+ return frame->loader().subresourceCachePolicy();
+
+ if (Page* page = frame->page()) {
+ if (page->isResourceCachingDisabled())
+ return CachePolicyReload;
+ }
+
+ switch (frame->loader().loadType()) {
+ case FrameLoadType::ReloadFromOrigin:
+ case FrameLoadType::Reload:
return CachePolicyReload;
- return CachePolicyVerify;
+ case FrameLoadType::Back:
+ case FrameLoadType::Forward:
+ case FrameLoadType::IndexedBackForward:
+ // Do not revalidate cached main resource on back/forward navigation.
+ return CachePolicyHistoryBuffer;
+ default:
+ return CachePolicyVerify;
+ }
}
-void CachedResourceLoader::removeCachedResource(CachedResource* resource) const
+void CachedResourceLoader::removeCachedResource(CachedResource& resource)
{
#ifndef NDEBUG
- DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
+ DocumentResourceMap::iterator it = m_documentResources.find(resource.url());
if (it != m_documentResources.end())
- ASSERT(it->value.get() == resource);
+ ASSERT(it->value.get() == &resource);
#endif
- m_documentResources.remove(resource->url());
+ m_documentResources.remove(resource.url());
}
-void CachedResourceLoader::loadDone(CachedResource* resource, bool shouldPerformPostLoadActions)
+void CachedResourceLoader::loadDone(bool shouldPerformPostLoadActions)
{
RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
RefPtr<Document> protectDocument(m_document);
-#if ENABLE(RESOURCE_TIMING)
- if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304)) {
- HashMap<CachedResource*, InitiatorInfo>::iterator initiatorIt = m_initiatorMap.find(resource);
- if (initiatorIt != m_initiatorMap.end()) {
- ASSERT(document());
- Document* initiatorDocument = document();
- if (resource->type() == CachedResource::MainResource)
- initiatorDocument = document()->parentDocument();
- ASSERT(initiatorDocument);
- const InitiatorInfo& info = initiatorIt->value;
- initiatorDocument->domWindow()->performance()->addResourceTiming(info.name, initiatorDocument, resource->resourceRequest(), resource->response(), info.startTime, resource->loadFinishTime());
- m_initiatorMap.remove(initiatorIt);
- }
- }
-#else
- UNUSED_PARAM(resource);
-#endif // ENABLE(RESOURCE_TIMING)
-
if (frame())
frame()->loader().loadDone();
@@ -762,111 +1163,65 @@ void CachedResourceLoader::loadDone(CachedResource* resource, bool shouldPerform
// bookkeeping on CachedResources, so instead pseudo-GC them -- when the
// reference count reaches 1, m_documentResources is the only reference, so
// remove it from the map.
-void CachedResourceLoader::garbageCollectDocumentResourcesTimerFired(Timer<CachedResourceLoader>& timer)
-{
- ASSERT_UNUSED(timer, &timer == &m_garbageCollectDocumentResourcesTimer);
- garbageCollectDocumentResources();
-}
-
void CachedResourceLoader::garbageCollectDocumentResources()
{
typedef Vector<String, 10> StringVector;
StringVector resourcesToDelete;
- for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
- if (it->value->hasOneHandle()) {
- resourcesToDelete.append(it->key);
- it->value->setOwningCachedResourceLoader(0);
+ for (auto& resource : m_documentResources) {
+ if (resource.value->hasOneHandle()) {
+ resourcesToDelete.append(resource.key);
+ resource.value->setOwningCachedResourceLoader(nullptr);
}
}
- for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
- m_documentResources.remove(*it);
+ for (auto& resource : resourcesToDelete)
+ m_documentResources.remove(resource);
}
void CachedResourceLoader::performPostLoadActions()
{
- checkForPendingPreloads();
-
- platformStrategies()->loaderStrategy()->resourceLoadScheduler()->servePendingRequests();
+ platformStrategies()->loaderStrategy()->servePendingRequests();
}
-void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
+void CachedResourceLoader::incrementRequestCount(const CachedResource& resource)
{
- if (res->ignoreForRequestCount())
+ if (resource.ignoreForRequestCount())
return;
++m_requestCount;
}
-void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
+void CachedResourceLoader::decrementRequestCount(const CachedResource& resource)
{
- if (res->ignoreForRequestCount())
+ if (resource.ignoreForRequestCount())
return;
--m_requestCount;
ASSERT(m_requestCount > -1);
}
-void CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
+CachedResourceHandle<CachedResource> CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest&& request)
{
- // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>.
- // FIXME: We should consider adding a setting to toggle aggressive preloading behavior as opposed
- // to making this behavior specific to iOS.
-#if !PLATFORM(IOS)
- bool hasRendering = m_document->body() && m_document->renderView();
- bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
- if (!hasRendering && !canBlockParser) {
- // Don't preload subresources that can't block the parser before we have something to draw.
- // This helps prevent preloads from delaying first display when bandwidth is limited.
- PendingPreload pendingPreload = { type, request, charset };
- m_pendingPreloads.append(pendingPreload);
- return;
- }
-#endif
- requestPreload(type, request, charset);
-}
-
-void CachedResourceLoader::checkForPendingPreloads()
-{
- if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
- return;
-#if PLATFORM(IOS)
- // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>.
- // So, we should never have any pending preloads.
- // FIXME: We should look to avoid compiling this code entirely when building for iOS.
- ASSERT_NOT_REACHED();
-#endif
- while (!m_pendingPreloads.isEmpty()) {
- PendingPreload preload = m_pendingPreloads.takeFirst();
- // 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(preload.m_request.resourceRequest().url()))
- requestPreload(preload.m_type, preload.m_request, preload.m_charset);
- }
- m_pendingPreloads.clear();
-}
+ if (request.charset().isEmpty() && (type == CachedResource::Script || type == CachedResource::CSSStyleSheet))
+ request.setCharset(m_document->charset());
-void CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
-{
- String encoding;
- if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
- encoding = charset.isEmpty() ? m_document->charset() : charset;
-
- request.setCharset(encoding);
- request.setForPreload(true);
-
- CachedResourceHandle<CachedResource> resource = requestResource(type, request);
+ CachedResourceHandle<CachedResource> resource = requestResource(type, WTFMove(request), ForPreload::Yes);
if (!resource || (m_preloads && m_preloads->contains(resource.get())))
- return;
+ return nullptr;
+ // Fonts need special treatment since just creating the resource doesn't trigger a load.
+ if (type == CachedResource::FontResource)
+ downcast<CachedFont>(resource.get())->beginLoadIfNeeded(*this);
resource->increasePreloadCount();
if (!m_preloads)
- m_preloads = adoptPtr(new ListHashSet<CachedResource*>);
+ m_preloads = std::make_unique<ListHashSet<CachedResource*>>();
m_preloads->add(resource.get());
#if PRELOAD_DEBUG
printf("PRELOADING %s\n", resource->url().latin1().data());
#endif
+ return resource;
}
bool CachedResourceLoader::isPreloaded(const String& urlString) const
@@ -874,24 +1229,15 @@ bool CachedResourceLoader::isPreloaded(const String& urlString) const
const URL& url = m_document->completeURL(urlString);
if (m_preloads) {
- ListHashSet<CachedResource*>::iterator end = m_preloads->end();
- for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
- CachedResource* resource = *it;
+ for (auto& resource : *m_preloads) {
if (resource->url() == url)
return true;
}
}
-
- Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
- for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
- PendingPreload pendingPreload = *it;
- if (pendingPreload.m_request.resourceRequest().url() == url)
- return true;
- }
return false;
}
-void CachedResourceLoader::clearPreloads()
+void CachedResourceLoader::clearPreloads(ClearPreloadsMode mode)
{
#if PRELOAD_DEBUG
printPreloadStats();
@@ -899,20 +1245,21 @@ void CachedResourceLoader::clearPreloads()
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();
- bool deleted = res->deleteIfPossible();
- if (!deleted && res->preloadResult() == CachedResource::PreloadNotReferenced)
- memoryCache()->remove(res);
+ std::unique_ptr<ListHashSet<CachedResource*>> remainingLinkPreloads;
+ for (auto* resource : *m_preloads) {
+ ASSERT(resource);
+ if (mode == ClearPreloadsMode::ClearSpeculativePreloads && resource->isLinkPreload()) {
+ if (!remainingLinkPreloads)
+ remainingLinkPreloads = std::make_unique<ListHashSet<CachedResource*>>();
+ remainingLinkPreloads->add(resource);
+ continue;
+ }
+ resource->decreasePreloadCount();
+ bool deleted = resource->deleteIfPossible();
+ if (!deleted && resource->preloadResult() == CachedResource::PreloadNotReferenced)
+ MemoryCache::singleton().remove(*resource);
}
- m_preloads.clear();
-}
-
-void CachedResourceLoader::clearPendingPreloads()
-{
- m_pendingPreloads.clear();
+ m_preloads = WTFMove(remainingLinkPreloads);
}
#if PRELOAD_DEBUG
@@ -924,37 +1271,35 @@ void CachedResourceLoader::printPreloadStats()
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) {
+ for (auto& resource : m_preloads) {
+ if (resource->preloadResult() == CachedResource::PreloadNotReferenced)
+ printf("!! UNREFERENCED PRELOAD %s\n", resource->url().latin1().data());
+ else if (resource->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
+ printf("HIT COMPLETE PRELOAD %s\n", resource->url().latin1().data());
+ else if (resource->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
+ printf("HIT LOADING PRELOAD %s\n", resource->url().latin1().data());
+
+ if (resource->type() == CachedResource::Script) {
scripts++;
- if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
+ if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
scriptMisses++;
- } else if (res->type() == CachedResource::CSSStyleSheet) {
+ } else if (resource->type() == CachedResource::CSSStyleSheet) {
stylesheets++;
- if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
+ if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
stylesheetMisses++;
} else {
images++;
- if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
+ if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
imageMisses++;
}
-
- if (res->errorOccurred())
- memoryCache()->remove(res);
-
- res->decreasePreloadCount();
+
+ if (resource->errorOccurred() && resource->preloadResult() == CachedResource::PreloadNotReferenced)
+ MemoryCache::singleton().remove(resource);
+
+ resource->decreasePreloadCount();
}
- m_preloads.clear();
-
+ m_preloads = nullptr;
+
if (scripts)
printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
if (stylesheets)
@@ -966,8 +1311,13 @@ void CachedResourceLoader::printPreloadStats()
const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
{
- static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType);
+ static NeverDestroyed<ResourceLoaderOptions> options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientCredentialPolicy::MayAskClientForCredentials, FetchOptions::Credentials::Include, DoSecurityCheck, FetchOptions::Mode::NoCors, DoNotIncludeCertificateInfo, ContentSecurityPolicyImposition::DoPolicyCheck, DefersLoadingPolicy::AllowDefersLoading, CachingPolicy::AllowCaching);
return options;
}
+bool CachedResourceLoader::isAlwaysOnLoggingAllowed() const
+{
+ return m_documentLoader ? m_documentLoader->isAlwaysOnLoggingAllowed() : true;
+}
+
}
diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.h b/Source/WebCore/loader/cache/CachedResourceLoader.h
index bcc793e0c..23eb1dd6f 100644
--- a/Source/WebCore/loader/cache/CachedResourceLoader.h
+++ b/Source/WebCore/loader/cache/CachedResourceLoader.h
@@ -23,14 +23,15 @@
pages from the web. It has a memory cache for these objects.
*/
-#ifndef CachedResourceLoader_h
-#define CachedResourceLoader_h
+#pragma once
#include "CachePolicy.h"
#include "CachedResource.h"
#include "CachedResourceHandle.h"
#include "CachedResourceRequest.h"
+#include "ContentSecurityPolicy.h"
#include "ResourceLoadPriority.h"
+#include "ResourceTimingInformation.h"
#include "Timer.h"
#include <wtf/Deque.h>
#include <wtf/HashMap.h>
@@ -68,28 +69,26 @@ friend class ImageLoader;
friend class ResourceCacheValidationSuppressor;
public:
- static PassRef<CachedResourceLoader> create(DocumentLoader* documentLoader) { return adoptRef(*new CachedResourceLoader(documentLoader)); }
+ static Ref<CachedResourceLoader> create(DocumentLoader* documentLoader) { return adoptRef(*new CachedResourceLoader(documentLoader)); }
~CachedResourceLoader();
- CachedResourceHandle<CachedImage> requestImage(CachedResourceRequest&);
- CachedResourceHandle<CachedCSSStyleSheet> requestCSSStyleSheet(CachedResourceRequest&);
- CachedResourceHandle<CachedCSSStyleSheet> requestUserCSSStyleSheet(CachedResourceRequest&);
- CachedResourceHandle<CachedScript> requestScript(CachedResourceRequest&);
- CachedResourceHandle<CachedFont> requestFont(CachedResourceRequest&);
- CachedResourceHandle<CachedRawResource> requestRawResource(CachedResourceRequest&);
- CachedResourceHandle<CachedRawResource> requestMainResource(CachedResourceRequest&);
-
-#if ENABLE(SVG)
- CachedResourceHandle<CachedSVGDocument> requestSVGDocument(CachedResourceRequest&);
-#endif
+ CachedResourceHandle<CachedImage> requestImage(CachedResourceRequest&&);
+ CachedResourceHandle<CachedCSSStyleSheet> requestCSSStyleSheet(CachedResourceRequest&&);
+ CachedResourceHandle<CachedCSSStyleSheet> requestUserCSSStyleSheet(CachedResourceRequest&&);
+ CachedResourceHandle<CachedScript> requestScript(CachedResourceRequest&&);
+ CachedResourceHandle<CachedFont> requestFont(CachedResourceRequest&&, bool isSVG);
+ CachedResourceHandle<CachedRawResource> requestMedia(CachedResourceRequest&&);
+ CachedResourceHandle<CachedRawResource> requestRawResource(CachedResourceRequest&&);
+ CachedResourceHandle<CachedRawResource> requestMainResource(CachedResourceRequest&&);
+ CachedResourceHandle<CachedSVGDocument> requestSVGDocument(CachedResourceRequest&&);
#if ENABLE(XSLT)
- CachedResourceHandle<CachedXSLStyleSheet> requestXSLStyleSheet(CachedResourceRequest&);
+ CachedResourceHandle<CachedXSLStyleSheet> requestXSLStyleSheet(CachedResourceRequest&&);
#endif
#if ENABLE(LINK_PREFETCH)
- CachedResourceHandle<CachedResource> requestLinkResource(CachedResource::Type, CachedResourceRequest&);
+ CachedResourceHandle<CachedResource> requestLinkResource(CachedResource::Type, CachedResourceRequest&&);
#endif
#if ENABLE(VIDEO_TRACK)
- CachedResourceHandle<CachedTextTrack> requestTextTrack(CachedResourceRequest&);
+ CachedResourceHandle<CachedTextTrack> requestTextTrack(CachedResourceRequest&&);
#endif
// Logs an access denied message to the console for the specified URL.
@@ -97,67 +96,89 @@ public:
CachedResource* cachedResource(const String& url) const;
CachedResource* cachedResource(const URL& url) const;
-
+
typedef HashMap<String, CachedResourceHandle<CachedResource>> DocumentResourceMap;
const DocumentResourceMap& allCachedResources() const { return m_documentResources; }
bool autoLoadImages() const { return m_autoLoadImages; }
void setAutoLoadImages(bool);
+ bool imagesEnabled() const { return m_imagesEnabled; }
void setImagesEnabled(bool);
bool shouldDeferImageLoad(const URL&) const;
+ bool shouldPerformImageLoad(const URL&) const;
CachePolicy cachePolicy(CachedResource::Type) const;
Frame* frame() const; // Can be null
Document* document() const { return m_document; } // Can be null
void setDocument(Document* document) { m_document = document; }
- void clearDocumentLoader() { m_documentLoader = 0; }
+ void clearDocumentLoader() { m_documentLoader = nullptr; }
+ SessionID sessionID() const;
- void removeCachedResource(CachedResource*) const;
+ void removeCachedResource(CachedResource&);
- void loadDone(CachedResource*, bool shouldPerformPostLoadActions = true);
+ void loadDone(bool shouldPerformPostLoadActions = true);
- void garbageCollectDocumentResources();
-
- void incrementRequestCount(const CachedResource*);
- void decrementRequestCount(const CachedResource*);
+ WEBCORE_EXPORT void garbageCollectDocumentResources();
+
+ void incrementRequestCount(const CachedResource&);
+ void decrementRequestCount(const CachedResource&);
int requestCount() const { return m_requestCount; }
- bool isPreloaded(const String& urlString) const;
- void clearPreloads();
- void clearPendingPreloads();
- void preload(CachedResource::Type, CachedResourceRequest&, const String& charset);
- void checkForPendingPreloads();
+ WEBCORE_EXPORT bool isPreloaded(const String& urlString) const;
+ enum class ClearPreloadsMode { ClearSpeculativePreloads, ClearAllPreloads };
+ void clearPreloads(ClearPreloadsMode);
+ CachedResourceHandle<CachedResource> preload(CachedResource::Type, CachedResourceRequest&&);
void printPreloadStats();
- bool canRequest(CachedResource::Type, const URL&, const ResourceLoaderOptions&, bool forPreload = false);
+
+ bool updateRequestAfterRedirection(CachedResource::Type, ResourceRequest&, const ResourceLoaderOptions&);
static const ResourceLoaderOptions& defaultCachedResourceOptions();
+ void documentDidFinishLoadEvent();
+
+#if ENABLE(WEB_TIMING)
+ ResourceTimingInformation& resourceTimingInformation() { return m_resourceTimingInfo; }
+#endif
+
+ bool isAlwaysOnLoggingAllowed() const;
+
private:
explicit CachedResourceLoader(DocumentLoader*);
- CachedResourceHandle<CachedResource> requestResource(CachedResource::Type, CachedResourceRequest&);
- CachedResourceHandle<CachedResource> revalidateResource(const CachedResourceRequest&, CachedResource*);
- CachedResourceHandle<CachedResource> loadResource(CachedResource::Type, CachedResourceRequest&, const String& charset);
-#if ENABLE(RESOURCE_TIMING)
- void storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>&, const CachedResourceRequest&);
-#endif
- void requestPreload(CachedResource::Type, CachedResourceRequest&, const String& charset);
+ enum class ForPreload { Yes, No };
+ enum class DeferOption { NoDefer, DeferredByClient };
+
+ CachedResourceHandle<CachedResource> requestResource(CachedResource::Type, CachedResourceRequest&&, ForPreload = ForPreload::No, DeferOption = DeferOption::NoDefer);
+ CachedResourceHandle<CachedResource> revalidateResource(CachedResourceRequest&&, CachedResource&);
+ CachedResourceHandle<CachedResource> loadResource(CachedResource::Type, CachedResourceRequest&&);
+
+ void prepareFetch(CachedResource::Type, CachedResourceRequest&);
+ void updateHTTPRequestHeaders(CachedResource::Type, CachedResourceRequest&);
+ void updateReferrerOriginAndUserAgentHeaders(CachedResourceRequest&);
+
+ bool canRequest(CachedResource::Type, const URL&, const CachedResourceRequest&, ForPreload);
enum RevalidationPolicy { Use, Revalidate, Reload, Load };
- RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, ResourceRequest&, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption) const;
-
- bool shouldContinueAfterNotifyingLoadedFromMemoryCache(CachedResource*);
+ RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, CachedResourceRequest&, CachedResource* existingResource, ForPreload, DeferOption) const;
+
+ bool shouldUpdateCachedResourceWithCurrentRequest(const CachedResource&, const CachedResourceRequest&);
+ CachedResourceHandle<CachedResource> updateCachedResourceWithCurrentRequest(const CachedResource&, CachedResourceRequest&&);
+
+ bool shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest&, CachedResource*);
bool checkInsecureContent(CachedResource::Type, const URL&) const;
+ bool allowedByContentSecurityPolicy(CachedResource::Type, const URL&, const ResourceLoaderOptions&, ContentSecurityPolicy::RedirectResponseReceived) const;
- void garbageCollectDocumentResourcesTimerFired(Timer<CachedResourceLoader>&);
void performPostLoadActions();
bool clientDefersImage(const URL&) const;
void reloadImagesIfNotDeferred();
-
+
+ bool canRequestAfterRedirection(CachedResource::Type, const URL&, const ResourceLoaderOptions&) const;
+ bool canRequestInContentDispositionAttachmentSandbox(CachedResource::Type, const URL&) const;
+
HashSet<String> m_validatedURLs;
mutable DocumentResourceMap m_documentResources;
Document* m_document;
@@ -165,22 +186,12 @@ private:
int m_requestCount;
- OwnPtr<ListHashSet<CachedResource*>> m_preloads;
- struct PendingPreload {
- CachedResource::Type m_type;
- CachedResourceRequest m_request;
- String m_charset;
- };
- Deque<PendingPreload> m_pendingPreloads;
-
- Timer<CachedResourceLoader> m_garbageCollectDocumentResourcesTimer;
-
-#if ENABLE(RESOURCE_TIMING)
- struct InitiatorInfo {
- AtomicString name;
- double startTime;
- };
- HashMap<CachedResource*, InitiatorInfo> m_initiatorMap;
+ std::unique_ptr<ListHashSet<CachedResource*>> m_preloads;
+
+ Timer m_garbageCollectDocumentResourcesTimer;
+
+#if ENABLE(WEB_TIMING)
+ ResourceTimingInformation m_resourceTimingInfo;
#endif
// 29 bits left
@@ -193,25 +204,19 @@ class ResourceCacheValidationSuppressor {
WTF_MAKE_NONCOPYABLE(ResourceCacheValidationSuppressor);
WTF_MAKE_FAST_ALLOCATED;
public:
- ResourceCacheValidationSuppressor(CachedResourceLoader* loader)
+ ResourceCacheValidationSuppressor(CachedResourceLoader& loader)
: m_loader(loader)
- , m_previousState(false)
+ , m_previousState(m_loader.m_allowStaleResources)
{
- if (m_loader) {
- m_previousState = m_loader->m_allowStaleResources;
- m_loader->m_allowStaleResources = true;
- }
+ m_loader.m_allowStaleResources = true;
}
~ResourceCacheValidationSuppressor()
{
- if (m_loader)
- m_loader->m_allowStaleResources = m_previousState;
+ m_loader.m_allowStaleResources = m_previousState;
}
private:
- CachedResourceLoader* m_loader;
+ CachedResourceLoader& m_loader;
bool m_previousState;
};
} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/loader/cache/CachedResourceRequest.cpp b/Source/WebCore/loader/cache/CachedResourceRequest.cpp
index 55a493859..a5ac5637a 100644
--- a/Source/WebCore/loader/cache/CachedResourceRequest.cpp
+++ b/Source/WebCore/loader/cache/CachedResourceRequest.cpp
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * 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
@@ -27,52 +27,49 @@
#include "CachedResourceRequest.h"
#include "CachedResourceLoader.h"
+#include "ContentExtensionActions.h"
+#include "CrossOriginAccessControl.h"
#include "Document.h"
#include "Element.h"
+#include "FrameLoader.h"
+#include "HTTPHeaderValues.h"
+#include "MemoryCache.h"
+#include "SecurityPolicy.h"
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
-CachedResourceRequest::CachedResourceRequest(const ResourceRequest& resourceRequest, const String& charset, ResourceLoadPriority priority)
- : m_resourceRequest(resourceRequest)
- , m_charset(charset)
- , m_options(CachedResourceLoader::defaultCachedResourceOptions())
- , m_priority(priority)
- , m_forPreload(false)
- , m_defer(NoDefer)
-{
-}
-
-CachedResourceRequest::CachedResourceRequest(const ResourceRequest& resourceRequest, const ResourceLoaderOptions& options)
- : m_resourceRequest(resourceRequest)
+CachedResourceRequest::CachedResourceRequest(ResourceRequest&& resourceRequest, const ResourceLoaderOptions& options, std::optional<ResourceLoadPriority> priority, String&& charset)
+ : m_resourceRequest(WTFMove(resourceRequest))
+ , m_charset(WTFMove(charset))
, m_options(options)
- , m_priority(ResourceLoadPriorityUnresolved)
- , m_forPreload(false)
- , m_defer(NoDefer)
-{
-}
-
-CachedResourceRequest::CachedResourceRequest(const ResourceRequest& resourceRequest, ResourceLoadPriority priority)
- : m_resourceRequest(resourceRequest)
- , m_options(CachedResourceLoader::defaultCachedResourceOptions())
, m_priority(priority)
- , m_forPreload(false)
- , m_defer(NoDefer)
+ , m_fragmentIdentifier(splitFragmentIdentifierFromRequestURL(m_resourceRequest))
{
}
-CachedResourceRequest::~CachedResourceRequest()
+String CachedResourceRequest::splitFragmentIdentifierFromRequestURL(ResourceRequest& request)
{
+ if (!MemoryCache::shouldRemoveFragmentIdentifier(request.url()))
+ return { };
+ URL url = request.url();
+ String fragmentIdentifier = url.fragmentIdentifier();
+ url.removeFragmentIdentifier();
+ request.setURL(url);
+ return fragmentIdentifier;
}
-void CachedResourceRequest::setInitiator(PassRefPtr<Element> element)
+void CachedResourceRequest::setInitiator(Element& element)
{
- ASSERT(!m_initiatorElement && m_initiatorName.isEmpty());
- m_initiatorElement = element;
+ ASSERT(!m_initiatorElement);
+ ASSERT(m_initiatorName.isEmpty());
+ m_initiatorElement = &element;
}
void CachedResourceRequest::setInitiator(const AtomicString& name)
{
- ASSERT(!m_initiatorElement && m_initiatorName.isEmpty());
+ ASSERT(!m_initiatorElement);
+ ASSERT(m_initiatorName.isEmpty());
m_initiatorName = name;
}
@@ -83,8 +80,198 @@ const AtomicString& CachedResourceRequest::initiatorName() const
if (!m_initiatorName.isEmpty())
return m_initiatorName;
- DEFINE_STATIC_LOCAL(AtomicString, defaultName, ("resource", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<AtomicString> defaultName("other", AtomicString::ConstructFromLiteral);
return defaultName;
}
+void CachedResourceRequest::setAsPotentiallyCrossOrigin(const String& mode, Document& document)
+{
+ ASSERT(m_options.mode == FetchOptions::Mode::NoCors);
+
+ m_origin = &document.securityOrigin();
+
+ if (mode.isNull())
+ return;
+
+ m_options.mode = FetchOptions::Mode::Cors;
+
+ FetchOptions::Credentials credentials = equalLettersIgnoringASCIICase(mode, "omit")
+ ? FetchOptions::Credentials::Omit : equalLettersIgnoringASCIICase(mode, "use-credentials")
+ ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin;
+ m_options.credentials = credentials;
+ m_options.allowCredentials = credentials == FetchOptions::Credentials::Include ? AllowStoredCredentials : DoNotAllowStoredCredentials;
+ WebCore::updateRequestForAccessControl(m_resourceRequest, document.securityOrigin(), m_options.allowCredentials);
+}
+
+void CachedResourceRequest::updateForAccessControl(Document& document)
+{
+ ASSERT(m_options.mode == FetchOptions::Mode::Cors);
+
+ m_origin = &document.securityOrigin();
+ WebCore::updateRequestForAccessControl(m_resourceRequest, *m_origin, m_options.allowCredentials);
+}
+
+void upgradeInsecureResourceRequestIfNeeded(ResourceRequest& request, Document& document)
+{
+ URL url = request.url();
+
+ ASSERT(document.contentSecurityPolicy());
+ document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(url, ContentSecurityPolicy::InsecureRequestType::Load);
+
+ if (url == request.url())
+ return;
+
+ request.setURL(url);
+}
+
+void CachedResourceRequest::upgradeInsecureRequestIfNeeded(Document& document)
+{
+ upgradeInsecureResourceRequestIfNeeded(m_resourceRequest, document);
+}
+
+void CachedResourceRequest::setDomainForCachePartition(Document& document)
+{
+ m_resourceRequest.setDomainForCachePartition(document.topOrigin().domainForCachePartition());
+}
+
+static inline String acceptHeaderValueFromType(CachedResource::Type type)
+{
+ switch (type) {
+ case CachedResource::Type::MainResource:
+ return ASCIILiteral("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
+ case CachedResource::Type::ImageResource:
+ return ASCIILiteral("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
+ case CachedResource::Type::CSSStyleSheet:
+ return ASCIILiteral("text/css,*/*;q=0.1");
+ case CachedResource::Type::SVGDocumentResource:
+ return ASCIILiteral("image/svg+xml");
+#if ENABLE(XSLT)
+ case CachedResource::Type::XSLStyleSheet:
+ // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example.
+ return ASCIILiteral("text/xml,application/xml,application/xhtml+xml,text/xsl,application/rss+xml,application/atom+xml");
+#endif
+ default:
+ return ASCIILiteral("*/*");
+ }
+}
+
+void CachedResourceRequest::setAcceptHeaderIfNone(CachedResource::Type type)
+{
+ if (!m_resourceRequest.hasHTTPHeader(HTTPHeaderName::Accept))
+ m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Accept, acceptHeaderValueFromType(type));
+}
+
+void CachedResourceRequest::updateAccordingCacheMode()
+{
+ if (m_options.cache == FetchOptions::Cache::Default
+ && (m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfModifiedSince)
+ || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfNoneMatch)
+ || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfUnmodifiedSince)
+ || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfMatch)
+ || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfRange)))
+ m_options.cache = FetchOptions::Cache::NoStore;
+
+ switch (m_options.cache) {
+ case FetchOptions::Cache::NoCache:
+ m_resourceRequest.setCachePolicy(RefreshAnyCacheData);
+ m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::maxAge0());
+ break;
+ case FetchOptions::Cache::NoStore:
+ m_options.cachingPolicy = CachingPolicy::DisallowCaching;
+ m_resourceRequest.setCachePolicy(DoNotUseAnyCache);
+ m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
+ m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
+ break;
+ case FetchOptions::Cache::Reload:
+ m_resourceRequest.setCachePolicy(ReloadIgnoringCacheData);
+ m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
+ m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
+ break;
+ case FetchOptions::Cache::Default:
+ break;
+ case FetchOptions::Cache::ForceCache:
+ m_resourceRequest.setCachePolicy(ReturnCacheDataElseLoad);
+ break;
+ case FetchOptions::Cache::OnlyIfCached:
+ m_resourceRequest.setCachePolicy(ReturnCacheDataDontLoad);
+ break;
+ }
+}
+
+void CachedResourceRequest::removeFragmentIdentifierIfNeeded()
+{
+ URL url = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url());
+ if (url.string() != m_resourceRequest.url())
+ m_resourceRequest.setURL(url);
+}
+
+#if ENABLE(CONTENT_EXTENSIONS)
+
+void CachedResourceRequest::applyBlockedStatus(const ContentExtensions::BlockedStatus& blockedStatus)
+{
+ ContentExtensions::applyBlockedStatusToRequest(blockedStatus, m_resourceRequest);
+}
+
+#endif
+
+void CachedResourceRequest::updateReferrerOriginAndUserAgentHeaders(FrameLoader& frameLoader, ReferrerPolicy defaultPolicy)
+{
+ // Implementing step 7 to 9 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
+
+ String outgoingOrigin;
+ String outgoingReferrer = m_resourceRequest.httpReferrer();
+ if (!outgoingReferrer.isNull())
+ outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
+ else {
+ outgoingReferrer = frameLoader.outgoingReferrer();
+ outgoingOrigin = frameLoader.outgoingOrigin();
+ }
+
+ // FIXME: Refactor SecurityPolicy::generateReferrerHeader to align with new terminology used in https://w3c.github.io/webappsec-referrer-policy.
+ switch (m_options.referrerPolicy) {
+ case FetchOptions::ReferrerPolicy::EmptyString: {
+ outgoingReferrer = SecurityPolicy::generateReferrerHeader(defaultPolicy, m_resourceRequest.url(), outgoingReferrer);
+ break; }
+ case FetchOptions::ReferrerPolicy::NoReferrerWhenDowngrade:
+ outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Default, m_resourceRequest.url(), outgoingReferrer);
+ break;
+ case FetchOptions::ReferrerPolicy::NoReferrer:
+ outgoingReferrer = String();
+ break;
+ case FetchOptions::ReferrerPolicy::Origin:
+ outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer);
+ break;
+ case FetchOptions::ReferrerPolicy::OriginWhenCrossOrigin:
+ if (isRequestCrossOrigin(m_origin.get(), m_resourceRequest.url(), m_options))
+ outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer);
+ break;
+ case FetchOptions::ReferrerPolicy::UnsafeUrl:
+ break;
+ };
+
+ if (outgoingReferrer.isEmpty())
+ m_resourceRequest.clearHTTPReferrer();
+ else
+ m_resourceRequest.setHTTPReferrer(outgoingReferrer);
+ FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
+
+ frameLoader.applyUserAgent(m_resourceRequest);
+}
+
+bool isRequestCrossOrigin(SecurityOrigin* origin, const URL& requestURL, const ResourceLoaderOptions& options)
+{
+ if (!origin)
+ return false;
+
+ // Using same origin mode guarantees the loader will not do a cross-origin load, so we let it take care of it and just return false.
+ if (options.mode == FetchOptions::Mode::SameOrigin)
+ return false;
+
+ // FIXME: We should remove options.sameOriginDataURLFlag once https://github.com/whatwg/fetch/issues/393 is fixed.
+ if (requestURL.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set)
+ return false;
+
+ return !origin->canRequest(requestURL);
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedResourceRequest.h b/Source/WebCore/loader/cache/CachedResourceRequest.h
index 0122c9958..1030b3660 100644
--- a/Source/WebCore/loader/cache/CachedResourceRequest.h
+++ b/Source/WebCore/loader/cache/CachedResourceRequest.h
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * 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
@@ -23,55 +23,83 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CachedResourceRequest_h
-#define CachedResourceRequest_h
+#pragma once
+#include "CachedResource.h"
+#include "DocumentLoader.h"
#include "Element.h"
#include "ResourceLoadPriority.h"
#include "ResourceLoaderOptions.h"
#include "ResourceRequest.h"
+#include "SecurityOrigin.h"
#include <wtf/RefPtr.h>
#include <wtf/text/AtomicString.h>
namespace WebCore {
+
+namespace ContentExtensions {
+struct BlockedStatus;
+}
+
class Document;
+class FrameLoader;
+enum class ReferrerPolicy;
+
+bool isRequestCrossOrigin(SecurityOrigin*, const URL& requestURL, const ResourceLoaderOptions&);
class CachedResourceRequest {
public:
- enum DeferOption { NoDefer, DeferredByClient };
-
- explicit CachedResourceRequest(const ResourceRequest&, const String& charset = String(), ResourceLoadPriority = ResourceLoadPriorityUnresolved);
- CachedResourceRequest(const ResourceRequest&, const ResourceLoaderOptions&);
- CachedResourceRequest(const ResourceRequest&, ResourceLoadPriority);
- ~CachedResourceRequest();
+ CachedResourceRequest(ResourceRequest&&, const ResourceLoaderOptions&, std::optional<ResourceLoadPriority> = std::nullopt, String&& charset = String());
- ResourceRequest& mutableResourceRequest() { return m_resourceRequest; }
+ ResourceRequest&& releaseResourceRequest() { return WTFMove(m_resourceRequest); }
const ResourceRequest& resourceRequest() const { return m_resourceRequest; }
const String& charset() const { return m_charset; }
void setCharset(const String& charset) { m_charset = charset; }
const ResourceLoaderOptions& options() const { return m_options; }
void setOptions(const ResourceLoaderOptions& options) { m_options = options; }
- void setPriority(ResourceLoadPriority priority) { m_priority = priority; }
- ResourceLoadPriority priority() const { return m_priority; }
- bool forPreload() const { return m_forPreload; }
- void setForPreload(bool forPreload) { m_forPreload = forPreload; }
- DeferOption defer() const { return m_defer; }
- void setDefer(DeferOption defer) { m_defer = defer; }
- void setInitiator(PassRefPtr<Element>);
+ const std::optional<ResourceLoadPriority>& priority() const { return m_priority; }
+ void setInitiator(Element&);
void setInitiator(const AtomicString& name);
const AtomicString& initiatorName() const;
+ bool allowsCaching() const { return m_options.cachingPolicy == CachingPolicy::AllowCaching; }
+ void setCachingPolicy(CachingPolicy policy) { m_options.cachingPolicy = policy; }
+
+ void setAsPotentiallyCrossOrigin(const String&, Document&);
+ void updateForAccessControl(Document&);
+
+ void updateReferrerOriginAndUserAgentHeaders(FrameLoader&, ReferrerPolicy);
+ void upgradeInsecureRequestIfNeeded(Document&);
+ void setAcceptHeaderIfNone(CachedResource::Type);
+ void updateAccordingCacheMode();
+ void removeFragmentIdentifierIfNeeded();
+#if ENABLE(CONTENT_EXTENSIONS)
+ void applyBlockedStatus(const ContentExtensions::BlockedStatus&);
+#endif
+ void setDomainForCachePartition(Document&);
+ bool isLinkPreload() const { return m_isLinkPreload; }
+ void setIsLinkPreload() { m_isLinkPreload = true; }
+
+ void setOrigin(Ref<SecurityOrigin>&& origin) { m_origin = WTFMove(origin); }
+ RefPtr<SecurityOrigin> releaseOrigin() { return WTFMove(m_origin); }
+ SecurityOrigin* origin() const { return m_origin.get(); }
+
+ String&& releaseFragmentIdentifier() { return WTFMove(m_fragmentIdentifier); }
+ void clearFragmentIdentifier() { m_fragmentIdentifier = { }; }
+
+ static String splitFragmentIdentifierFromRequestURL(ResourceRequest&);
private:
ResourceRequest m_resourceRequest;
String m_charset;
ResourceLoaderOptions m_options;
- ResourceLoadPriority m_priority;
- bool m_forPreload;
- DeferOption m_defer;
+ std::optional<ResourceLoadPriority> m_priority;
RefPtr<Element> m_initiatorElement;
AtomicString m_initiatorName;
+ RefPtr<SecurityOrigin> m_origin;
+ String m_fragmentIdentifier;
+ bool m_isLinkPreload { false };
};
-} // namespace WebCore
+void upgradeInsecureResourceRequestIfNeeded(ResourceRequest&, Document&);
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp b/Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp
index 91a17b66b..b339a8e58 100644
--- a/Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp
+++ b/Source/WebCore/loader/cache/CachedResourceRequestInitiators.cpp
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * 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
@@ -30,6 +30,7 @@ namespace WebCore {
CachedResourceRequestInitiators::CachedResourceRequestInitiators()
: css("css", AtomicString::ConstructFromLiteral)
+ , fetch("fetch", AtomicString::ConstructFromLiteral)
, icon("icon", AtomicString::ConstructFromLiteral)
, xmlhttprequest("xmlhttprequest", AtomicString::ConstructFromLiteral)
{
diff --git a/Source/WebCore/loader/cache/CachedResourceRequestInitiators.h b/Source/WebCore/loader/cache/CachedResourceRequestInitiators.h
index 24ac2387d..50538cafd 100644
--- a/Source/WebCore/loader/cache/CachedResourceRequestInitiators.h
+++ b/Source/WebCore/loader/cache/CachedResourceRequestInitiators.h
@@ -13,7 +13,7 @@
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CachedResourceRequestInitiators_h
-#define CachedResourceRequestInitiators_h
+#pragma once
#include "ThreadGlobalData.h"
#include <wtf/text/AtomicString.h>
@@ -32,12 +31,14 @@
namespace WebCore {
struct CachedResourceRequestInitiators {
+ CachedResourceRequestInitiators();
+
const AtomicString css;
+ const AtomicString fetch;
const AtomicString icon;
const AtomicString xmlhttprequest;
WTF_MAKE_NONCOPYABLE(CachedResourceRequestInitiators); WTF_MAKE_FAST_ALLOCATED;
private:
- CachedResourceRequestInitiators();
friend class ThreadGlobalData;
};
@@ -47,5 +48,3 @@ inline const CachedResourceRequestInitiators& cachedResourceRequestInitiators()
}
} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/loader/cache/CachedSVGDocument.cpp b/Source/WebCore/loader/cache/CachedSVGDocument.cpp
index 15946263f..08b40169d 100644
--- a/Source/WebCore/loader/cache/CachedSVGDocument.cpp
+++ b/Source/WebCore/loader/cache/CachedSVGDocument.cpp
@@ -21,22 +21,16 @@
*/
#include "config.h"
-
-#if ENABLE(SVG)
#include "CachedSVGDocument.h"
-#include "CachedResourceClient.h"
-#include "CachedResourceHandle.h"
-#include "ResourceBuffer.h"
-#include <wtf/text/StringBuilder.h>
+#include "SharedBuffer.h"
namespace WebCore {
-CachedSVGDocument::CachedSVGDocument(const ResourceRequest& request)
- : CachedResource(request, SVGDocumentResource)
+CachedSVGDocument::CachedSVGDocument(CachedResourceRequest&& request, SessionID sessionID)
+ : CachedResource(WTFMove(request), SVGDocumentResource, sessionID)
, m_decoder(TextResourceDecoder::create("application/xml"))
{
- setAccept("image/svg+xml");
}
CachedSVGDocument::~CachedSVGDocument()
@@ -53,20 +47,14 @@ String CachedSVGDocument::encoding() const
return m_decoder->encoding().name();
}
-void CachedSVGDocument::finishLoading(ResourceBuffer* data)
+void CachedSVGDocument::finishLoading(SharedBuffer* data)
{
if (data) {
- StringBuilder decodedText;
- decodedText.append(m_decoder->decode(data->data(), data->size()));
- decodedText.append(m_decoder->flush());
// We don't need to create a new frame because the new document belongs to the parent UseElement.
- m_document = SVGDocument::create(0, response().url());
- m_document->setContent(decodedText.toString());
+ m_document = SVGDocument::create(nullptr, response().url());
+ m_document->setContent(m_decoder->decodeAndFlush(data->data(), data->size()));
}
CachedResource::finishLoading(data);
}
}
-
-#endif
-
diff --git a/Source/WebCore/loader/cache/CachedSVGDocument.h b/Source/WebCore/loader/cache/CachedSVGDocument.h
index d6a6f5d4e..7981f1de0 100644
--- a/Source/WebCore/loader/cache/CachedSVGDocument.h
+++ b/Source/WebCore/loader/cache/CachedSVGDocument.h
@@ -20,10 +20,8 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef CachedSVGDocument_h
-#define CachedSVGDocument_h
+#pragma once
-#if ENABLE(SVG)
#include "CachedResource.h"
#include "CachedResourceHandle.h"
#include "SVGDocument.h"
@@ -33,24 +31,22 @@ namespace WebCore {
class CachedSVGDocument final : public CachedResource {
public:
- explicit CachedSVGDocument(const ResourceRequest&);
+ explicit CachedSVGDocument(CachedResourceRequest&&, SessionID);
virtual ~CachedSVGDocument();
SVGDocument* document() const { return m_document.get(); }
-protected:
+private:
+ bool mayTryReplaceEncodedData() const override { return true; }
+ void setEncoding(const String&) override;
+ String encoding() const override;
+ const TextResourceDecoder* textResourceDecoder() const override { return m_decoder.get(); }
+ void finishLoading(SharedBuffer*) override;
+
RefPtr<SVGDocument> m_document;
RefPtr<TextResourceDecoder> m_decoder;
-
-private:
- virtual bool mayTryReplaceEncodedData() const override { return true; }
- virtual void setEncoding(const String&) override;
- virtual String encoding() const override;
- virtual void finishLoading(ResourceBuffer*) override;
};
} // namespace WebCore
-#endif // USE(SVG)
-
-#endif // CachedSVGDocument_h
+SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedSVGDocument, CachedResource::SVGDocumentResource)
diff --git a/Source/WebCore/loader/cache/CachedSVGDocumentClient.h b/Source/WebCore/loader/cache/CachedSVGDocumentClient.h
index a01e0aee9..499dcd10e 100644
--- a/Source/WebCore/loader/cache/CachedSVGDocumentClient.h
+++ b/Source/WebCore/loader/cache/CachedSVGDocumentClient.h
@@ -20,26 +20,17 @@
Boston, MA 02110-1301, USA.
*/
-#ifndef CachedSVGDocumentClient_h
-#define CachedSVGDocumentClient_h
-
-#if ENABLE(SVG)
+#pragma once
#include "CachedResourceClient.h"
namespace WebCore {
-class CachedSVGDocument;
-
class CachedSVGDocumentClient : public CachedResourceClient {
public:
virtual ~CachedSVGDocumentClient() { }
static CachedResourceClientType expectedType() { return SVGDocumentType; }
- virtual CachedResourceClientType resourceClientType() const override { return expectedType(); }
+ CachedResourceClientType resourceClientType() const override { return expectedType(); }
};
} // namespace WebCore
-
-#endif // ENABLE(SVG)
-
-#endif // CachedSVGDocumentClient_h
diff --git a/Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp b/Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp
index 116f20e88..a9bf7c66e 100644
--- a/Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp
+++ b/Source/WebCore/loader/cache/CachedSVGDocumentReference.cpp
@@ -26,7 +26,6 @@
#include "config.h"
#include "CachedSVGDocumentReference.h"
-#if ENABLE(SVG) && ENABLE(CSS_FILTERS)
#include "CachedResourceHandle.h"
#include "CachedResourceLoader.h"
#include "CachedResourceRequest.h"
@@ -37,7 +36,7 @@ namespace WebCore {
CachedSVGDocumentReference::CachedSVGDocumentReference(const String& url)
: m_url(url)
- , m_document(0)
+ , m_document(nullptr)
, m_loadRequested(false)
{
}
@@ -45,24 +44,23 @@ CachedSVGDocumentReference::CachedSVGDocumentReference(const String& url)
CachedSVGDocumentReference::~CachedSVGDocumentReference()
{
if (m_document)
- m_document->removeClient(this);
+ m_document->removeClient(*this);
}
-void CachedSVGDocumentReference::load(CachedResourceLoader* loader)
+void CachedSVGDocumentReference::load(CachedResourceLoader& loader, const ResourceLoaderOptions& options)
{
- ASSERT(loader);
if (m_loadRequested)
return;
- CachedResourceRequest request(ResourceRequest(loader->document()->completeURL(m_url)));
+ auto fetchOptions = options;
+ fetchOptions.mode = FetchOptions::Mode::SameOrigin;
+ CachedResourceRequest request(ResourceRequest(loader.document()->completeURL(m_url)), fetchOptions);
request.setInitiator(cachedResourceRequestInitiators().css);
- m_document = loader->requestSVGDocument(request);
+ m_document = loader.requestSVGDocument(WTFMove(request));
if (m_document)
- m_document->addClient(this);
+ m_document->addClient(*this);
m_loadRequested = true;
}
}
-
-#endif
diff --git a/Source/WebCore/loader/cache/CachedSVGDocumentReference.h b/Source/WebCore/loader/cache/CachedSVGDocumentReference.h
index a297abcf8..70cb1afa4 100644
--- a/Source/WebCore/loader/cache/CachedSVGDocumentReference.h
+++ b/Source/WebCore/loader/cache/CachedSVGDocumentReference.h
@@ -23,10 +23,8 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CachedSVGDocumentReference_h
-#define CachedSVGDocumentReference_h
+#pragma once
-#if ENABLE(SVG) && ENABLE(CSS_FILTERS)
#include "CachedResourceHandle.h"
#include "CachedSVGDocumentClient.h"
#include <wtf/text/WTFString.h>
@@ -35,6 +33,7 @@ namespace WebCore {
class CachedSVGDocument;
class CachedResourceLoader;
+struct ResourceLoaderOptions;
class CachedSVGDocumentReference : public CachedSVGDocumentClient {
public:
@@ -42,7 +41,7 @@ public:
virtual ~CachedSVGDocumentReference();
- void load(CachedResourceLoader*);
+ void load(CachedResourceLoader&, const ResourceLoaderOptions&);
bool loadRequested() const { return m_loadRequested; }
CachedSVGDocument* document() { return m_document.get(); }
@@ -53,8 +52,4 @@ private:
bool m_loadRequested;
};
-};
-
-#endif // ENABLE(SVG) && ENABLE(CSS_FILTERS)
-
-#endif // CachedSVGDocumentReference_h
+} // namespace WebCore
diff --git a/Source/WebCore/loader/cache/CachedSVGFont.cpp b/Source/WebCore/loader/cache/CachedSVGFont.cpp
new file mode 100644
index 000000000..a06641256
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedSVGFont.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 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 "CachedSVGFont.h"
+
+#if ENABLE(SVG_FONTS)
+
+#include "FontDescription.h"
+#include "FontPlatformData.h"
+#include "NoEventDispatchAssertion.h"
+#include "SVGDocument.h"
+#include "SVGFontElement.h"
+#include "SVGFontFaceElement.h"
+#include "SharedBuffer.h"
+#include "TextResourceDecoder.h"
+#include "TypedElementDescendantIterator.h"
+#include "SVGToOTFFontConversion.h"
+
+#if USE(DIRECT2D)
+#include <dwrite.h>
+#endif
+
+namespace WebCore {
+
+CachedSVGFont::CachedSVGFont(CachedResourceRequest&& request, SessionID sessionID)
+ : CachedFont(WTFMove(request), sessionID, SVGFontResource)
+ , m_externalSVGFontElement(nullptr)
+{
+}
+
+RefPtr<Font> CachedSVGFont::createFont(const FontDescription& fontDescription, const AtomicString& remoteURI, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings)
+{
+ if (firstFontFace(remoteURI))
+ return CachedFont::createFont(fontDescription, remoteURI, syntheticBold, syntheticItalic, fontFaceFeatures, fontFaceVariantSettings);
+ return nullptr;
+}
+
+FontPlatformData CachedSVGFont::platformDataFromCustomData(const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings)
+{
+ if (m_externalSVGDocument)
+ return FontPlatformData(fontDescription.computedPixelSize(), bold, italic);
+ return CachedFont::platformDataFromCustomData(fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings);
+}
+
+bool CachedSVGFont::ensureCustomFontData(const AtomicString& remoteURI)
+{
+ if (!m_externalSVGDocument && !errorOccurred() && !isLoading() && m_data) {
+ bool sawError = false;
+ {
+ // We may get here during render tree updates when events are forbidden.
+ // Frameless document can't run scripts or call back to the client so this is safe.
+ m_externalSVGDocument = SVGDocument::create(nullptr, URL());
+ auto decoder = TextResourceDecoder::create("application/xml");
+
+ NoEventDispatchAssertion::EventAllowedScope allowedScope(*m_externalSVGDocument);
+
+ m_externalSVGDocument->setContent(decoder->decodeAndFlush(m_data->data(), m_data->size()));
+ sawError = decoder->sawError();
+ }
+
+ if (sawError)
+ m_externalSVGDocument = nullptr;
+ if (m_externalSVGDocument)
+ maybeInitializeExternalSVGFontElement(remoteURI);
+ if (!m_externalSVGFontElement)
+ return false;
+ if (auto convertedFont = convertSVGToOTFFont(*m_externalSVGFontElement))
+ m_convertedFont = SharedBuffer::adoptVector(convertedFont.value());
+ else {
+ m_externalSVGDocument = nullptr;
+ m_externalSVGFontElement = nullptr;
+ return false;
+ }
+ }
+
+ return m_externalSVGDocument && CachedFont::ensureCustomFontData(m_convertedFont.get());
+}
+
+SVGFontElement* CachedSVGFont::getSVGFontById(const String& fontName) const
+{
+ ASSERT(m_externalSVGDocument);
+ auto elements = descendantsOfType<SVGFontElement>(*m_externalSVGDocument);
+
+ if (fontName.isEmpty())
+ return elements.first();
+
+ for (auto& element : elements) {
+ if (element.getIdAttribute() == fontName)
+ return &element;
+ }
+ return nullptr;
+}
+
+SVGFontElement* CachedSVGFont::maybeInitializeExternalSVGFontElement(const AtomicString& remoteURI)
+{
+ if (m_externalSVGFontElement)
+ return m_externalSVGFontElement;
+ String fragmentIdentifier;
+ size_t start = remoteURI.find('#');
+ if (start != notFound)
+ fragmentIdentifier = remoteURI.string().substring(start + 1);
+ m_externalSVGFontElement = getSVGFontById(fragmentIdentifier);
+ return m_externalSVGFontElement;
+}
+
+SVGFontFaceElement* CachedSVGFont::firstFontFace(const AtomicString& remoteURI)
+{
+ if (!maybeInitializeExternalSVGFontElement(remoteURI))
+ return nullptr;
+
+ if (auto* firstFontFace = childrenOfType<SVGFontFaceElement>(*m_externalSVGFontElement).first())
+ return firstFontFace;
+ return nullptr;
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/loader/cache/CachedSVGFont.h b/Source/WebCore/loader/cache/CachedSVGFont.h
new file mode 100644
index 000000000..93ed7a0c4
--- /dev/null
+++ b/Source/WebCore/loader/cache/CachedSVGFont.h
@@ -0,0 +1,61 @@
+/*
+ * 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 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.
+ */
+
+#pragma once
+
+#if ENABLE(SVG_FONTS)
+
+#include "CachedFont.h"
+
+namespace WebCore {
+
+class SVGFontFaceElement;
+
+class CachedSVGFont final : public CachedFont {
+public:
+ CachedSVGFont(CachedResourceRequest&&, SessionID);
+
+ bool ensureCustomFontData(const AtomicString& remoteURI) override;
+
+ RefPtr<Font> createFont(const FontDescription&, const AtomicString& remoteURI, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings&, const FontVariantSettings&) override;
+
+private:
+ FontPlatformData platformDataFromCustomData(const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&);
+
+ SVGFontElement* getSVGFontById(const String&) const;
+
+ SVGFontElement* maybeInitializeExternalSVGFontElement(const AtomicString& remoteURI);
+ SVGFontFaceElement* firstFontFace(const AtomicString& remoteURI);
+
+ RefPtr<SharedBuffer> m_convertedFont;
+ RefPtr<SVGDocument> m_externalSVGDocument;
+ SVGFontElement* m_externalSVGFontElement;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedSVGFont, CachedResource::SVGFontResource)
+
+#endif // ENABLE(SVG_FONTS)
diff --git a/Source/WebCore/loader/cache/CachedScript.cpp b/Source/WebCore/loader/cache/CachedScript.cpp
index 48facd7bc..84de38ebe 100644
--- a/Source/WebCore/loader/cache/CachedScript.cpp
+++ b/Source/WebCore/loader/cache/CachedScript.cpp
@@ -29,24 +29,20 @@
#include "CachedResourceClient.h"
#include "CachedResourceClientWalker.h"
+#include "CachedResourceRequest.h"
+#include "HTTPHeaderNames.h"
#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
-#include "MemoryCache.h"
-#include "ResourceBuffer.h"
#include "RuntimeApplicationChecks.h"
+#include "SharedBuffer.h"
#include "TextResourceDecoder.h"
-#include <wtf/Vector.h>
namespace WebCore {
-CachedScript::CachedScript(const ResourceRequest& resourceRequest, const String& charset)
- : CachedResource(resourceRequest, Script)
- , m_decoder(TextResourceDecoder::create(ASCIILiteral("application/javascript"), charset))
+CachedScript::CachedScript(CachedResourceRequest&& request, SessionID sessionID)
+ : CachedResource(WTFMove(request), Script, sessionID)
+ , m_decoder(TextResourceDecoder::create(ASCIILiteral("application/javascript"), request.charset()))
{
- // 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()
@@ -65,27 +61,55 @@ String CachedScript::encoding() const
String CachedScript::mimeType() const
{
- return extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type")).lower();
+ return extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType)).convertToASCIILowercase();
}
-const String& CachedScript::script()
+StringView CachedScript::script()
{
- ASSERT(!isPurgeable());
+ if (!m_data)
+ return { };
- if (!m_script && m_data) {
- m_script = m_decoder->decode(m_data->data(), encodedSize());
- m_script.append(m_decoder->flush());
+ if (m_decodingState == NeverDecoded
+ && TextEncoding(encoding()).isByteBasedEncoding()
+ && m_data->size()
+ && charactersAreAllASCII(reinterpret_cast<const LChar*>(m_data->data()), m_data->size())) {
+
+ m_decodingState = DataAndDecodedStringHaveSameBytes;
+
+ // If the encoded and decoded data are the same, there is no decoded data cost!
+ setDecodedSize(0);
+ m_decodedDataDeletionTimer.stop();
+
+ m_scriptHash = StringHasher::computeHashAndMaskTop8Bits(reinterpret_cast<const LChar*>(m_data->data()), m_data->size());
+ }
+
+ if (m_decodingState == DataAndDecodedStringHaveSameBytes)
+ return { reinterpret_cast<const LChar*>(m_data->data()), m_data->size() };
+
+ if (!m_script) {
+ m_script = m_decoder->decodeAndFlush(m_data->data(), encodedSize());
+ ASSERT(!m_scriptHash || m_scriptHash == m_script.impl()->hash());
+ if (m_decodingState == NeverDecoded)
+ m_scriptHash = m_script.impl()->hash();
+ m_decodingState = DataAndDecodedStringHaveDifferentBytes;
setDecodedSize(m_script.sizeInBytes());
}
+
m_decodedDataDeletionTimer.restart();
-
return m_script;
}
-void CachedScript::finishLoading(ResourceBuffer* data)
+unsigned CachedScript::scriptHash()
+{
+ if (m_decodingState == NeverDecoded)
+ script();
+ return m_scriptHash;
+}
+
+void CachedScript::finishLoading(SharedBuffer* data)
{
m_data = data;
- setEncodedSize(m_data.get() ? m_data->size() : 0);
+ setEncodedSize(data ? data->size() : 0);
CachedResource::finishLoading(data);
}
@@ -93,27 +117,40 @@ void CachedScript::destroyDecodedData()
{
m_script = String();
setDecodedSize(0);
- if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable())
- makePurgeable(true);
+}
+
+void CachedScript::setBodyDataFrom(const CachedResource& resource)
+{
+ ASSERT(resource.type() == type());
+ auto& script = static_cast<const CachedScript&>(resource);
+
+ CachedResource::setBodyDataFrom(resource);
+
+ m_script = script.m_script;
+ m_scriptHash = script.m_scriptHash;
+ m_decodingState = script.m_decodingState;
+ m_decoder = script.m_decoder;
}
#if ENABLE(NOSNIFF)
bool CachedScript::mimeTypeAllowedByNosniff() const
{
- return !parseContentTypeOptionsHeader(m_response.httpHeaderField("X-Content-Type-Options")) == ContentTypeOptionsNosniff || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType());
+ return parseContentTypeOptionsHeader(m_response.httpHeaderField(HTTPHeaderName::XContentTypeOptions)) != ContentTypeOptionsNosniff || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType());
}
#endif
bool CachedScript::shouldIgnoreHTTPStatusCodeErrors() const
{
+#if PLATFORM(MAC)
// This is a workaround for <rdar://problem/13916291>
// REGRESSION (r119759): Adobe Flash Player "smaller" installer relies on the incorrect firing
// of a load event and needs an app-specific hack for compatibility.
// The installer in question tries to load .js file that doesn't exist, causing the server to
// return a 404 response. Normally, this would trigger an error event to be dispatched, but the
// installer expects a load event instead so we work around it here.
- if (applicationIsSolidStateNetworksDownloader())
+ if (MacApplication::isSolidStateNetworksDownloader())
return true;
+#endif
return CachedResource::shouldIgnoreHTTPStatusCodeErrors();
}
diff --git a/Source/WebCore/loader/cache/CachedScript.h b/Source/WebCore/loader/cache/CachedScript.h
index 80b073c2a..f8898d378 100644
--- a/Source/WebCore/loader/cache/CachedScript.h
+++ b/Source/WebCore/loader/cache/CachedScript.h
@@ -23,44 +23,51 @@
pages from the web. It has a memory cache for these objects.
*/
-#ifndef CachedScript_h
-#define CachedScript_h
+#pragma once
#include "CachedResource.h"
namespace WebCore {
- class CachedResourceLoader;
- class TextResourceDecoder;
+class TextResourceDecoder;
- class CachedScript final : public CachedResource {
- public:
- CachedScript(const ResourceRequest&, const String& charset);
- virtual ~CachedScript();
+class CachedScript final : public CachedResource {
+public:
+ CachedScript(CachedResourceRequest&&, SessionID);
+ virtual ~CachedScript();
- const String& script();
+ StringView script();
+ unsigned scriptHash();
- String mimeType() const;
+ String mimeType() const;
#if ENABLE(NOSNIFF)
- bool mimeTypeAllowedByNosniff() const;
+ bool mimeTypeAllowedByNosniff() const;
#endif
- private:
- virtual PurgePriority purgePriority() const override { return PurgeLast; }
- virtual bool mayTryReplaceEncodedData() const override { return true; }
+private:
+ bool mayTryReplaceEncodedData() const final { return true; }
- virtual bool shouldIgnoreHTTPStatusCodeErrors() const override;
+ bool shouldIgnoreHTTPStatusCodeErrors() const final;
- virtual void setEncoding(const String&) override;
- virtual String encoding() const override;
- virtual void finishLoading(ResourceBuffer*) override;
+ void setEncoding(const String&) final;
+ String encoding() const final;
+ const TextResourceDecoder* textResourceDecoder() const final { return m_decoder.get(); }
+ void finishLoading(SharedBuffer*) final;
- virtual void destroyDecodedData() override;
+ void destroyDecodedData() final;
- String m_script;
- RefPtr<TextResourceDecoder> m_decoder;
- };
-}
+ void setBodyDataFrom(const CachedResource&) final;
-#endif
+ String m_script;
+ unsigned m_scriptHash { 0 };
+
+ enum DecodingState { NeverDecoded, DataAndDecodedStringHaveSameBytes, DataAndDecodedStringHaveDifferentBytes };
+ DecodingState m_decodingState { NeverDecoded };
+
+ RefPtr<TextResourceDecoder> m_decoder;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedScript, CachedResource::Script)
diff --git a/Source/WebCore/loader/cache/CachedStyleSheetClient.h b/Source/WebCore/loader/cache/CachedStyleSheetClient.h
index eed2d3a3e..c2f187389 100644
--- a/Source/WebCore/loader/cache/CachedStyleSheetClient.h
+++ b/Source/WebCore/loader/cache/CachedStyleSheetClient.h
@@ -23,8 +23,7 @@
pages from the web. It has a memory cache for these objects.
*/
-#ifndef CachedStyleSheetClient_h
-#define CachedStyleSheetClient_h
+#pragma once
#include "CachedResourceClient.h"
#include <wtf/Forward.h>
@@ -38,11 +37,9 @@ class CachedStyleSheetClient : public CachedResourceClient {
public:
virtual ~CachedStyleSheetClient() { }
static CachedResourceClientType expectedType() { return StyleSheetType; }
- virtual CachedResourceClientType resourceClientType() const override { return expectedType(); }
+ CachedResourceClientType resourceClientType() const override { return expectedType(); }
virtual void setCSSStyleSheet(const String& /* href */, const URL& /* baseURL */, const String& /* charset */, const CachedCSSStyleSheet*) { }
virtual void setXSLStyleSheet(const String& /* href */, const URL& /* baseURL */, const String& /* sheet */) { }
};
} // namespace WebCore
-
-#endif // CachedStyleSheetClient_h
diff --git a/Source/WebCore/loader/cache/CachedTextTrack.cpp b/Source/WebCore/loader/cache/CachedTextTrack.cpp
index 94974a591..1d82bd71b 100644
--- a/Source/WebCore/loader/cache/CachedTextTrack.cpp
+++ b/Source/WebCore/loader/cache/CachedTextTrack.cpp
@@ -32,36 +32,36 @@
#include "CachedResourceClient.h"
#include "CachedResourceClientWalker.h"
#include "CachedResourceLoader.h"
-#include "ResourceBuffer.h"
#include "SharedBuffer.h"
#include "TextResourceDecoder.h"
-#include <wtf/Vector.h>
namespace WebCore {
-CachedTextTrack::CachedTextTrack(const ResourceRequest& resourceRequest)
- : CachedResource(resourceRequest, TextTrackResource)
+CachedTextTrack::CachedTextTrack(CachedResourceRequest&& request, SessionID sessionID)
+ : CachedResource(WTFMove(request), TextTrackResource, sessionID)
{
}
-CachedTextTrack::~CachedTextTrack()
+void CachedTextTrack::updateData(SharedBuffer* data)
{
-}
-
-void CachedTextTrack::addDataBuffer(ResourceBuffer* data)
-{
- ASSERT(m_options.dataBufferingPolicy == BufferData);
+ ASSERT(dataBufferingPolicy() == BufferData);
m_data = data;
- setEncodedSize(m_data.get() ? m_data->size() : 0);
+ setEncodedSize(data ? data->size() : 0);
CachedResourceClientWalker<CachedResourceClient> walker(m_clients);
- while (CachedResourceClient *client = walker.next())
- client->deprecatedDidReceiveCachedResource(this);
+ while (CachedResourceClient* client = walker.next())
+ client->deprecatedDidReceiveCachedResource(*this);
+}
+
+void CachedTextTrack::addDataBuffer(SharedBuffer& data)
+{
+ updateData(&data);
+ CachedResource::addDataBuffer(data);
}
-void CachedTextTrack::finishLoading(ResourceBuffer* data)
+void CachedTextTrack::finishLoading(SharedBuffer* data)
{
- addDataBuffer(data);
+ updateData(data);
CachedResource::finishLoading(data);
}
diff --git a/Source/WebCore/loader/cache/CachedTextTrack.h b/Source/WebCore/loader/cache/CachedTextTrack.h
index 0df56e2d3..adf544a1b 100644
--- a/Source/WebCore/loader/cache/CachedTextTrack.h
+++ b/Source/WebCore/loader/cache/CachedTextTrack.h
@@ -23,28 +23,29 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CachedTextTrack_h
-#define CachedTextTrack_h
+#pragma once
#if ENABLE(VIDEO_TRACK)
#include "CachedResource.h"
-#include "FontOrientation.h"
+#include "TextFlags.h"
namespace WebCore {
class CachedTextTrack final : public CachedResource {
public:
- CachedTextTrack(const ResourceRequest&);
- virtual ~CachedTextTrack();
+ CachedTextTrack(CachedResourceRequest&&, SessionID);
private:
- virtual bool mayTryReplaceEncodedData() const override { return true; }
- virtual void addDataBuffer(ResourceBuffer*) override;
- virtual void finishLoading(ResourceBuffer*) override;
+ bool mayTryReplaceEncodedData() const override { return true; }
+ void addDataBuffer(SharedBuffer&) override;
+ void finishLoading(SharedBuffer*) override;
+
+ void updateData(SharedBuffer*);
};
-}
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedTextTrack, CachedResource::TextTrackResource)
-#endif
-#endif
+#endif // ENABLE(VIDEO_TRACK)
diff --git a/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp b/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp
index eb6c8e1fe..c9f0dc87a 100644
--- a/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp
+++ b/Source/WebCore/loader/cache/CachedXSLStyleSheet.cpp
@@ -29,28 +29,28 @@
#include "CachedResourceClientWalker.h"
#include "CachedStyleSheetClient.h"
-#include "ResourceBuffer.h"
+#include "SharedBuffer.h"
#include "TextResourceDecoder.h"
-#include <wtf/Vector.h>
namespace WebCore {
#if ENABLE(XSLT)
-CachedXSLStyleSheet::CachedXSLStyleSheet(const ResourceRequest& resourceRequest)
- : CachedResource(resourceRequest, XSLStyleSheet)
+CachedXSLStyleSheet::CachedXSLStyleSheet(CachedResourceRequest&& request, SessionID sessionID)
+ : CachedResource(WTFMove(request), XSLStyleSheet, sessionID)
, 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)
-{
- ASSERT(c->resourceClientType() == CachedStyleSheetClient::expectedType());
+CachedXSLStyleSheet::~CachedXSLStyleSheet()
+{
+}
+
+void CachedXSLStyleSheet::didAddClient(CachedResourceClient& client)
+{
+ ASSERT(client.resourceClientType() == CachedStyleSheetClient::expectedType());
if (!isLoading())
- static_cast<CachedStyleSheetClient*>(c)->setXSLStyleSheet(m_resourceRequest.url(), m_response.url(), m_sheet);
+ static_cast<CachedStyleSheetClient&>(client).setXSLStyleSheet(m_resourceRequest.url(), m_response.url(), m_sheet);
}
void CachedXSLStyleSheet::setEncoding(const String& chs)
@@ -63,14 +63,12 @@ String CachedXSLStyleSheet::encoding() const
return m_decoder->encoding().name();
}
-void CachedXSLStyleSheet::finishLoading(ResourceBuffer* data)
+void CachedXSLStyleSheet::finishLoading(SharedBuffer* data)
{
m_data = data;
- setEncodedSize(m_data.get() ? m_data->size() : 0);
- if (m_data.get()) {
- m_sheet = m_decoder->decode(m_data->data(), encodedSize());
- m_sheet.append(m_decoder->flush());
- }
+ setEncodedSize(data ? data->size() : 0);
+ if (data)
+ m_sheet = m_decoder->decodeAndFlush(data->data(), encodedSize());
setLoading(false);
checkNotify();
}
diff --git a/Source/WebCore/loader/cache/CachedXSLStyleSheet.h b/Source/WebCore/loader/cache/CachedXSLStyleSheet.h
index 307bda691..3ef0f600e 100644
--- a/Source/WebCore/loader/cache/CachedXSLStyleSheet.h
+++ b/Source/WebCore/loader/cache/CachedXSLStyleSheet.h
@@ -23,42 +23,38 @@
pages from the web. It has a memory cache for these objects.
*/
-#ifndef CachedXSLStyleSheet_h
-#define CachedXSLStyleSheet_h
+#pragma once
+
+#if ENABLE(XSLT)
#include "CachedResource.h"
-#include <wtf/Vector.h>
namespace WebCore {
- class CachedResourceLoader;
- class TextResourceDecoder;
-
-#if ENABLE(XSLT)
- class CachedXSLStyleSheet final : public CachedResource {
- public:
- CachedXSLStyleSheet(const ResourceRequest&);
-
- const String& sheet() const { return m_sheet; }
-
- protected:
- virtual void checkNotify() override;
+class TextResourceDecoder;
- String m_sheet;
- RefPtr<TextResourceDecoder> m_decoder;
+class CachedXSLStyleSheet final : public CachedResource {
+public:
+ CachedXSLStyleSheet(CachedResourceRequest&&, SessionID);
+ virtual ~CachedXSLStyleSheet();
- private:
- virtual bool mayTryReplaceEncodedData() const override { return true; }
+ const String& sheet() const { return m_sheet; }
- virtual void didAddClient(CachedResourceClient*) override;
+private:
+ void checkNotify() final;
+ bool mayTryReplaceEncodedData() const final { return true; }
+ void didAddClient(CachedResourceClient&) final;
+ void setEncoding(const String&) final;
+ String encoding() const final;
+ const TextResourceDecoder* textResourceDecoder() const final { return m_decoder.get(); }
+ void finishLoading(SharedBuffer*) final;
- virtual void setEncoding(const String&) override;
- virtual String encoding() const override;
- virtual void finishLoading(ResourceBuffer*) override;
- };
+ String m_sheet;
+ RefPtr<TextResourceDecoder> m_decoder;
+};
-#endif
+} // namespace WebCore
-}
+SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(CachedXSLStyleSheet, CachedResource::XSLStyleSheet)
-#endif
+#endif // ENABLE(XSLT)
diff --git a/Source/WebCore/loader/cache/MemoryCache.cpp b/Source/WebCore/loader/cache/MemoryCache.cpp
index 652422cf6..75ee84bb7 100644
--- a/Source/WebCore/loader/cache/MemoryCache.cpp
+++ b/Source/WebCore/loader/cache/MemoryCache.cpp
@@ -25,9 +25,9 @@
#include "BitmapImage.h"
#include "CachedImage.h"
+#include "CachedImageClient.h"
#include "CachedResource.h"
#include "CachedResourceHandle.h"
-#include "CrossThreadTask.h"
#include "Document.h"
#include "FrameLoader.h"
#include "FrameLoaderTypes.h"
@@ -35,166 +35,156 @@
#include "Image.h"
#include "Logging.h"
#include "PublicSuffix.h"
-#include "SecurityOrigin.h"
-#include "SecurityOriginHash.h"
+#include "SharedBuffer.h"
#include "WorkerGlobalScope.h"
#include "WorkerLoaderProxy.h"
#include "WorkerThread.h"
#include <stdio.h>
#include <wtf/CurrentTime.h>
#include <wtf/MathExtras.h>
-#include <wtf/TemporaryChange.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/SetForScope.h>
#include <wtf/text/CString.h>
-#if ENABLE(DISK_IMAGE_CACHE)
-#include "DiskImageCacheIOS.h"
-#include "ResourceBuffer.h"
-#endif
-
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;
+static const auto defaultDecodedDataDeletionInterval = std::chrono::seconds { 0 };
-MemoryCache* memoryCache()
+MemoryCache& MemoryCache::singleton()
{
- static MemoryCache* staticCache = new MemoryCache;
ASSERT(WTF::isMainThread());
-
- return staticCache;
+ static NeverDestroyed<MemoryCache> memoryCache;
+ return memoryCache;
}
MemoryCache::MemoryCache()
: m_disabled(false)
- , m_pruneEnabled(true)
, m_inPruneResources(false)
, m_capacity(cDefaultCacheCapacity)
, m_minDeadCapacity(0)
, m_maxDeadCapacity(cDefaultCacheCapacity)
- , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval)
+ , m_deadDecodedDataDeletionInterval(defaultDecodedDataDeletionInterval)
, m_liveSize(0)
, m_deadSize(0)
+ , m_pruneTimer(*this, &MemoryCache::prune)
{
+ static_assert(sizeof(long long) > sizeof(unsigned), "Numerical overflow can happen when adjusting the size of the cached memory.");
}
-URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL)
+auto MemoryCache::sessionResourceMap(SessionID sessionID) const -> CachedResourceMap*
+{
+ ASSERT(sessionID.isValid());
+ return m_sessionResources.get(sessionID);
+}
+
+auto MemoryCache::ensureSessionResourceMap(SessionID sessionID) -> CachedResourceMap&
+{
+ ASSERT(sessionID.isValid());
+ auto& map = m_sessionResources.add(sessionID, nullptr).iterator->value;
+ if (!map)
+ map = std::make_unique<CachedResourceMap>();
+ return *map;
+}
+
+bool MemoryCache::shouldRemoveFragmentIdentifier(const URL& originalURL)
{
if (!originalURL.hasFragmentIdentifier())
- return originalURL;
+ return false;
// Strip away fragment identifier from HTTP URLs.
- // Data URLs must be unmodified. For file and custom URLs clients may expect resources
+ // Data URLs must be unmodified. For file and custom URLs clients may expect resources
// to be unique even when they differ by the fragment identifier only.
- if (!originalURL.protocolIsInHTTPFamily())
+ return originalURL.protocolIsInHTTPFamily();
+}
+
+URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL)
+{
+ if (!shouldRemoveFragmentIdentifier(originalURL))
return originalURL;
URL url = originalURL;
url.removeFragmentIdentifier();
return url;
}
-bool MemoryCache::add(CachedResource* resource)
+bool MemoryCache::add(CachedResource& resource)
{
if (disabled())
return false;
ASSERT(WTF::isMainThread());
-#if ENABLE(CACHE_PARTITIONING)
- CachedResourceItem* originMap = m_resources.get(resource->url());
- if (!originMap) {
- originMap = new CachedResourceItem;
- m_resources.set(resource->url(), adoptPtr(originMap));
- }
- originMap->set(resource->cachePartition(), resource);
-#else
- m_resources.set(resource->url(), resource);
-#endif
- resource->setInCache(true);
+ auto key = std::make_pair(resource.url(), resource.cachePartition());
+
+ ensureSessionResourceMap(resource.sessionID()).set(key, &resource);
+ resource.setInCache(true);
resourceAccessed(resource);
- LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource->url().string().latin1().data(), resource);
+ LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource.url().string().latin1().data(), &resource);
return true;
}
-void MemoryCache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response)
+void MemoryCache::revalidationSucceeded(CachedResource& revalidatingResource, const ResourceResponse& response)
{
- CachedResource* resource = revalidatingResource->resourceToRevalidate();
- ASSERT(resource);
- ASSERT(!resource->inCache());
- ASSERT(resource->isLoaded());
- ASSERT(revalidatingResource->inCache());
+ ASSERT(revalidatingResource.resourceToRevalidate());
+ CachedResource& resource = *revalidatingResource.resourceToRevalidate();
+ ASSERT(!resource.inCache());
+ ASSERT(resource.isLoaded());
- // Calling evict() can potentially delete revalidatingResource, which we use
+ // Calling remove() can potentially delete revalidatingResource, which we use
// below. This mustn't be the case since revalidation means it is loaded
// and so canDelete() is false.
- ASSERT(!revalidatingResource->canDelete());
+ ASSERT(!revalidatingResource.canDelete());
- evict(revalidatingResource);
+ remove(revalidatingResource);
-#if ENABLE(CACHE_PARTITIONING)
- ASSERT(!m_resources.get(resource->url()) || !m_resources.get(resource->url())->get(resource->cachePartition()));
- CachedResourceItem* originMap = m_resources.get(resource->url());
- if (!originMap) {
- originMap = new CachedResourceItem;
- m_resources.set(resource->url(), adoptPtr(originMap));
- }
- originMap->set(resource->cachePartition(), resource);
-#else
- ASSERT(!m_resources.get(resource->url()));
- m_resources.set(resource->url(), resource);
-#endif
- resource->setInCache(true);
- resource->updateResponseAfterRevalidation(response);
+ auto& resources = ensureSessionResourceMap(resource.sessionID());
+ auto key = std::make_pair(resource.url(), resource.cachePartition());
+
+ ASSERT(!resources.get(key));
+ resources.set(key, &resource);
+ resource.setInCache(true);
+ resource.updateResponseAfterRevalidation(response);
insertInLRUList(resource);
- int delta = resource->size();
- if (resource->decodedSize() && resource->hasClients())
+ long long delta = resource.size();
+ if (resource.decodedSize() && resource.hasClients())
insertInLiveDecodedResourcesList(resource);
if (delta)
- adjustSize(resource->hasClients(), delta);
-
- revalidatingResource->switchClientsToRevalidatedResource();
- ASSERT(!revalidatingResource->m_deleted);
+ adjustSize(resource.hasClients(), delta);
+
+ revalidatingResource.switchClientsToRevalidatedResource();
+ ASSERT(!revalidatingResource.m_deleted);
// this deletes the revalidating resource
- revalidatingResource->clearResourceToRevalidate();
+ revalidatingResource.clearResourceToRevalidate();
}
-void MemoryCache::revalidationFailed(CachedResource* revalidatingResource)
+void MemoryCache::revalidationFailed(CachedResource& revalidatingResource)
{
ASSERT(WTF::isMainThread());
- LOG(ResourceLoading, "Revalidation failed for %p", revalidatingResource);
- ASSERT(revalidatingResource->resourceToRevalidate());
- revalidatingResource->clearResourceToRevalidate();
+ LOG(ResourceLoading, "Revalidation failed for %p", &revalidatingResource);
+ ASSERT(revalidatingResource.resourceToRevalidate());
+ revalidatingResource.clearResourceToRevalidate();
}
-CachedResource* MemoryCache::resourceForURL(const URL& resourceURL)
+CachedResource* MemoryCache::resourceForRequest(const ResourceRequest& request, SessionID sessionID)
{
- return resourceForRequest(ResourceRequest(resourceURL));
+ // FIXME: Change all clients to make sure HTTP(s) URLs have no fragment identifiers before calling here.
+ // CachedResourceLoader is now doing this. Add an assertion once all other clients are doing it too.
+ auto* resources = sessionResourceMap(sessionID);
+ if (!resources)
+ return nullptr;
+ return resourceForRequestImpl(request, *resources);
}
-CachedResource* MemoryCache::resourceForRequest(const ResourceRequest& request)
+CachedResource* MemoryCache::resourceForRequestImpl(const ResourceRequest& request, CachedResourceMap& resources)
{
ASSERT(WTF::isMainThread());
URL url = removeFragmentIdentifierIfNeeded(request.url());
-#if ENABLE(CACHE_PARTITIONING)
- CachedResourceItem* item = m_resources.get(url);
- CachedResource* resource = 0;
- if (item)
- resource = item->get(request.cachePartition());
-#else
- CachedResource* resource = m_resources.get(url);
-#endif
- 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;
+
+ auto key = std::make_pair(url, request.cachePartition());
+ return resources.get(key);
}
unsigned MemoryCache::deadCapacity() const
@@ -212,51 +202,46 @@ unsigned MemoryCache::liveCapacity() const
return m_capacity - deadCapacity();
}
-#if USE(CG)
-// FIXME: Remove the USE(CG) once we either make NativeImagePtr a smart pointer on all platforms or
-// remove the usage of CFRetain() in MemoryCache::addImageToCache() so as to make the code platform-independent.
-bool MemoryCache::addImageToCache(NativeImagePtr image, const URL& url, const String& cachePartition)
+static CachedImageClient& dummyCachedImageClient()
+{
+ static NeverDestroyed<CachedImageClient> client;
+ return client;
+}
+
+bool MemoryCache::addImageToCache(NativeImagePtr&& image, const URL& url, const String& domainForCachePartition)
{
ASSERT(image);
- removeImageFromCache(url, cachePartition); // Remove cache entry if it already exists.
+ SessionID sessionID = SessionID::defaultSessionID();
+ removeImageFromCache(url, domainForCachePartition); // Remove cache entry if it already exists.
- RefPtr<BitmapImage> bitmapImage = BitmapImage::create(image, nullptr);
+ RefPtr<BitmapImage> bitmapImage = BitmapImage::create(WTFMove(image), nullptr);
if (!bitmapImage)
return false;
- CachedImageManual* cachedImage = new CachedImageManual(url, bitmapImage.get());
- if (!cachedImage)
- return false;
+ auto cachedImage = std::make_unique<CachedImage>(url, bitmapImage.get(), sessionID);
- // Actual release of the CGImageRef is done in BitmapImage.
- CFRetain(image);
- cachedImage->addFakeClient();
+ cachedImage->addClient(dummyCachedImageClient());
cachedImage->setDecodedSize(bitmapImage->decodedSize());
-#if ENABLE(CACHE_PARTITIONING)
- cachedImage->resourceRequest().setCachePartition(cachePartition);
-#endif
- add(cachedImage);
- return true;
+ cachedImage->resourceRequest().setDomainForCachePartition(domainForCachePartition);
+
+ return add(*cachedImage.release());
}
-void MemoryCache::removeImageFromCache(const URL& url, const String& cachePartition)
+void MemoryCache::removeImageFromCache(const URL& url, const String& domainForCachePartition)
{
-#if ENABLE(CACHE_PARTITIONING)
- CachedResource* resource;
- if (CachedResourceItem* item = m_resources.get(url))
- resource = item->get(ResourceRequest::partitionName(cachePartition));
- else
- resource = nullptr;
-#else
- UNUSED_PARAM(cachePartition);
- CachedResource* resource = m_resources.get(url);
-#endif
+ auto* resources = sessionResourceMap(SessionID::defaultSessionID());
+ if (!resources)
+ return;
+
+ auto key = std::make_pair(url, ResourceRequest::partitionName(domainForCachePartition));
+
+ CachedResource* resource = resources->get(key);
if (!resource)
return;
// A resource exists and is not a manually cached image, so just remove it.
- if (!resource->isImage() || !static_cast<CachedImage*>(resource)->isManual()) {
- evict(resource);
+ if (!is<CachedImage>(*resource) || !downcast<CachedImage>(*resource).isManuallyCached()) {
+ remove(*resource);
return;
}
@@ -265,15 +250,11 @@ void MemoryCache::removeImageFromCache(const URL& url, const String& cachePartit
// dead resources are pruned. That might be immediately since
// removing the last client triggers a MemoryCache::prune, so the
// resource may be deleted after this call.
- static_cast<CachedImageManual*>(resource)->removeFakeClient();
+ downcast<CachedImage>(*resource).removeClient(dummyCachedImageClient());
}
-#endif
void MemoryCache::pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources)
{
- if (!m_pruneEnabled)
- return;
-
unsigned capacity = shouldDestroyDecodedDataForAllLiveResources ? 0 : liveCapacity();
if (capacity && m_liveSize <= capacity)
return;
@@ -283,41 +264,67 @@ void MemoryCache::pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveReso
pruneLiveResourcesToSize(targetSize, shouldDestroyDecodedDataForAllLiveResources);
}
-void MemoryCache::pruneLiveResourcesToPercentage(float prunePercentage)
+void MemoryCache::forEachResource(const std::function<void(CachedResource&)>& function)
{
- if (!m_pruneEnabled)
- return;
+ for (auto& unprotectedLRUList : m_allResources) {
+ Vector<CachedResourceHandle<CachedResource>> lruList;
+ copyToVector(*unprotectedLRUList, lruList);
+ for (auto& resource : lruList)
+ function(*resource);
+ }
+}
- if (prunePercentage < 0.0f || prunePercentage > 0.95f)
+void MemoryCache::forEachSessionResource(SessionID sessionID, const std::function<void (CachedResource&)>& function)
+{
+ auto it = m_sessionResources.find(sessionID);
+ if (it == m_sessionResources.end())
return;
- unsigned currentSize = m_liveSize + m_deadSize;
- unsigned targetSize = static_cast<unsigned>(currentSize * prunePercentage);
+ Vector<CachedResourceHandle<CachedResource>> resourcesForSession;
+ copyValuesToVector(*it->value, resourcesForSession);
+
+ for (auto& resource : resourcesForSession)
+ function(*resource);
+}
- pruneLiveResourcesToSize(targetSize);
+void MemoryCache::destroyDecodedDataForAllImages()
+{
+ MemoryCache::singleton().forEachResource([](CachedResource& resource) {
+ if (resource.isImage())
+ resource.destroyDecodedData();
+ });
}
void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources)
{
if (m_inPruneResources)
return;
- TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true);
+ SetForScope<bool> reentrancyProtector(m_inPruneResources, true);
double currentTime = FrameView::currentPaintTimeStamp();
if (!currentTime) // In case prune is called directly, outside of a Frame paint.
currentTime = monotonicallyIncreasingTime();
// Destroy any decoded data in live objects that we can.
- // Start from the tail, since this is the least recently accessed of the objects.
+ // Start from the head, 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;
+ auto it = m_liveDecodedResources.begin();
+ while (it != m_liveDecodedResources.end()) {
+ auto* current = *it;
+
+ // Increment the iterator now because the call to destroyDecodedData() below
+ // may cause a call to ListHashSet::remove() and invalidate the current
+ // iterator. Note that this is safe because unlike iteration of most
+ // WTF Hash data structures, iteration is guaranteed safe against mutation
+ // of the ListHashSet, except for removal of the item currently pointed to
+ // by a given iterator.
+ ++it;
+
ASSERT(current->hasClients());
if (current->isLoaded() && current->decodedSize()) {
// Check to see if the remaining resources are too new to prune.
@@ -325,23 +332,18 @@ void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestr
if (!shouldDestroyDecodedDataForAllLiveResources && 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.
+ // 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;
@@ -350,135 +352,61 @@ void MemoryCache::pruneDeadResources()
pruneDeadResourcesToSize(targetSize);
}
-void MemoryCache::pruneDeadResourcesToPercentage(float prunePercentage)
-{
- if (!m_pruneEnabled)
- return;
-
- if (prunePercentage < 0.0f || prunePercentage > 0.95f)
- return;
-
- unsigned currentSize = m_liveSize + m_deadSize;
- unsigned targetSize = static_cast<unsigned>(currentSize * prunePercentage);
-
- pruneDeadResourcesToSize(targetSize);
-}
-
void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize)
{
if (m_inPruneResources)
return;
- TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true);
-
- int size = m_allResources.size();
+ SetForScope<bool> reentrancyProtector(m_inPruneResources, true);
- // 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;
- 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;
-
+ for (int i = m_allResources.size() - 1; i >= 0; i--) {
+ // Make a copy of the LRUList first (and ref the resources) as calling
+ // destroyDecodedData() can alter the LRUList.
+ Vector<CachedResourceHandle<CachedResource>> lruList;
+ copyToVector(*m_allResources[i], lruList);
+
// First flush all the decoded data in this queue.
- while (current) {
- // Protect 'previous' so it can't get deleted during destroyDecodedData().
- CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList;
- ASSERT(!previous || previous->inCache());
- if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) {
+ // Remove from the head, since this is the least frequently accessed of the objects.
+ for (auto& resource : lruList) {
+ if (!resource->inCache())
+ continue;
+
+ if (!resource->hasClients() && !resource->isPreloaded() && resource->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();
+ resource->destroyDecodedData();
if (targetSize && m_deadSize <= targetSize)
return;
}
- // Decoded data may reference other resources. Stop iterating if 'previous' somehow got
- // kicked out of cache during destroyDecodedData().
- if (previous && !previous->inCache())
- break;
- current = previous.get();
}
- // Now evict objects from this queue.
- current = m_allResources[i].m_tail;
- while (current) {
- CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList;
- ASSERT(!previous || previous->inCache());
- if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) {
- if (!makeResourcePurgeable(current))
- evict(current);
+ // Now evict objects from this list.
+ // Remove from the head, since this is the least frequently accessed of the objects.
+ for (auto& resource : lruList) {
+ if (!resource->inCache())
+ continue;
+ if (!resource->hasClients() && !resource->isPreloaded() && !resource->isCacheValidator()) {
+ remove(*resource);
if (targetSize && m_deadSize <= targetSize)
return;
}
- if (previous && !previous->inCache())
- break;
- current = previous.get();
}
// Shrink the vector back down so we don't waste time inspecting
// empty LRU lists on future prunes.
- if (m_allResources[i].m_head)
+ if (!m_allResources[i]->isEmpty())
canShrinkLRULists = false;
else if (canShrinkLRULists)
- m_allResources.resize(i);
+ m_allResources.shrink(i);
}
}
-#if ENABLE(DISK_IMAGE_CACHE)
-void MemoryCache::flushCachedImagesToDisk()
-{
- if (!diskImageCache().isEnabled())
- return;
-
-#ifndef NDEBUG
- double start = WTF::currentTimeMS();
- unsigned resourceCount = 0;
- unsigned cachedSize = 0;
-#endif
-
- for (size_t i = m_allResources.size(); i; ) {
- --i;
- CachedResource* current = m_allResources[i].m_tail;
- while (current) {
- CachedResource* previous = current->m_prevInAllResourcesList;
-
- if (!current->isUsingDiskImageCache() && current->canUseDiskImageCache()) {
- current->useDiskImageCache();
- current->destroyDecodedData();
-#ifndef NDEBUG
- LOG(DiskImageCache, "Cache::diskCacheResources(): attempting to save (%d) bytes", current->resourceBuffer()->sharedBuffer()->size());
- ++resourceCount;
- cachedSize += current->resourceBuffer()->sharedBuffer()->size();
-#endif
- }
-
- current = previous;
- }
- }
-
-#ifndef NDEBUG
- double end = WTF::currentTimeMS();
- LOG(DiskImageCache, "DiskImageCache: took (%f) ms to cache (%d) bytes for (%d) resources", end - start, cachedSize, resourceCount);
-#endif
-}
-#endif // ENABLE(DISK_IMAGE_CACHE)
-
void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes)
{
ASSERT(minDeadBytes <= maxDeadBytes);
@@ -489,400 +417,269 @@ void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, un
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)
+void MemoryCache::remove(CachedResource& resource)
{
ASSERT(WTF::isMainThread());
- LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", resource, resource->url().string().latin1().data());
+ LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", &resource, resource.url().string().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.
-#if ENABLE(CACHE_PARTITIONING)
- CachedResourceItem* item = m_resources.get(resource->url());
- if (item) {
- item->remove(resource->cachePartition());
- if (!item->size())
- m_resources.remove(resource->url());
- }
-#else
- m_resources.remove(resource->url());
-#endif
- 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
-#if ENABLE(CACHE_PARTITIONING)
- ASSERT(!m_resources.get(resource->url()) || m_resources.get(resource->url())->get(resource->cachePartition()) != resource);
-#else
- ASSERT(m_resources.get(resource->url()) != resource);
-#endif
+ if (auto* resources = sessionResourceMap(resource.sessionID())) {
+ auto key = std::make_pair(resource.url(), resource.cachePartition());
+
+ if (resource.inCache()) {
+ // Remove resource from the resource map.
+ resources->remove(key);
+ resource.setInCache(false);
+
+ // If the resource map is now empty, remove it from m_sessionResources.
+ if (resources->isEmpty())
+ m_sessionResources.remove(resource.sessionID());
+
+ // Remove from the appropriate LRU list.
+ removeFromLRUList(resource);
+ removeFromLiveDecodedResourcesList(resource);
+ adjustSize(resource.hasClients(), -static_cast<long long>(resource.size()));
+ } else
+ ASSERT(resources->get(key) != &resource);
+ }
- resource->deleteIfPossible();
+ resource.deleteIfPossible();
}
-MemoryCache::LRUList* MemoryCache::lruListFor(CachedResource* resource)
+auto MemoryCache::lruListFor(CachedResource& resource) -> LRUList&
{
- unsigned accessCount = std::max(resource->accessCount(), 1U);
- unsigned queueIndex = WTF::fastLog2(resource->size() / accessCount);
+ unsigned accessCount = std::max(resource.accessCount(), 1U);
+ unsigned queueIndex = WTF::fastLog2(resource.size() / accessCount);
#ifndef NDEBUG
- resource->m_lruIndex = queueIndex;
+ resource.m_lruIndex = queueIndex;
#endif
- if (m_allResources.size() <= queueIndex)
- m_allResources.grow(queueIndex + 1);
- return &m_allResources[queueIndex];
+
+ m_allResources.reserveCapacity(queueIndex + 1);
+ while (m_allResources.size() <= queueIndex)
+ m_allResources.uncheckedAppend(std::make_unique<LRUList>());
+ return *m_allResources[queueIndex];
}
-void MemoryCache::removeFromLRUList(CachedResource* resource)
+void MemoryCache::removeFromLRUList(CachedResource& resource)
{
// If we've never been accessed, then we're brand new and not in any list.
- if (resource->accessCount() == 0)
+ if (!resource.accessCount())
return;
#if !ASSERT_DISABLED
- unsigned oldListIndex = resource->m_lruIndex;
+ unsigned oldListIndex = resource.m_lruIndex;
#endif
- LRUList* list = lruListFor(resource);
+ 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;
+ ASSERT(resource.m_lruIndex == oldListIndex);
- if (prev)
- prev->m_nextInAllResourcesList = next;
- else if (list->m_head == resource)
- list->m_head = next;
+ bool removed = list.remove(&resource);
+ ASSERT_UNUSED(removed, removed);
}
-void MemoryCache::insertInLRUList(CachedResource* resource)
+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;
+ ASSERT(resource.inCache());
+ ASSERT(resource.accessCount() > 0);
- if (!resource->m_nextInAllResourcesList)
- list->m_tail = resource;
-
-#if !ASSERT_DISABLED
- // 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
-
+ auto addResult = lruListFor(resource).add(&resource);
+ ASSERT_UNUSED(addResult, addResult.isNewEntry);
}
-void MemoryCache::resourceAccessed(CachedResource* resource)
+void MemoryCache::resourceAccessed(CachedResource& resource)
{
- ASSERT(resource->inCache());
+ 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());
+ if (!resource.accessCount())
+ adjustSize(resource.hasClients(), resource.size());
// Add to our access count.
- resource->increaseAccessCount();
+ resource.increaseAccessCount();
// Now insert into the new queue.
insertInLRUList(resource);
}
-void MemoryCache::removeResourcesWithOrigin(SecurityOrigin* origin)
+void MemoryCache::removeResourcesWithOrigin(SecurityOrigin& origin)
{
- Vector<CachedResource*> resourcesWithOrigin;
-
- CachedResourceMap::iterator e = m_resources.end();
-#if ENABLE(CACHE_PARTITIONING)
- String originPartition = ResourceRequest::partitionName(origin->host());
-#endif
+ String originPartition = ResourceRequest::partitionName(origin.host());
- for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it) {
-#if ENABLE(CACHE_PARTITIONING)
- for (CachedResourceItem::iterator itemIterator = it->value->begin(); itemIterator != it->value->end(); ++itemIterator) {
- CachedResource* resource = itemIterator->value;
- String partition = itemIterator->key;
- if (partition == originPartition) {
- resourcesWithOrigin.append(resource);
+ Vector<CachedResource*> resourcesWithOrigin;
+ for (auto& resources : m_sessionResources.values()) {
+ for (auto& keyValue : *resources) {
+ auto& resource = *keyValue.value;
+ auto& partitionName = keyValue.key.second;
+ if (partitionName == originPartition) {
+ resourcesWithOrigin.append(&resource);
continue;
}
-#else
- CachedResource* resource = it->value;
-#endif
- RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::createFromString(resource->url());
- if (!resourceOrigin)
- continue;
- if (resourceOrigin->equal(origin))
- resourcesWithOrigin.append(resource);
-#if ENABLE(CACHE_PARTITIONING)
+ RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::create(resource.url());
+ if (resourceOrigin->equal(&origin))
+ resourcesWithOrigin.append(&resource);
}
-#endif
}
- for (size_t i = 0; i < resourcesWithOrigin.size(); ++i)
- remove(resourcesWithOrigin[i]);
+ for (auto* resource : resourcesWithOrigin)
+ remove(*resource);
+}
+
+void MemoryCache::removeResourcesWithOrigins(SessionID sessionID, const HashSet<RefPtr<SecurityOrigin>>& origins)
+{
+ auto* resourceMap = sessionResourceMap(sessionID);
+ if (!resourceMap)
+ return;
+
+ HashSet<String> originPartitions;
+
+ for (auto& origin : origins)
+ originPartitions.add(ResourceRequest::partitionName(origin->host()));
+
+ Vector<CachedResource*> resourcesToRemove;
+ for (auto& keyValuePair : *resourceMap) {
+ auto& resource = *keyValuePair.value;
+ auto& partitionName = keyValuePair.key.second;
+ if (originPartitions.contains(partitionName)) {
+ resourcesToRemove.append(&resource);
+ continue;
+ }
+ if (origins.contains(SecurityOrigin::create(resource.url()).ptr()))
+ resourcesToRemove.append(&resource);
+ }
+
+ for (auto& resource : resourcesToRemove)
+ remove(*resource);
}
void MemoryCache::getOriginsWithCache(SecurityOriginSet& origins)
{
-#if ENABLE(CACHE_PARTITIONING)
- DEFINE_STATIC_LOCAL(String, httpString, ("http"));
-#endif
- CachedResourceMap::iterator e = m_resources.end();
- for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it) {
-#if ENABLE(CACHE_PARTITIONING)
- if (it->value->begin()->key == emptyString())
- origins.add(SecurityOrigin::createFromString(it->value->begin()->value->url()));
- else
- origins.add(SecurityOrigin::create(httpString, it->value->begin()->key, 0));
-#else
- origins.add(SecurityOrigin::createFromString(it->value->url()));
-#endif
+ for (auto& resources : m_sessionResources.values()) {
+ for (auto& keyValue : *resources) {
+ auto& resource = *keyValue.value;
+ auto& partitionName = keyValue.key.second;
+ if (!partitionName.isEmpty())
+ origins.add(SecurityOrigin::create(ASCIILiteral("http"), partitionName, 0));
+ else
+ origins.add(SecurityOrigin::create(resource.url()));
+ }
}
}
-void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource* resource)
+HashSet<RefPtr<SecurityOrigin>> MemoryCache::originsWithCache(SessionID sessionID) const
{
- // 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;
+ HashSet<RefPtr<SecurityOrigin>> origins;
-#if !ASSERT_DISABLED
- // 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;
+ auto it = m_sessionResources.find(sessionID);
+ if (it != m_sessionResources.end()) {
+ for (auto& keyValue : *it->value) {
+ auto& resource = *keyValue.value;
+ auto& partitionName = keyValue.key.second;
+ if (!partitionName.isEmpty())
+ origins.add(SecurityOrigin::create("http", partitionName, 0));
+ else
+ origins.add(SecurityOrigin::create(resource.url()));
}
}
- 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;
+ return origins;
+}
- if (prev)
- prev->m_nextInLiveResourcesList = next;
- else if (m_liveDecodedResources.m_head == resource)
- m_liveDecodedResources.m_head = next;
+void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource& resource)
+{
+ m_liveDecodedResources.remove(&resource);
}
-void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource)
+void MemoryCache::insertInLiveDecodedResourcesList(CachedResource& resource)
{
// Make sure we aren't in the list already.
- ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResourcesList && !resource->m_inLiveDecodedResourcesList);
- 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;
-
-#if !ASSERT_DISABLED
- // 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
-
+ ASSERT(!m_liveDecodedResources.contains(&resource));
+ m_liveDecodedResources.add(&resource);
}
-void MemoryCache::addToLiveResourcesSize(CachedResource* resource)
+void MemoryCache::addToLiveResourcesSize(CachedResource& resource)
{
- m_liveSize += resource->size();
- m_deadSize -= resource->size();
+ m_liveSize += resource.size();
+ m_deadSize -= resource.size();
}
-void MemoryCache::removeFromLiveResourcesSize(CachedResource* resource)
+void MemoryCache::removeFromLiveResourcesSize(CachedResource& resource)
{
- m_liveSize -= resource->size();
- m_deadSize += resource->size();
+ m_liveSize -= resource.size();
+ m_deadSize += resource.size();
}
-void MemoryCache::adjustSize(bool live, int delta)
+void MemoryCache::adjustSize(bool live, long long delta)
{
if (live) {
- ASSERT(delta >= 0 || ((int)m_liveSize + delta >= 0));
+ ASSERT(delta >= 0 || (static_cast<long long>(m_liveSize) + delta >= 0));
m_liveSize += delta;
} else {
- ASSERT(delta >= 0 || ((int)m_deadSize + delta >= 0));
+ ASSERT(delta >= 0 || (static_cast<long long>(m_deadSize) + delta >= 0));
m_deadSize += delta;
}
}
-void MemoryCache::removeUrlFromCache(ScriptExecutionContext* context, const String& urlString)
-{
- removeRequestFromCache(context, ResourceRequest(urlString));
-}
-
-void MemoryCache::removeRequestFromCache(ScriptExecutionContext* context, const ResourceRequest& request)
+void MemoryCache::removeRequestFromSessionCaches(ScriptExecutionContext& context, const ResourceRequest& request)
{
- if (context->isWorkerGlobalScope()) {
- WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
- workerGlobalScope->thread()->workerLoaderProxy().postTaskToLoader(createCallbackTask(&crossThreadRemoveRequestFromCache, request));
+ if (is<WorkerGlobalScope>(context)) {
+ downcast<WorkerGlobalScope>(context).thread().workerLoaderProxy().postTaskToLoader([request = request.isolatedCopy()] (ScriptExecutionContext& context) {
+ MemoryCache::removeRequestFromSessionCaches(context, request);
+ });
return;
}
- removeRequestFromCacheImpl(context, request);
-}
-
-void MemoryCache::removeRequestFromCacheImpl(ScriptExecutionContext*, const ResourceRequest& request)
-{
- if (CachedResource* resource = memoryCache()->resourceForRequest(request))
- memoryCache()->remove(resource);
-}
-
-void MemoryCache::crossThreadRemoveRequestFromCache(ScriptExecutionContext* context, PassOwnPtr<WebCore::CrossThreadResourceRequestData> requestData)
-{
- OwnPtr<ResourceRequest> request(ResourceRequest::adopt(requestData));
- MemoryCache::removeRequestFromCacheImpl(context, *request);
+ auto& memoryCache = MemoryCache::singleton();
+ for (auto& resources : memoryCache.m_sessionResources) {
+ if (CachedResource* resource = memoryCache.resourceForRequestImpl(request, *resources.value))
+ memoryCache.remove(*resource);
+ }
}
-void MemoryCache::TypeStatistic::addResource(CachedResource* o)
+void MemoryCache::TypeStatistic::addResource(CachedResource& resource)
{
- 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;
-#if ENABLE(DISK_IMAGE_CACHE)
- // Only the data inside the resource was mapped, not the entire resource.
- mappedSize += o->isUsingDiskImageCache() ? o->resourceBuffer()->sharedBuffer()->size() : 0;
-#endif
+ size += resource.size();
+ liveSize += resource.hasClients() ? resource.size() : 0;
+ decodedSize += resource.decodedSize();
}
MemoryCache::Statistics MemoryCache::getStatistics()
{
Statistics stats;
- CachedResourceMap::iterator e = m_resources.end();
- for (CachedResourceMap::iterator i = m_resources.begin(); i != e; ++i) {
-#if ENABLE(CACHE_PARTITIONING)
- for (CachedResourceItem::iterator itemIterator = i->value->begin(); itemIterator != i->value->end(); ++itemIterator) {
- CachedResource* resource = itemIterator->value;
-#else
- CachedResource* resource = i->value;
-#endif
+
+ for (auto& resources : m_sessionResources.values()) {
+ for (auto* resource : resources->values()) {
switch (resource->type()) {
case CachedResource::ImageResource:
- stats.images.addResource(resource);
+ stats.images.addResource(*resource);
break;
case CachedResource::CSSStyleSheet:
- stats.cssStyleSheets.addResource(resource);
+ stats.cssStyleSheets.addResource(*resource);
break;
case CachedResource::Script:
- stats.scripts.addResource(resource);
+ stats.scripts.addResource(*resource);
break;
#if ENABLE(XSLT)
case CachedResource::XSLStyleSheet:
- stats.xslStyleSheets.addResource(resource);
+ stats.xslStyleSheets.addResource(*resource);
break;
#endif
+#if ENABLE(SVG_FONTS)
+ case CachedResource::SVGFontResource:
+#endif
case CachedResource::FontResource:
- stats.fonts.addResource(resource);
+ stats.fonts.addResource(*resource);
break;
default:
break;
}
-#if ENABLE(CACHE_PARTITIONING)
}
-#endif
}
return stats;
}
@@ -893,16 +690,10 @@ void MemoryCache::setDisabled(bool disabled)
if (!m_disabled)
return;
- for (;;) {
- CachedResourceMap::iterator outerIterator = m_resources.begin();
- if (outerIterator == m_resources.end())
- break;
-#if ENABLE(CACHE_PARTITIONING)
- CachedResourceItem::iterator innerIterator = outerIterator->value->begin();
- evict(innerIterator->value);
-#else
- evict(outerIterator->value);
-#endif
+ while (!m_sessionResources.isEmpty()) {
+ auto& resources = *m_sessionResources.begin()->value;
+ ASSERT(!resources.isEmpty());
+ remove(*resources.begin()->value);
}
}
@@ -915,66 +706,65 @@ void MemoryCache::evictResources()
setDisabled(false);
}
+void MemoryCache::evictResources(SessionID sessionID)
+{
+ if (disabled())
+ return;
+
+ forEachSessionResource(sessionID, [this] (CachedResource& resource) { remove(resource); });
+
+ ASSERT(!m_sessionResources.contains(sessionID));
+}
+
+bool MemoryCache::needsPruning() const
+{
+ return m_liveSize + m_deadSize > m_capacity || m_deadSize > m_maxDeadCapacity;
+}
+
void MemoryCache::prune()
{
- if (m_liveSize + m_deadSize <= m_capacity && m_deadSize <= m_maxDeadCapacity) // Fast path.
+ if (!needsPruning())
return;
pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live.
pruneLiveResources();
}
-void MemoryCache::pruneToPercentage(float targetPercentLive)
+void MemoryCache::pruneSoon()
{
- pruneDeadResourcesToPercentage(targetPercentLive); // Prune dead first, in case it was "borrowing" capacity from live.
- pruneLiveResourcesToPercentage(targetPercentLive);
+ if (m_pruneTimer.isActive())
+ return;
+ if (!needsPruning())
+ return;
+ m_pruneTimer.startOneShot(0);
}
-
#ifndef NDEBUG
void MemoryCache::dumpStats()
{
Statistics s = getStatistics();
-#if ENABLE(DISK_IMAGE_CACHE)
- printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize", "Mapped", "\"Real\"");
- printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
- printf("%-13s %13d %13d %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, s.images.mappedSize, s.images.size - s.images.mappedSize);
-#else
- 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);
-#endif
- 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);
+ printf("%-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize");
+ printf("%-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------");
+ printf("%-13s %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize);
+ printf("%-13s %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize);
#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);
+ printf("%-13s %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize);
#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", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
+ printf("%-13s %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize);
+ printf("%-13s %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize);
+ printf("%-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------");
}
void MemoryCache::dumpLRULists(bool includeLive) const
{
-#if ENABLE(DISK_IMAGE_CACHE)
- printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged, isMemoryMapped):\n");
-#else
- printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n");
-#endif
+ printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced):\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())
-#if ENABLE(DISK_IMAGE_CACHE)
- printf("(%.1fK, %.1fK, %uA, %dR, %d, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged(), current->isUsingDiskImageCache());
-#else
- 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());
-#endif
-
- current = prev;
+ for (auto* resource : *m_allResources[i]) {
+ if (includeLive || !resource->hasClients())
+ printf("(%.1fK, %.1fK, %uA, %dR); ", resource->decodedSize() / 1024.0f, (resource->encodedSize() + resource->overheadSize()) / 1024.0f, resource->accessCount(), resource->hasClients());
}
}
}
diff --git a/Source/WebCore/loader/cache/MemoryCache.h b/Source/WebCore/loader/cache/MemoryCache.h
index c023a9725..c5d97e1d3 100644
--- a/Source/WebCore/loader/cache/MemoryCache.h
+++ b/Source/WebCore/loader/cache/MemoryCache.h
@@ -22,13 +22,16 @@
pages from the web. It has a memory cache for these objects.
*/
-#ifndef Cache_h
-#define Cache_h
+#pragma once
-#include "NativeImagePtr.h"
+#include "NativeImage.h"
#include "SecurityOriginHash.h"
+#include "SessionID.h"
+#include "Timer.h"
+#include <wtf/Forward.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
+#include <wtf/ListHashSet.h>
#include <wtf/Noncopyable.h>
#include <wtf/Vector.h>
#include <wtf/text/StringHash.h>
@@ -36,16 +39,12 @@
namespace WebCore {
-class CachedCSSStyleSheet;
class CachedResource;
-class CachedResourceLoader;
class URL;
class ResourceRequest;
class ResourceResponse;
class ScriptExecutionContext;
class SecurityOrigin;
-struct CrossThreadResourceRequestData;
-struct SecurityOriginHash;
// This cache holds subresources used by Web pages: images, scripts, stylesheets, etc.
@@ -60,54 +59,26 @@ struct SecurityOriginHash;
// -------|-----+++++++++++++++|
// -------|-----+++++++++++++++|+++++
-// 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 {
WTF_MAKE_NONCOPYABLE(MemoryCache); WTF_MAKE_FAST_ALLOCATED;
+ friend NeverDestroyed<MemoryCache>;
+ friend class Internals;
public:
- friend MemoryCache* memoryCache();
-
-#if ENABLE(CACHE_PARTITIONING)
- typedef HashMap<String, CachedResource*> CachedResourceItem;
- typedef HashMap<String, OwnPtr<CachedResourceItem>> CachedResourceMap;
-#else
- typedef HashMap<String, CachedResource*> CachedResourceMap;
-#endif
-
- 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;
-#if ENABLE(DISK_IMAGE_CACHE)
- int mappedSize;
- TypeStatistic() : count(0), size(0), liveSize(0), decodedSize(0), purgeableSize(0), purgedSize(0), mappedSize(0) { }
-#else
- TypeStatistic() : count(0), size(0), liveSize(0), decodedSize(0), purgeableSize(0), purgedSize(0) { }
-#endif
- void addResource(CachedResource*);
+
+ TypeStatistic()
+ : count(0)
+ , size(0)
+ , liveSize(0)
+ , decodedSize(0)
+ {
+ }
+
+ void addResource(CachedResource&);
};
struct Statistics {
@@ -118,98 +89,92 @@ public:
TypeStatistic fonts;
};
- CachedResource* resourceForURL(const URL&);
- CachedResource* resourceForRequest(const ResourceRequest&);
-
- bool add(CachedResource* resource);
- void remove(CachedResource* resource) { evict(resource); }
+ WEBCORE_EXPORT static MemoryCache& singleton();
- static URL removeFragmentIdentifierIfNeeded(const URL& originalURL);
-
- void revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse&);
- void revalidationFailed(CachedResource* revalidatingResource);
-
- // Sets the cache's memory capacities, in bytes. These will hold only approximately,
+ WEBCORE_EXPORT CachedResource* resourceForRequest(const ResourceRequest&, SessionID);
+
+ bool add(CachedResource&);
+ void remove(CachedResource&);
+
+ static bool shouldRemoveFragmentIdentifier(const URL&);
+ static URL removeFragmentIdentifierIfNeeded(const URL&);
+
+ void revalidationSucceeded(CachedResource& revalidatingResource, const ResourceResponse&);
+ void revalidationFailed(CachedResource& revalidatingResource);
+
+ void forEachResource(const std::function<void(CachedResource&)>&);
+ void forEachSessionResource(SessionID, const std::function<void(CachedResource&)>&);
+ void destroyDecodedDataForAllImages();
+
+ // 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);
+ WEBCORE_EXPORT 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);
+ WEBCORE_EXPORT void setDisabled(bool);
bool disabled() const { return m_disabled; }
- void evictResources();
-
- void setPruneEnabled(bool enabled) { m_pruneEnabled = enabled; }
+ WEBCORE_EXPORT void evictResources();
+ WEBCORE_EXPORT void evictResources(SessionID);
+
void prune();
- void pruneToPercentage(float targetPercentLive);
+ void pruneSoon();
+ unsigned size() const { return m_liveSize + m_deadSize; }
- void setDeadDecodedDataDeletionInterval(double interval) { m_deadDecodedDataDeletionInterval = interval; }
- double deadDecodedDataDeletionInterval() const { return m_deadDecodedDataDeletionInterval; }
+ void setDeadDecodedDataDeletionInterval(std::chrono::milliseconds interval) { m_deadDecodedDataDeletionInterval = interval; }
+ std::chrono::milliseconds deadDecodedDataDeletionInterval() const { return m_deadDecodedDataDeletionInterval; }
// Calls to put the cached resource into and out of LRU lists.
- void insertInLRUList(CachedResource*);
- void removeFromLRUList(CachedResource*);
+ void insertInLRUList(CachedResource&);
+ void removeFromLRUList(CachedResource&);
// Called to adjust the cache totals when a resource changes size.
- void adjustSize(bool live, int delta);
+ void adjustSize(bool live, long long 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*);
+ void insertInLiveDecodedResourcesList(CachedResource&);
+ void removeFromLiveDecodedResourcesList(CachedResource&);
- static bool shouldMakeResourcePurgeableOnEviction();
+ void addToLiveResourcesSize(CachedResource&);
+ void removeFromLiveResourcesSize(CachedResource&);
-#if ENABLE(DISK_IMAGE_CACHE)
- void flushCachedImagesToDisk(); // Flush encoded data from resources still referenced by web pages.
-#endif
-
- static void removeUrlFromCache(ScriptExecutionContext*, const String& urlString);
- static void removeRequestFromCache(ScriptExecutionContext*, const ResourceRequest&);
+ static void removeRequestFromSessionCaches(ScriptExecutionContext&, const ResourceRequest&);
// Function to collect cache statistics for the caches window in the Safari Debug menu.
- Statistics getStatistics();
+ WEBCORE_EXPORT Statistics getStatistics();
- void resourceAccessed(CachedResource*);
+ void resourceAccessed(CachedResource&);
+ bool inLiveDecodedResourcesList(CachedResource& resource) const { return m_liveDecodedResources.contains(&resource); }
typedef HashSet<RefPtr<SecurityOrigin>> SecurityOriginSet;
- void removeResourcesWithOrigin(SecurityOrigin*);
- void getOriginsWithCache(SecurityOriginSet& origins);
-
- unsigned minDeadCapacity() const { return m_minDeadCapacity; }
- unsigned maxDeadCapacity() const { return m_maxDeadCapacity; }
- unsigned capacity() const { return m_capacity; }
- unsigned liveSize() const { return m_liveSize; }
- unsigned deadSize() const { return m_deadSize; }
-
-#if USE(CG)
- // FIXME: Remove the USE(CG) once we either make NativeImagePtr a smart pointer on all platforms or
- // remove the usage of CFRetain() in MemoryCache::addImageToCache() so as to make the code platform-independent.
- bool addImageToCache(NativeImagePtr, const URL&, const String& cachePartition);
- void removeImageFromCache(const URL&, const String& cachePartition);
-#endif
+ WEBCORE_EXPORT void removeResourcesWithOrigin(SecurityOrigin&);
+ WEBCORE_EXPORT void removeResourcesWithOrigins(SessionID, const HashSet<RefPtr<SecurityOrigin>>&);
+ WEBCORE_EXPORT void getOriginsWithCache(SecurityOriginSet& origins);
+ WEBCORE_EXPORT HashSet<RefPtr<SecurityOrigin>> originsWithCache(SessionID) const;
+
+ WEBCORE_EXPORT bool addImageToCache(NativeImagePtr&&, const URL&, const String& domainForCachePartition);
+ WEBCORE_EXPORT void removeImageFromCache(const URL&, const String& domainForCachePartition);
// pruneDead*() - Flush decoded and encoded data from resources not referenced by Web pages.
// pruneLive*() - Flush decoded data from resources still referenced by Web pages.
- void pruneDeadResources(); // Automatically decide how much to prune.
- void pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources = false);
+ WEBCORE_EXPORT void pruneDeadResources(); // Automatically decide how much to prune.
+ WEBCORE_EXPORT void pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources = false);
+
+ WEBCORE_EXPORT void pruneDeadResourcesToSize(unsigned targetSize);
+ WEBCORE_EXPORT void pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources = false);
private:
- void pruneDeadResourcesToPercentage(float prunePercentage); // Prune to % current size
- void pruneLiveResourcesToPercentage(float prunePercentage);
- void pruneDeadResourcesToSize(unsigned targetSize);
- void pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources = false);
+ typedef HashMap<std::pair<URL, String /* partitionName */>, CachedResource*> CachedResourceMap;
+ typedef ListHashSet<CachedResource*> LRUList;
MemoryCache();
~MemoryCache(); // Not implemented to make sure nobody accidentally calls delete -- WebCore does not delete singletons.
- LRUList* lruListFor(CachedResource*);
+ LRUList& lruListFor(CachedResource&);
#ifndef NDEBUG
void dumpStats();
void dumpLRULists(bool includeLive) const;
@@ -217,21 +182,20 @@ private:
unsigned liveCapacity() const;
unsigned deadCapacity() const;
+ bool needsPruning() const;
- bool makeResourcePurgeable(CachedResource*);
- void evict(CachedResource*);
+ CachedResource* resourceForRequestImpl(const ResourceRequest&, CachedResourceMap&);
- static void removeRequestFromCacheImpl(ScriptExecutionContext*, const ResourceRequest&);
- static void crossThreadRemoveRequestFromCache(ScriptExecutionContext*, PassOwnPtr<CrossThreadResourceRequestData>);
+ CachedResourceMap& ensureSessionResourceMap(SessionID);
+ CachedResourceMap* sessionResourceMap(SessionID) const;
bool m_disabled; // Whether or not the cache is enabled.
- bool m_pruneEnabled;
bool m_inPruneResources;
unsigned m_capacity;
unsigned m_minDeadCapacity;
unsigned m_maxDeadCapacity;
- double m_deadDecodedDataDeletionInterval;
+ std::chrono::milliseconds 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.
@@ -239,28 +203,17 @@ private:
// 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;
+ Vector<std::unique_ptr<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).
- CachedResourceMap m_resources;
-};
-
-inline bool MemoryCache::shouldMakeResourcePurgeableOnEviction()
-{
-#if PLATFORM(IOS)
- return true;
-#else
- return false;
-#endif
-}
+ typedef HashMap<SessionID, std::unique_ptr<CachedResourceMap>> SessionCachedResourceMap;
+ SessionCachedResourceMap m_sessionResources;
-// Function to obtain the global cache.
-MemoryCache* memoryCache();
-
-}
+ Timer m_pruneTimer;
+};
-#endif
+} // namespace WebCore