https://bugs.webkit.org/show_bug.cgi?id=144952
Source/WebCore:
rdar://problem/
13387307
Reviewed by Oliver Hunt.
This is a symptom of a general problem that we don't revalidate subresources of cached parsed stylesheets.
Fix by tightening the check we perform when choosing to used the cached sheet. If there are expired subresources
we reparse the sheet.
Test: http/tests/cache/stylesheet-sharing.html
* css/CSSCrossfadeValue.cpp:
(WebCore::CSSCrossfadeValue::traverseSubresources):
(WebCore::CSSCrossfadeValue::hasFailedOrCanceledSubresources): Deleted.
Replace hasFailedOrCanceledSubresources with general purpose subresource traversal functions.
* css/CSSCrossfadeValue.h:
* css/CSSFilterImageValue.cpp:
(WebCore::CSSFilterImageValue::traverseSubresources):
(WebCore::CSSFilterImageValue::hasFailedOrCanceledSubresources): Deleted.
* css/CSSFilterImageValue.h:
* css/CSSFontFaceSrcValue.cpp:
(WebCore::CSSFontFaceSrcValue::traverseSubresources):
(WebCore::CSSFontFaceSrcValue::hasFailedOrCanceledSubresources): Deleted.
* css/CSSFontFaceSrcValue.h:
* css/CSSImageSetValue.cpp:
(WebCore::CSSImageSetValue::traverseSubresources):
(WebCore::CSSImageSetValue::hasFailedOrCanceledSubresources): Deleted.
* css/CSSImageSetValue.h:
* css/CSSImageValue.cpp:
(WebCore::CSSImageValue::traverseSubresources):
(WebCore::CSSImageValue::hasFailedOrCanceledSubresources): Deleted.
* css/CSSImageValue.h:
* css/CSSValue.cpp:
(WebCore::CSSValue::traverseSubresources):
(WebCore::CSSValue::hasFailedOrCanceledSubresources): Deleted.
* css/CSSValue.h:
* css/CSSValueList.cpp:
(WebCore::CSSValueList::traverseSubresources):
(WebCore::CSSValueList::hasFailedOrCanceledSubresources): Deleted.
* css/CSSValueList.h:
* css/StyleProperties.cpp:
(WebCore::StyleProperties::traverseSubresources):
(WebCore::StyleProperties::hasFailedOrCanceledSubresources): Deleted.
* css/StyleProperties.h:
* css/StyleSheetContents.cpp:
(WebCore::traverseSubresourcesInRules):
(WebCore::StyleSheetContents::traverseSubresources):
(WebCore::StyleSheetContents::subresourcesAllowReuse):
Disallow reuse if there are expired subresources.
(WebCore::StyleSheetContents::isLoadingSubresources):
Testing support.
(WebCore::childRulesHaveFailedOrCanceledSubresources): Deleted.
(WebCore::StyleSheetContents::hasFailedOrCanceledSubresources): Deleted.
* css/StyleSheetContents.h:
(WebCore::StyleSheetContents::loadCompleted):
* html/HTMLLinkElement.cpp:
(WebCore::HTMLLinkElement::setCSSStyleSheet):
* loader/cache/CachedCSSStyleSheet.cpp:
(WebCore::CachedCSSStyleSheet::restoreParsedStyleSheet):
* loader/cache/CachedCSSStyleSheet.h:
* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::makeRevalidationDecision):
(WebCore::CachedImage::mustRevalidateDueToCacheHeaders): Deleted.
Move the logging code out from this function (it requires frame access this function doesn't otherwise need)
and refactor to return a decision enum.
* loader/cache/CachedImage.h:
* loader/cache/CachedResource.cpp:
(WebCore::CachedResource::makeRevalidationDecision):
(WebCore::logResourceRevalidationReason): Deleted.
(WebCore::CachedResource::mustRevalidateDueToCacheHeaders): Deleted.
* loader/cache/CachedResource.h:
(WebCore::CachedResource::loadFailedOrCanceled):
* loader/cache/CachedResourceLoader.cpp:
(WebCore::logRevalidation):
(WebCore::logResourceRevalidationDecision):
(WebCore::CachedResourceLoader::determineRevalidationPolicy):
Move logging here.
* testing/Internals.cpp:
(WebCore::Internals::isSharingStyleSheetContents):
(WebCore::Internals::isStyleSheetLoadingSubresources):
* testing/Internals.h:
* testing/Internals.idl:
LayoutTests:
Reviewed by Oliver Hunt.
* http/tests/cache/resources/non-shareable.css: Added.
(#foo):
(#bar):
(#test1):
(#test2):
* http/tests/cache/resources/shareable.css: Added.
(#foo):
(#bar):
(#test1):
(#test2):
* http/tests/cache/resources/stylesheet-html.php: Added.
* http/tests/cache/stylesheet-sharing-expected.txt: Added.
* http/tests/cache/stylesheet-sharing.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@184315
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2015-05-13 Antti Koivisto <antti@apple.com>
+
+ Cached CSS image resources don't show up after reloading <http://nightly.webkit.org/start/>
+ https://bugs.webkit.org/show_bug.cgi?id=144952
+
+ Reviewed by Oliver Hunt.
+
+ * http/tests/cache/resources/non-shareable.css: Added.
+ (#foo):
+ (#bar):
+ (#test1):
+ (#test2):
+ * http/tests/cache/resources/shareable.css: Added.
+ (#foo):
+ (#bar):
+ (#test1):
+ (#test2):
+ * http/tests/cache/resources/stylesheet-html.php: Added.
+ * http/tests/cache/stylesheet-sharing-expected.txt: Added.
+ * http/tests/cache/stylesheet-sharing.html: Added.
+
2015-05-13 Ryosuke Niwa <rniwa@webkit.org>
REGRESSION(r183770): Crash inside WebEditorClient::shouldApplyStyle when applying underline
--- /dev/null
+#foo { color: red }
+#bar { color: blue }
+#test1 { background-image:url(/resources/square100.png) }
+#test2 { background-image:url(../disk-cache/resources/generate-response.cgi?Content-Type=image/png&Cache-Control=max-age=0) }
--- /dev/null
+#foo { color: red }
+#bar { color: blue }
+#test1 { background-image:url(/resources/square100.png) }
+#test2 { background-image:url(../disk-cache/resources/generate-response.cgi?Content-Type=image/png&Cache-Control=max-age=100) }
--- /dev/null
+<?php
+echo "<link rel=stylesheet href=" . $_GET['sheet'] . ">";
+?>
+<div id=test1></div>
+<div id=test2></div>
--- /dev/null
+shareable.css
+PASS styleSheetContentsShared is true
+non-shareable.css
+PASS styleSheetContentsShared is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<script src="../../js-test-resources/js-test-pre.js"></script>
+<body>
+<script>
+jsTestIsAsync = true;
+
+function waitForStylesheet(iframe, completionHandler)
+{
+ var link = iframe.contentDocument.getElementsByTagName('link')[0];
+ if (internals.isStyleSheetLoadingSubresources(link)) {
+ setTimeout(function () { waitForStylesheet(iframe, completionHandler) }, 10);
+ return;
+ }
+ completionHandler(link);
+}
+
+function load(src, completionHandler)
+{
+ var iframe = document.createElement("iframe");
+ iframe.src = "resources/stylesheet-html.php?sheet=" + src;
+ iframe.onload = function () {
+ iframe.contentDocument.body.offsetWidth;
+ waitForStylesheet(iframe, completionHandler);
+ };
+ document.body.appendChild(iframe);
+}
+
+function testSharing(src, expected, completionHandler)
+{
+ load(src, function (linkA) {
+ load(src, function (linkB) {
+ styleSheetContentsShared = internals.isSharingStyleSheetContents(linkA, linkB);
+ debug(src);
+ if (expected)
+ shouldBeTrue("styleSheetContentsShared");
+ else
+ shouldBeFalse("styleSheetContentsShared");
+ completionHandler();
+ });
+ });
+}
+
+testSharing("shareable.css", true, function () {
+ testSharing("non-shareable.css", false, function () {
+ finishJSTest();
+ });
+});
+
+</script>
+
+<script src="../../js-test-resources/js-test-post.js"></script>
+2015-05-13 Antti Koivisto <antti@apple.com>
+
+ Cached CSS image resources don't show up after reloading <http://nightly.webkit.org/start/>
+ https://bugs.webkit.org/show_bug.cgi?id=144952
+ rdar://problem/13387307
+
+ Reviewed by Oliver Hunt.
+
+ This is a symptom of a general problem that we don't revalidate subresources of cached parsed stylesheets.
+
+ Fix by tightening the check we perform when choosing to used the cached sheet. If there are expired subresources
+ we reparse the sheet.
+
+ Test: http/tests/cache/stylesheet-sharing.html
+
+ * css/CSSCrossfadeValue.cpp:
+ (WebCore::CSSCrossfadeValue::traverseSubresources):
+ (WebCore::CSSCrossfadeValue::hasFailedOrCanceledSubresources): Deleted.
+
+ Replace hasFailedOrCanceledSubresources with general purpose subresource traversal functions.
+
+ * css/CSSCrossfadeValue.h:
+ * css/CSSFilterImageValue.cpp:
+ (WebCore::CSSFilterImageValue::traverseSubresources):
+ (WebCore::CSSFilterImageValue::hasFailedOrCanceledSubresources): Deleted.
+ * css/CSSFilterImageValue.h:
+ * css/CSSFontFaceSrcValue.cpp:
+ (WebCore::CSSFontFaceSrcValue::traverseSubresources):
+ (WebCore::CSSFontFaceSrcValue::hasFailedOrCanceledSubresources): Deleted.
+ * css/CSSFontFaceSrcValue.h:
+ * css/CSSImageSetValue.cpp:
+ (WebCore::CSSImageSetValue::traverseSubresources):
+ (WebCore::CSSImageSetValue::hasFailedOrCanceledSubresources): Deleted.
+ * css/CSSImageSetValue.h:
+ * css/CSSImageValue.cpp:
+ (WebCore::CSSImageValue::traverseSubresources):
+ (WebCore::CSSImageValue::hasFailedOrCanceledSubresources): Deleted.
+ * css/CSSImageValue.h:
+ * css/CSSValue.cpp:
+ (WebCore::CSSValue::traverseSubresources):
+ (WebCore::CSSValue::hasFailedOrCanceledSubresources): Deleted.
+ * css/CSSValue.h:
+ * css/CSSValueList.cpp:
+ (WebCore::CSSValueList::traverseSubresources):
+ (WebCore::CSSValueList::hasFailedOrCanceledSubresources): Deleted.
+ * css/CSSValueList.h:
+ * css/StyleProperties.cpp:
+ (WebCore::StyleProperties::traverseSubresources):
+ (WebCore::StyleProperties::hasFailedOrCanceledSubresources): Deleted.
+ * css/StyleProperties.h:
+ * css/StyleSheetContents.cpp:
+ (WebCore::traverseSubresourcesInRules):
+ (WebCore::StyleSheetContents::traverseSubresources):
+ (WebCore::StyleSheetContents::subresourcesAllowReuse):
+
+ Disallow reuse if there are expired subresources.
+
+ (WebCore::StyleSheetContents::isLoadingSubresources):
+
+ Testing support.
+
+ (WebCore::childRulesHaveFailedOrCanceledSubresources): Deleted.
+ (WebCore::StyleSheetContents::hasFailedOrCanceledSubresources): Deleted.
+ * css/StyleSheetContents.h:
+ (WebCore::StyleSheetContents::loadCompleted):
+ * html/HTMLLinkElement.cpp:
+ (WebCore::HTMLLinkElement::setCSSStyleSheet):
+ * loader/cache/CachedCSSStyleSheet.cpp:
+ (WebCore::CachedCSSStyleSheet::restoreParsedStyleSheet):
+ * loader/cache/CachedCSSStyleSheet.h:
+ * loader/cache/CachedImage.cpp:
+ (WebCore::CachedImage::makeRevalidationDecision):
+ (WebCore::CachedImage::mustRevalidateDueToCacheHeaders): Deleted.
+
+ Move the logging code out from this function (it requires frame access this function doesn't otherwise need)
+ and refactor to return a decision enum.
+
+ * loader/cache/CachedImage.h:
+ * loader/cache/CachedResource.cpp:
+ (WebCore::CachedResource::makeRevalidationDecision):
+ (WebCore::logResourceRevalidationReason): Deleted.
+ (WebCore::CachedResource::mustRevalidateDueToCacheHeaders): Deleted.
+ * loader/cache/CachedResource.h:
+ (WebCore::CachedResource::loadFailedOrCanceled):
+ * loader/cache/CachedResourceLoader.cpp:
+ (WebCore::logRevalidation):
+ (WebCore::logResourceRevalidationDecision):
+ (WebCore::CachedResourceLoader::determineRevalidationPolicy):
+
+ Move logging here.
+
+ * testing/Internals.cpp:
+ (WebCore::Internals::isSharingStyleSheetContents):
+ (WebCore::Internals::isStyleSheetLoadingSubresources):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
2015-05-13 Ryuan Choi <ryuan.choi@navercorp.com>
[CoordinatedGraphics] Remove scaleFactor from SurfaceUpdateInfo
m_ownerValue->crossfadeChanged(*rect);
}
-bool CSSCrossfadeValue::hasFailedOrCanceledSubresources() const
+bool CSSCrossfadeValue::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
- if (m_cachedFromImage && m_cachedFromImage->loadFailedOrCanceled())
+ if (m_cachedFromImage && handler(*m_cachedFromImage))
return true;
- if (m_cachedToImage && m_cachedToImage->loadFailedOrCanceled())
+ if (m_cachedToImage && handler(*m_cachedToImage))
return true;
return false;
}
void setPercentage(PassRefPtr<CSSPrimitiveValue> percentageValue) { m_percentageValue = percentageValue; }
- bool hasFailedOrCanceledSubresources() const;
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
PassRefPtr<CSSCrossfadeValue> blend(const CSSCrossfadeValue&, double) const;
m_ownerValue->filterImageChanged(*rect);
}
-bool CSSFilterImageValue::hasFailedOrCanceledSubresources() const
+bool CSSFilterImageValue::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
- if (m_cachedImage && m_cachedImage->loadFailedOrCanceled())
- return true;
- return false;
+ if (!m_cachedImage)
+ return false;
+ return handler(*m_cachedImage);
}
bool CSSFilterImageValue::equals(const CSSFilterImageValue& other) const
void loadSubimages(CachedResourceLoader&);
- bool hasFailedOrCanceledSubresources() const;
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
bool equals(const CSSFilterImageValue&) const;
addSubresourceURL(urls, styleSheet->completeURL(m_resource));
}
-bool CSSFontFaceSrcValue::hasFailedOrCanceledSubresources() const
+bool CSSFontFaceSrcValue::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
if (!m_cachedFont)
return false;
- return m_cachedFont->loadFailedOrCanceled();
+ return handler(*m_cachedFont);
}
CachedFont* CSSFontFaceSrcValue::cachedFont(Document* document, bool isSVG)
void addSubresourceStyleURLs(ListHashSet<URL>&, const StyleSheetContents*) const;
- bool hasFailedOrCanceledSubresources() const;
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
CachedFont* cachedFont(Document*, bool isSVG);
return result.toString();
}
-bool CSSImageSetValue::hasFailedOrCanceledSubresources() const
+bool CSSImageSetValue::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
if (!is<StyleCachedImageSet>(m_imageSet.get()))
return false;
CachedImage* cachedResource = downcast<StyleCachedImageSet>(*m_imageSet).cachedImage();
- if (!cachedResource)
- return true;
- return cachedResource->loadFailedOrCanceled();
+ ASSERT(cachedResource);
+ return handler(*cachedResource);
}
CSSImageSetValue::CSSImageSetValue(const CSSImageSetValue& cloneFrom)
float scaleFactor;
};
- bool hasFailedOrCanceledSubresources() const;
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
PassRefPtr<CSSImageSetValue> cloneForCSSOM() const;
return cachedImage(loader, CachedResourceLoader::defaultCachedResourceOptions());
}
-bool CSSImageValue::hasFailedOrCanceledSubresources() const
+bool CSSImageValue::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
if (!is<StyleCachedImage>(m_image.get()))
return false;
CachedResource* cachedResource = downcast<StyleCachedImage>(*m_image).cachedImage();
- if (!cachedResource)
- return true;
- return cachedResource->loadFailedOrCanceled();
+ ASSERT(cachedResource);
+ return handler(*cachedResource);
}
bool CSSImageValue::equals(const CSSImageValue& other) const
PassRefPtr<CSSValue> cloneForCSSOM() const;
- bool hasFailedOrCanceledSubresources() const;
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
bool equals(const CSSImageValue&) const;
downcast<CSSReflectValue>(*this).addSubresourceStyleURLs(urls, styleSheet);
}
-bool CSSValue::hasFailedOrCanceledSubresources() const
+bool CSSValue::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
// This should get called for internal instances only.
ASSERT(!isCSSOMSafe());
if (is<CSSValueList>(*this))
- return downcast<CSSValueList>(*this).hasFailedOrCanceledSubresources();
+ return downcast<CSSValueList>(*this).traverseSubresources(handler);
if (is<CSSFontFaceSrcValue>(*this))
- return downcast<CSSFontFaceSrcValue>(*this).hasFailedOrCanceledSubresources();
+ return downcast<CSSFontFaceSrcValue>(*this).traverseSubresources(handler);
if (is<CSSImageValue>(*this))
- return downcast<CSSImageValue>(*this).hasFailedOrCanceledSubresources();
+ return downcast<CSSImageValue>(*this).traverseSubresources(handler);
if (is<CSSCrossfadeValue>(*this))
- return downcast<CSSCrossfadeValue>(*this).hasFailedOrCanceledSubresources();
+ return downcast<CSSCrossfadeValue>(*this).traverseSubresources(handler);
if (is<CSSFilterImageValue>(*this))
- return downcast<CSSFilterImageValue>(*this).hasFailedOrCanceledSubresources();
+ return downcast<CSSFilterImageValue>(*this).traverseSubresources(handler);
#if ENABLE(CSS_IMAGE_SET)
if (is<CSSImageSetValue>(*this))
- return downcast<CSSImageSetValue>(*this).hasFailedOrCanceledSubresources();
+ return downcast<CSSImageSetValue>(*this).traverseSubresources(handler);
#endif
return false;
}
namespace WebCore {
+class CachedResource;
class StyleSheetContents;
// FIXME: The current CSSValue and subclasses should be turned into internal types (StyleValue).
void addSubresourceStyleURLs(ListHashSet<URL>&, const StyleSheetContents*) const;
- bool hasFailedOrCanceledSubresources() const;
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
bool equals(const CSSValue&) const;
m_values[i].get().addSubresourceStyleURLs(urls, styleSheet);
}
-bool CSSValueList::hasFailedOrCanceledSubresources() const
+bool CSSValueList::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
for (unsigned i = 0; i < m_values.size(); ++i) {
- if (m_values[i].get().hasFailedOrCanceledSubresources())
+ if (m_values[i].get().traverseSubresources(handler))
return true;
}
return false;
void addSubresourceStyleURLs(ListHashSet<URL>&, const StyleSheetContents*) const;
- bool hasFailedOrCanceledSubresources() const;
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
PassRefPtr<CSSValueList> cloneForCSSOM() const;
propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
}
-bool StyleProperties::hasFailedOrCanceledSubresources() const
+bool StyleProperties::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
unsigned size = propertyCount();
for (unsigned i = 0; i < size; ++i) {
- if (propertyAt(i).value()->hasFailedOrCanceledSubresources())
+ if (propertyAt(i).value()->traverseSubresources(handler))
return true;
}
return false;
namespace WebCore {
class CSSStyleDeclaration;
+class CachedResource;
class ImmutableStyleProperties;
class URL;
class MutableStyleProperties;
bool isMutable() const { return m_isMutable; }
bool hasCSSOMWrapper() const;
- bool hasFailedOrCanceledSubresources() const;
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
static unsigned averageSizeInBytes();
}
}
-static bool childRulesHaveFailedOrCanceledSubresources(const Vector<RefPtr<StyleRuleBase>>& rules)
+static bool traverseSubresourcesInRules(const Vector<RefPtr<StyleRuleBase>>& rules, const std::function<bool (const CachedResource&)>& handler)
{
for (auto& rule : rules) {
switch (rule->type()) {
case StyleRuleBase::Style:
- if (downcast<StyleRule>(*rule).properties().hasFailedOrCanceledSubresources())
+ if (downcast<StyleRule>(*rule).properties().traverseSubresources(handler))
return true;
break;
case StyleRuleBase::FontFace:
- if (downcast<StyleRuleFontFace>(*rule).properties().hasFailedOrCanceledSubresources())
+ if (downcast<StyleRuleFontFace>(*rule).properties().traverseSubresources(handler))
return true;
break;
case StyleRuleBase::Media:
- if (childRulesHaveFailedOrCanceledSubresources(downcast<StyleRuleMedia>(*rule).childRules()))
+ if (traverseSubresourcesInRules(downcast<StyleRuleMedia>(*rule).childRules(), handler))
return true;
break;
case StyleRuleBase::Region:
- if (childRulesHaveFailedOrCanceledSubresources(downcast<StyleRuleRegion>(*rule).childRules()))
+ if (traverseSubresourcesInRules(downcast<StyleRuleRegion>(*rule).childRules(), handler))
return true;
break;
case StyleRuleBase::Import:
return false;
}
-bool StyleSheetContents::hasFailedOrCanceledSubresources() const
+bool StyleSheetContents::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
{
- ASSERT(isCacheable());
- return childRulesHaveFailedOrCanceledSubresources(m_childRules);
+ for (auto& importRule : m_importRules) {
+ if (!importRule->styleSheet())
+ continue;
+ if (traverseSubresourcesInRules(importRule->styleSheet()->m_childRules, handler))
+ return true;
+ }
+ return traverseSubresourcesInRules(m_childRules, handler);
+}
+
+bool StyleSheetContents::subresourcesAllowReuse(CachePolicy cachePolicy) const
+{
+ bool hasFailedOrExpiredResources = traverseSubresources([cachePolicy](const CachedResource& resource) {
+ if (resource.loadFailedOrCanceled())
+ return true;
+ // We can't revalidate subresources individually so don't use reuse the parsed sheet if they need revalidation.
+ if (resource.makeRevalidationDecision(cachePolicy) != CachedResource::RevalidationDecision::No)
+ return true;
+ return false;
+ });
+ return !hasFailedOrExpiredResources;
+}
+
+bool StyleSheetContents::isLoadingSubresources() const
+{
+ return traverseSubresources([](const CachedResource& resource) {
+ return resource.isLoading();
+ });
}
StyleSheetContents* StyleSheetContents::parentStyleSheet() const
#define StyleSheetContents_h
#include "CSSParserMode.h"
+#include "CachePolicy.h"
#include "URL.h"
#include <wtf/HashMap.h>
#include <wtf/ListHashSet.h>
class CSSStyleSheet;
class CachedCSSStyleSheet;
+class CachedResource;
class Document;
class Node;
class StyleRuleBase;
bool isCacheable() const;
bool isLoading() const;
+ bool subresourcesAllowReuse(CachePolicy) const;
+ WEBCORE_EXPORT bool isLoadingSubresources() const;
void checkLoaded();
void startLoadingDynamicSheet();
const String& charset() const { return m_parserContext.charset; }
bool loadCompleted() const { return m_loadCompleted; }
- bool hasFailedOrCanceledSubresources() const;
URL completeURL(const String& url) const;
void addSubresourceStyleURLs(ListHashSet<URL>&);
+ bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
void setIsUserStyleSheet(bool b) { m_isUserStyleSheet = b; }
bool isUserStyleSheet() const { return m_isUserStyleSheet; }
Ref<HTMLLinkElement> protect(*this);
CSSParserContext parserContext(document(), baseURL, charset);
+ auto cachePolicy = document().frame()->loader().subresourceCachePolicy();
- if (RefPtr<StyleSheetContents> restoredSheet = const_cast<CachedCSSStyleSheet*>(cachedStyleSheet)->restoreParsedStyleSheet(parserContext)) {
+ if (RefPtr<StyleSheetContents> restoredSheet = const_cast<CachedCSSStyleSheet*>(cachedStyleSheet)->restoreParsedStyleSheet(parserContext, cachePolicy)) {
ASSERT(restoredSheet->isCacheable());
ASSERT(!restoredSheet->isLoading());
setDecodedSize(0);
}
-PassRefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(const CSSParserContext& context)
+PassRefPtr<StyleSheetContents> CachedCSSStyleSheet::restoreParsedStyleSheet(const CSSParserContext& context, CachePolicy cachePolicy)
{
if (!m_parsedStyleSheetCache)
return 0;
- if (m_parsedStyleSheetCache->hasFailedOrCanceledSubresources()) {
+ if (!m_parsedStyleSheetCache->subresourcesAllowReuse(cachePolicy)) {
m_parsedStyleSheetCache->removedFromMemoryCache();
m_parsedStyleSheetCache.clear();
return 0;
const String sheetText(bool* hasValidMIMEType = nullptr) const;
- PassRefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&);
+ PassRefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&, CachePolicy);
void saveParsedStyleSheet(Ref<StyleSheetContents>&&);
private:
return !securityOrigin->taintsCanvas(response().url());
}
-bool CachedImage::mustRevalidateDueToCacheHeaders(const CachedResourceLoader& cachedResourceLoader, CachePolicy policy) const
+CachedResource::RevalidationDecision CachedImage::makeRevalidationDecision(CachePolicy cachePolicy) const
{
if (UNLIKELY(isManuallyCached())) {
// Do not revalidate manually cached images. This mechanism is used as a
// 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;
+ return RevalidationDecision::No;
}
- return CachedResource::mustRevalidateDueToCacheHeaders(cachedResourceLoader, policy);
+ return CachedResource::makeRevalidationDecision(cachePolicy);
}
} // namespace WebCore
void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio);
bool isManuallyCached() const { return m_isManuallyCached; }
- virtual bool mustRevalidateDueToCacheHeaders(const CachedResourceLoader&, CachePolicy) const override;
+ RevalidationDecision makeRevalidationDecision(CachePolicy) const override;
virtual void load(CachedResourceLoader&, const ResourceLoaderOptions&) override;
bool isOriginClean(SecurityOrigin*);
return m_response.hasCacheValidatorFields();
}
-static inline void logResourceRevalidationReason(Frame* frame, const String& reason)
-{
- if (frame)
- frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), DiagnosticLoggingKeys::reasonKey(), reason, ShouldSample::Yes);
-}
-
-bool CachedResource::mustRevalidateDueToCacheHeaders(const CachedResourceLoader& cachedResourceLoader, CachePolicy cachePolicy) const
+CachedResource::RevalidationDecision CachedResource::makeRevalidationDecision(CachePolicy cachePolicy) const
{
- ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyVerify);
-
- if (cachePolicy == CachePolicyRevalidate) {
- logResourceRevalidationReason(cachedResourceLoader.frame(), DiagnosticLoggingKeys::reloadKey());
- return true;
- }
-
- if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) {
- LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this);
+ switch (cachePolicy) {
+ case CachePolicyHistoryBuffer:
+ return RevalidationDecision::No;
+
+ case CachePolicyReload:
+ case CachePolicyRevalidate:
+ return RevalidationDecision::YesDueToCachePolicy;
+
+ case CachePolicyVerify:
+ if (m_response.cacheControlContainsNoCache())
+ return RevalidationDecision::YesDueToNoCache;
+ // FIXME: Cache-Control:no-store should prevent storing, not reuse.
if (m_response.cacheControlContainsNoStore())
- logResourceRevalidationReason(cachedResourceLoader.frame(), DiagnosticLoggingKeys::noStoreKey());
- else
- logResourceRevalidationReason(cachedResourceLoader.frame(), DiagnosticLoggingKeys::noCacheKey());
+ return RevalidationDecision::YesDueToNoStore;
- return true;
- }
+ if (isExpired())
+ return RevalidationDecision::YesDueToExpired;
- // CachePolicyVerify
- if (isExpired()) {
- LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this);
- logResourceRevalidationReason(cachedResourceLoader.frame(), DiagnosticLoggingKeys::isExpiredKey());
- return true;
- }
-
- return false;
+ return RevalidationDecision::No;
+ };
+ ASSERT_NOT_REACHED();
+ return RevalidationDecision::No;
}
bool CachedResource::redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot reuseExpiredRedirection) const
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 canUseCacheValidator() const;
- virtual bool mustRevalidateDueToCacheHeaders(const CachedResourceLoader&, CachePolicy) const;
+ enum class RevalidationDecision { No, YesDueToCachePolicy, YesDueToNoStore, YesDueToNoCache, YesDueToExpired };
+ virtual RevalidationDecision makeRevalidationDecision(CachePolicy) const;
bool redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot) const;
bool isCacheValidator() const { return m_resourceToRevalidate; }
}
#endif // ENABLE(RESOURCE_TIMING)
+static void logRevalidation(const String& reason, DiagnosticLoggingClient& logClient)
+{
+ logClient.logDiagnosticMessageWithValue(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), DiagnosticLoggingKeys::reasonKey(), reason, ShouldSample::Yes);
+}
+
+static void logResourceRevalidationDecision(CachedResource::RevalidationDecision reason, const Frame* frame)
+{
+ if (!frame)
+ return;
+ auto& logClient = frame->mainFrame().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;
+ }
+}
+
CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption defer) const
{
if (!existingResource)
if (existingResource->isLoading())
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(*this, cachePolicy(type))) {
+ if (revalidationDecision != CachedResource::RevalidationDecision::No) {
// See if the resource has usable ETag or Last-modified headers.
if (existingResource->canUseCacheValidator())
return Revalidate;
#include "HTMLIFrameElement.h"
#include "HTMLImageElement.h"
#include "HTMLInputElement.h"
+#include "HTMLLinkElement.h"
#include "HTMLNames.h"
#include "HTMLPlugInElement.h"
#include "HTMLPreloadScanner.h"
return "Error";
}
+bool Internals::isSharingStyleSheetContents(Element* a, Element* b)
+{
+ if (!is<HTMLLinkElement>(a) || !is<HTMLLinkElement>(b))
+ return false;
+ auto& aLink = downcast<HTMLLinkElement>(*a);
+ auto& bLink = downcast<HTMLLinkElement>(*b);
+ if (!aLink.sheet() || !bLink.sheet())
+ return false;
+ return &aLink.sheet()->contents() == &bLink.sheet()->contents();
+}
+
+bool Internals::isStyleSheetLoadingSubresources(Element* link)
+{
+ if (!is<HTMLLinkElement>(link))
+ return false;
+ auto& linkElement = downcast<HTMLLinkElement>(*link);
+ return linkElement.sheet() && linkElement.sheet()->contents().isLoadingSubresources();
+}
+
static ResourceRequestCachePolicy stringToResourceRequestCachePolicy(const String& policy)
{
if (policy == "UseProtocolCachePolicy")
bool isPreloaded(const String& url);
bool isLoadingFromMemoryCache(const String& url);
String xhrResponseSource(XMLHttpRequest*);
+ bool isSharingStyleSheetContents(Element* linkA, Element* linkB);
+ bool isStyleSheetLoadingSubresources(Element* link);
void setOverrideCachePolicy(const String&);
void setOverrideResourceLoadPriority(const String&);
boolean isPreloaded(DOMString url);
boolean isLoadingFromMemoryCache(DOMString url);
DOMString xhrResponseSource(XMLHttpRequest xhr);
+ boolean isSharingStyleSheetContents(Element linkA, Element linkB);
+ boolean isStyleSheetLoadingSubresources(Element link);
void clearMemoryCache();
void pruneMemoryCacheToSize(long size);
long memoryCacheSize();