Cached CSS image resources don't show up after reloading <http://nightly.webkit.org...
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 May 2015 23:09:13 +0000 (23:09 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 May 2015 23:09:13 +0000 (23:09 +0000)
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

36 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/cache/resources/non-shareable.css [new file with mode: 0644]
LayoutTests/http/tests/cache/resources/shareable.css [new file with mode: 0644]
LayoutTests/http/tests/cache/resources/stylesheet-html.php [new file with mode: 0644]
LayoutTests/http/tests/cache/stylesheet-sharing-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cache/stylesheet-sharing.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/CSSCrossfadeValue.cpp
Source/WebCore/css/CSSCrossfadeValue.h
Source/WebCore/css/CSSFilterImageValue.cpp
Source/WebCore/css/CSSFilterImageValue.h
Source/WebCore/css/CSSFontFaceSrcValue.cpp
Source/WebCore/css/CSSFontFaceSrcValue.h
Source/WebCore/css/CSSImageSetValue.cpp
Source/WebCore/css/CSSImageSetValue.h
Source/WebCore/css/CSSImageValue.cpp
Source/WebCore/css/CSSImageValue.h
Source/WebCore/css/CSSValue.cpp
Source/WebCore/css/CSSValue.h
Source/WebCore/css/CSSValueList.cpp
Source/WebCore/css/CSSValueList.h
Source/WebCore/css/StyleProperties.cpp
Source/WebCore/css/StyleProperties.h
Source/WebCore/css/StyleSheetContents.cpp
Source/WebCore/css/StyleSheetContents.h
Source/WebCore/html/HTMLLinkElement.cpp
Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp
Source/WebCore/loader/cache/CachedCSSStyleSheet.h
Source/WebCore/loader/cache/CachedImage.cpp
Source/WebCore/loader/cache/CachedImage.h
Source/WebCore/loader/cache/CachedResource.cpp
Source/WebCore/loader/cache/CachedResource.h
Source/WebCore/loader/cache/CachedResourceLoader.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 6b4d852..7561a37 100644 (file)
@@ -1,3 +1,24 @@
+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
diff --git a/LayoutTests/http/tests/cache/resources/non-shareable.css b/LayoutTests/http/tests/cache/resources/non-shareable.css
new file mode 100644 (file)
index 0000000..1adc2ae
--- /dev/null
@@ -0,0 +1,4 @@
+#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) }
diff --git a/LayoutTests/http/tests/cache/resources/shareable.css b/LayoutTests/http/tests/cache/resources/shareable.css
new file mode 100644 (file)
index 0000000..cbac1e4
--- /dev/null
@@ -0,0 +1,4 @@
+#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) }
diff --git a/LayoutTests/http/tests/cache/resources/stylesheet-html.php b/LayoutTests/http/tests/cache/resources/stylesheet-html.php
new file mode 100644 (file)
index 0000000..75fb648
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+echo "<link rel=stylesheet href=" . $_GET['sheet'] . ">";
+?>
+<div id=test1></div>
+<div id=test2></div>
diff --git a/LayoutTests/http/tests/cache/stylesheet-sharing-expected.txt b/LayoutTests/http/tests/cache/stylesheet-sharing-expected.txt
new file mode 100644 (file)
index 0000000..037a79d
--- /dev/null
@@ -0,0 +1,8 @@
+shareable.css
+PASS styleSheetContentsShared is true
+non-shareable.css
+PASS styleSheetContentsShared is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  
diff --git a/LayoutTests/http/tests/cache/stylesheet-sharing.html b/LayoutTests/http/tests/cache/stylesheet-sharing.html
new file mode 100644 (file)
index 0000000..d48f67a
--- /dev/null
@@ -0,0 +1,50 @@
+<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>
index f34ca47..582d74a 100644 (file)
@@ -1,3 +1,100 @@
+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
index ea4db6f..0d7a47d 100644 (file)
@@ -174,11 +174,11 @@ void CSSCrossfadeValue::CrossfadeSubimageObserverProxy::imageChanged(CachedImage
         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;
 }
index 52f2226..e94eaea 100644 (file)
@@ -63,7 +63,7 @@ public:
 
     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;
 
index 4cd4181..b8f38a3 100644 (file)
@@ -149,11 +149,11 @@ void CSSFilterImageValue::FilterSubimageObserverProxy::imageChanged(CachedImage*
         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
index 1245cd6..156096d 100644 (file)
@@ -64,7 +64,7 @@ public:
 
     void loadSubimages(CachedResourceLoader&);
 
-    bool hasFailedOrCanceledSubresources() const;
+    bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
 
     bool equals(const CSSFilterImageValue&) const;
 
index f100574..27c5836 100644 (file)
@@ -91,11 +91,11 @@ void CSSFontFaceSrcValue::addSubresourceStyleURLs(ListHashSet<URL>& urls, 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)
index 7a980db..7e49c21 100644 (file)
@@ -68,7 +68,7 @@ public:
 
     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);
 
index 2b3c5d1..3e7806b 100644 (file)
@@ -184,14 +184,13 @@ String CSSImageSetValue::customCSSText() const
     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)
index acfd556..70a4b48 100644 (file)
@@ -62,7 +62,7 @@ public:
         float scaleFactor;
     };
 
-    bool hasFailedOrCanceledSubresources() const;
+    bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
 
     PassRefPtr<CSSImageSetValue> cloneForCSSOM() const;
 
index b3d93b6..cef4ea8 100644 (file)
@@ -99,14 +99,13 @@ StyleCachedImage* CSSImageValue::cachedImage(CachedResourceLoader& loader)
     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
index 0cd86a0..5291a63 100644 (file)
@@ -50,7 +50,7 @@ public:
 
     PassRefPtr<CSSValue> cloneForCSSOM() const;
 
-    bool hasFailedOrCanceledSubresources() const;
+    bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
 
     bool equals(const CSSImageValue&) const;
 
index cd17eda..431dd45 100644 (file)
@@ -125,24 +125,24 @@ void CSSValue::addSubresourceStyleURLs(ListHashSet<URL>& urls, const StyleSheetC
         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;
 }
index 13a652f..9a40686 100644 (file)
@@ -30,6 +30,7 @@
 
 namespace WebCore {
 
+class CachedResource;
 class StyleSheetContents;
     
 // FIXME: The current CSSValue and subclasses should be turned into internal types (StyleValue).
@@ -122,7 +123,7 @@ public:
 
     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;
 
index 8ce69ca..43c1afe 100644 (file)
@@ -151,10 +151,10 @@ void CSSValueList::addSubresourceStyleURLs(ListHashSet<URL>& urls, const StyleSh
         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;
index a473cb7..5f5f8eb 100644 (file)
@@ -74,7 +74,7 @@ public:
 
     void addSubresourceStyleURLs(ListHashSet<URL>&, const StyleSheetContents*) const;
 
-    bool hasFailedOrCanceledSubresources() const;
+    bool traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const;
     
     PassRefPtr<CSSValueList> cloneForCSSOM() const;
 
index 2e570d3..b90908d 100644 (file)
@@ -1074,11 +1074,11 @@ void StyleProperties::addSubresourceStyleURLs(ListHashSet<URL>& urls, StyleSheet
         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;
index 450bd15..6f3024c 100644 (file)
@@ -37,6 +37,7 @@
 namespace WebCore {
 
 class CSSStyleDeclaration;
+class CachedResource;
 class ImmutableStyleProperties;
 class URL;
 class MutableStyleProperties;
@@ -107,7 +108,7 @@ public:
     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();
 
index c2312c3..951f9ff 100644 (file)
@@ -423,24 +423,24 @@ void StyleSheetContents::addSubresourceStyleURLs(ListHashSet<URL>& urls)
     }
 }
 
-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:
@@ -463,10 +463,35 @@ static bool childRulesHaveFailedOrCanceledSubresources(const Vector<RefPtr<Style
     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
index 796a72a..ed6ee13 100644 (file)
@@ -22,6 +22,7 @@
 #define StyleSheetContents_h
 
 #include "CSSParserMode.h"
+#include "CachePolicy.h"
 #include "URL.h"
 #include <wtf/HashMap.h>
 #include <wtf/ListHashSet.h>
@@ -34,6 +35,7 @@ namespace WebCore {
 
 class CSSStyleSheet;
 class CachedCSSStyleSheet;
+class CachedResource;
 class Document;
 class Node;
 class StyleRuleBase;
@@ -67,6 +69,8 @@ public:
     bool isCacheable() const;
 
     bool isLoading() const;
+    bool subresourcesAllowReuse(CachePolicy) const;
+    WEBCORE_EXPORT bool isLoadingSubresources() const;
 
     void checkLoaded();
     void startLoadingDynamicSheet();
@@ -78,10 +82,10 @@ public:
     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; }
index 65e653a..b29b425 100644 (file)
@@ -321,8 +321,9 @@ void HTMLLinkElement::setCSSStyleSheet(const String& href, const URL& baseURL, c
     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());
 
index 7ad7930..7c2fe7d 100644 (file)
@@ -143,11 +143,11 @@ void CachedCSSStyleSheet::destroyDecodedData()
     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;
index afa66ab..d0bcd74 100644 (file)
@@ -43,7 +43,7 @@ namespace WebCore {
 
         const String sheetText(bool* hasValidMIMEType = nullptr) const;
 
-        PassRefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&);
+        PassRefPtr<StyleSheetContents> restoreParsedStyleSheet(const CSSParserContext&, CachePolicy);
         void saveParsedStyleSheet(Ref<StyleSheetContents>&&);
 
     private:
index cd59f63..67d7500 100644 (file)
@@ -515,7 +515,7 @@ bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin)
     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
@@ -523,9 +523,9 @@ bool CachedImage::mustRevalidateDueToCacheHeaders(const CachedResourceLoader& ca
         // 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
index e67988e..8859252 100644 (file)
@@ -84,7 +84,7 @@ public:
     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*);
index 3a95360..ecc33a9 100644 (file)
@@ -680,39 +680,30 @@ bool CachedResource::canUseCacheValidator() const
     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
index 7ee6a68..11c9ed2 100644 (file)
@@ -211,7 +211,7 @@ public:
     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(); }
@@ -229,7 +229,8 @@ public:
     
     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; }
index 3523979..88e39a7 100644 (file)
@@ -646,6 +646,34 @@ void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedR
 }
 #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)
@@ -738,8 +766,11 @@ CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalida
     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;
index 05ae21d..2826c2b 100644 (file)
@@ -56,6 +56,7 @@
 #include "HTMLIFrameElement.h"
 #include "HTMLImageElement.h"
 #include "HTMLInputElement.h"
+#include "HTMLLinkElement.h"
 #include "HTMLNames.h"
 #include "HTMLPlugInElement.h"
 #include "HTMLPreloadScanner.h"
@@ -441,6 +442,25 @@ String Internals::xhrResponseSource(XMLHttpRequest* xhr)
     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")
index 12d6f87..4d3c853 100644 (file)
@@ -91,6 +91,8 @@ public:
     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&);
 
index 6d23299..b8a8b73 100644 (file)
@@ -59,6 +59,8 @@ enum ResourceLoadPriority {
     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();