Link loader should use FetchOptions::mode according its crossOrigin attribute
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Sep 2016 07:41:49 +0000 (07:41 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Sep 2016 07:41:49 +0000 (07:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161859

Patch by Youenn Fablet <youenn@apple.com> on 2016-09-16
Reviewed by Sam Weinig.

Source/WebCore:

Tests: http/tests/security/cached-cross-origin-preloaded-css-stylesheet.html
       http/tests/security/cached-cross-origin-preloading-css-stylesheet.html

Setting fetch mode according crossorigin attribute for link preload elements.
This allows calling onerror callback for CORS failures, which was not the case before the patch.

Making cached CSS stylesheet reusable accross origins and fetch modes.

* loader/LinkLoader.cpp:
(WebCore::LinkLoader::preloadIfNeeded): Using CachedResourceRequest::setAsPotentiallyCrossOrigin to set fetch mode.
* loader/cache/CachedCSSStyleSheet.cpp:
(WebCore::CachedCSSStyleSheet::sheetText): clean-up.
(WebCore::CachedCSSStyleSheet::setBodyDataFrom): Implementing data init for cached css stylesheets.
* loader/cache/CachedCSSStyleSheet.h:
* loader/cache/CachedResourceLoader.cpp:
(WebCore::CachedResourceLoader::updateCachedResourceWithCurrentRequest): Activating update support for stylesheets.
(WebCore::CachedResourceLoader::requestResource): Fixing for matching cached resources that need being reloaded due to different origin/fetch mode.

LayoutTests:

* http/tests/security/cached-cross-origin-preloaded-css-stylesheet-expected.txt: Added.
* http/tests/security/cached-cross-origin-preloaded-css-stylesheet.html: Added.
* http/tests/security/cached-cross-origin-preloading-css-stylesheet-expected.txt: Added.
* http/tests/security/cached-cross-origin-preloading-css-stylesheet.html: Added.
* http/tests/security/resources/allow-if-origin.php: Adding support for allowing credentials and setting contentType.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@206010 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/http/tests/security/cached-cross-origin-preloaded-css-stylesheet-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/cached-cross-origin-preloaded-css-stylesheet.html [new file with mode: 0644]
LayoutTests/http/tests/security/cached-cross-origin-preloading-css-stylesheet-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/cached-cross-origin-preloading-css-stylesheet.html [new file with mode: 0644]
LayoutTests/http/tests/security/resources/allow-if-origin.php
Source/WebCore/ChangeLog
Source/WebCore/loader/LinkLoader.cpp
Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp
Source/WebCore/loader/cache/CachedCSSStyleSheet.h
Source/WebCore/loader/cache/CachedResourceLoader.cpp

index 50633af..38528a8 100644 (file)
@@ -1,5 +1,18 @@
 2016-09-16  Youenn Fablet  <youenn@apple.com>
 
+        Link loader should use FetchOptions::mode according its crossOrigin attribute
+        https://bugs.webkit.org/show_bug.cgi?id=161859
+
+        Reviewed by Sam Weinig.
+
+        * http/tests/security/cached-cross-origin-preloaded-css-stylesheet-expected.txt: Added.
+        * http/tests/security/cached-cross-origin-preloaded-css-stylesheet.html: Added.
+        * http/tests/security/cached-cross-origin-preloading-css-stylesheet-expected.txt: Added.
+        * http/tests/security/cached-cross-origin-preloading-css-stylesheet.html: Added.
+        * http/tests/security/resources/allow-if-origin.php: Adding support for allowing credentials and setting contentType.
+
+2016-09-16  Youenn Fablet  <youenn@apple.com>
+
         [Fetch API] Referrer and Origin header should not be considered as safe request headers
         https://bugs.webkit.org/show_bug.cgi?id=161902
 
diff --git a/LayoutTests/http/tests/security/cached-cross-origin-preloaded-css-stylesheet-expected.txt b/LayoutTests/http/tests/security/cached-cross-origin-preloaded-css-stylesheet-expected.txt
new file mode 100644 (file)
index 0000000..fbcfdc3
--- /dev/null
@@ -0,0 +1,8 @@
+CONSOLE MESSAGE: Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+
+PASS: did not load http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials (cors mode)
+PASS: loaded successfuly http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss2.css&contentType=text/css&allowCredentials (cors mode)
+PASS: loaded successfuly http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials (no-cors mode)
+PASS: loaded successfuly http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss1.css&contentType=text/css&allowCredentials (cors mode)
+PASS: loaded successfuly http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss2.css&contentType=text/css&allowCredentials (no-cors mode)
diff --git a/LayoutTests/http/tests/security/cached-cross-origin-preloaded-css-stylesheet.html b/LayoutTests/http/tests/security/cached-cross-origin-preloaded-css-stylesheet.html
new file mode 100644 (file)
index 0000000..03d10b5
--- /dev/null
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<script>
+if (window.internals)
+    internals.setLinkPreloadSupport(true);
+if (window.testRunner) {
+   testRunner.dumpAsText();
+   testRunner.waitUntilDone();
+}
+function errorPreload(e)
+{
+    console.log("unexpected preload result " + e);
+    preloaded();
+}
+var numPreloads = 0;
+function preloaded()
+{
+    if (++numPreloads == 4)
+        doTests();
+}
+</script>
+<link rel=preload onload="preloaded()" onerror="errorPreload(1)" as=style href="http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials">
+<link rel=preload onload="preloaded()" onerror="errorPreload(2)" as=style href="http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss1.css&contentType=text/css&allowCredentials">
+<link rel=preload onload="preloaded()" onerror="errorPreload(3)" as=style crossorigin=use-credentials href="http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss2.css&contentType=text/css&allowCredentials">
+<link rel=preload onerror="preloaded()" onload="errorPreload(4)" as=style crossorigin=use-credentials href="http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss2.css&contentType=text/css&allowCredentials">
+<div id ="log"></div>
+<script>
+if (window.testRunner) {
+   testRunner.dumpAsText();
+   testRunner.waitUntilDone();
+}
+
+var maxResults = 5;
+var results = [];
+
+function checkDone()
+{
+    if (results.length !== maxResults)
+        return;
+    var log = "";
+    results.sort();
+    for (value of results)
+        log += "<br>" + value;
+    document.getElementById('log').innerHTML = log;
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function logOnLoad(url, isCORS, expectSuccess)
+{
+    results.push((expectSuccess ? "PASS: " : "FAIL: ") + "loaded successfuly " + url + (isCORS ? " (cors mode)" : " (no-cors mode)"));
+    checkDone();
+}
+
+function logOnError(url, isCORS, expectSuccess)
+{
+    results.push((!expectSuccess ? "PASS: " : "FAIL: ") + "did not load " + url + (isCORS ? " (cors mode)" : " (no-cors mode)"));
+    checkDone();
+}
+
+function createLinkElement(url, isCORS, expectSuccess)
+{
+    link = document.createElement('link');
+    link.href = url;
+    link.rel = "stylesheet";
+    link.type = "text/css";
+    if (isCORS)
+        link.crossOrigin = "use-credentials";
+    link.onload = () => { logOnLoad(url, isCORS, expectSuccess); };
+    link.onerror = () => { logOnError(url, isCORS, expectSuccess); };
+    return link;
+}
+
+function doTests()
+{
+    document.body.appendChild(createLinkElement("http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials", true, false));
+    document.body.appendChild(createLinkElement("http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials", false, true));
+    document.body.appendChild(createLinkElement("http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss1.css&contentType=text/css&allowCredentials", true, true));
+    document.body.appendChild(createLinkElement("http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss2.css&contentType=text/css&allowCredentials", true, true));
+    document.body.appendChild(createLinkElement("http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss2.css&contentType=text/css&allowCredentials", false, true));
+}
+</script>
diff --git a/LayoutTests/http/tests/security/cached-cross-origin-preloading-css-stylesheet-expected.txt b/LayoutTests/http/tests/security/cached-cross-origin-preloading-css-stylesheet-expected.txt
new file mode 100644 (file)
index 0000000..fbcfdc3
--- /dev/null
@@ -0,0 +1,8 @@
+CONSOLE MESSAGE: Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+
+PASS: did not load http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials (cors mode)
+PASS: loaded successfuly http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss2.css&contentType=text/css&allowCredentials (cors mode)
+PASS: loaded successfuly http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials (no-cors mode)
+PASS: loaded successfuly http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss1.css&contentType=text/css&allowCredentials (cors mode)
+PASS: loaded successfuly http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss2.css&contentType=text/css&allowCredentials (no-cors mode)
diff --git a/LayoutTests/http/tests/security/cached-cross-origin-preloading-css-stylesheet.html b/LayoutTests/http/tests/security/cached-cross-origin-preloading-css-stylesheet.html
new file mode 100644 (file)
index 0000000..ce4b896
--- /dev/null
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+if (window.internals)
+    internals.setLinkPreloadSupport(true);
+if (window.testRunner) {
+   testRunner.dumpAsText();
+   testRunner.waitUntilDone();
+}
+function errorPreload(e)
+{
+    console.log("unexpected loading result for preload " + e);
+}
+</script>
+<link rel=preload onerror="errorPreload(1)" as=style href="http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials">
+<link rel=preload onerror="errorPreload(2)" as=style href="http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss1.css&contentType=text/css&allowCredentials">
+<link rel=preload onerror="errorPreload(3)" as=style crossorigin="with-credentials" href="http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss2.css&contentType=text/css&allowCredentials"><link rel=preload onload="errorPreload(4)" as=style crossorigin="with-credentials" href="http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss2.css&contentType=text/css&allowCredentials">
+<link rel=preload onload="errorPreload(4)" as=style crossorigin="with-credentials" href="http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss2.css&contentType=text/css&allowCredentials">
+<div id="log"></div>
+<script>
+if (window.testRunner) {
+   testRunner.dumpAsText();
+   testRunner.waitUntilDone();
+}
+
+var maxResults = 5;
+var results = [];
+
+function checkDone()
+{
+    if (results.length !== maxResults)
+        return;
+    var log = "";
+    results.sort();
+    for (value of results)
+        log += "<br>" + value;
+    document.getElementById('log').innerHTML = log;
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function logOnLoad(url, isCORS, expectSuccess)
+{
+    results.push((expectSuccess ? "PASS: " : "FAIL: ") + "loaded successfuly " + url + (isCORS ? " (cors mode)" : " (no-cors mode)"));
+    checkDone();
+}
+
+function logOnError(url, isCORS, expectSuccess)
+{
+    results.push((!expectSuccess ? "PASS: " : "FAIL: ") + "did not load " + url + (isCORS ? " (cors mode)" : " (no-cors mode)"));
+    checkDone();
+}
+
+function createLinkElement(url, isCORS, expectSuccess)
+{
+    link = document.createElement('link');
+    link.href = url;
+    link.rel = "stylesheet";
+    link.type = "text/css";
+    if (isCORS)
+        link.crossOrigin = "with-credentials";
+    link.onload = () => { logOnLoad(url, isCORS, expectSuccess); };
+    link.onerror = () => { logOnError(url, isCORS, expectSuccess); };
+    return link;
+}
+
+document.body.appendChild(createLinkElement("http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials", true, false));
+document.body.appendChild(createLinkElement("http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss1.css&contentType=text/css&allowCredentials", false, true));
+document.body.appendChild(createLinkElement("http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss1.css&contentType=text/css&allowCredentials", true, true));
+document.body.appendChild(createLinkElement("http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=xorigincss2.css&contentType=text/css&allowCredentials", true, true));
+document.body.appendChild(createLinkElement("http://localhost:8080/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2Flocalhost%3A8080&name=xorigincss2.css&contentType=text/css&allowCredentials", false, true));
+</script>
+</body>
+</html>
index 565fcc5..7c2d47d 100644 (file)
@@ -12,6 +12,10 @@ else if ($_SERVER["HTTP_ORIGIN"]) {
     header("Vary: Origin");
 }
 
+$allowCredentials = $_GET['allowCredentials'];
+if (isset($allowCredentials))
+    header("Access-Control-Allow-Credentials: true");
+
 $allowCache = $_GET['allowCache'];
 if (isset($allowCache))
     header("Cache-Control: max-age=100");
@@ -22,7 +26,12 @@ if (!isset($name))
 
 $fp = fopen($name, 'rb');
 
-header("Content-Type: image/png");
+$contentType = $_GET['contentType'];
+if (!isset($contentType))
+    $contentType = 'image/png';
+
+header("Content-Type: " . $contentType);
+
 header("Content-Length: " . filesize($name));
 
 fpassthru($fp);
index 2172dda..5acd471 100644 (file)
@@ -1,5 +1,30 @@
 2016-09-16  Youenn Fablet  <youenn@apple.com>
 
+        Link loader should use FetchOptions::mode according its crossOrigin attribute
+        https://bugs.webkit.org/show_bug.cgi?id=161859
+
+        Reviewed by Sam Weinig.
+
+        Tests: http/tests/security/cached-cross-origin-preloaded-css-stylesheet.html
+               http/tests/security/cached-cross-origin-preloading-css-stylesheet.html
+
+        Setting fetch mode according crossorigin attribute for link preload elements.
+        This allows calling onerror callback for CORS failures, which was not the case before the patch.
+
+        Making cached CSS stylesheet reusable accross origins and fetch modes.
+
+        * loader/LinkLoader.cpp:
+        (WebCore::LinkLoader::preloadIfNeeded): Using CachedResourceRequest::setAsPotentiallyCrossOrigin to set fetch mode.
+        * loader/cache/CachedCSSStyleSheet.cpp:
+        (WebCore::CachedCSSStyleSheet::sheetText): clean-up.
+        (WebCore::CachedCSSStyleSheet::setBodyDataFrom): Implementing data init for cached css stylesheets.
+        * loader/cache/CachedCSSStyleSheet.h:
+        * loader/cache/CachedResourceLoader.cpp:
+        (WebCore::CachedResourceLoader::updateCachedResourceWithCurrentRequest): Activating update support for stylesheets.
+        (WebCore::CachedResourceLoader::requestResource): Fixing for matching cached resources that need being reloaded due to different origin/fetch mode.
+
+2016-09-16  Youenn Fablet  <youenn@apple.com>
+
         [Fetch API] Referrer and Origin header should not be considered as safe request headers
         https://bugs.webkit.org/show_bug.cgi?id=161902
 
index aefa7eb..3c5cc8b 100644 (file)
@@ -161,11 +161,7 @@ void LinkLoader::preloadIfNeeded(const LinkRelAttribute& relAttribute, const URL
     CachedResourceRequest linkRequest(resourceRequest, CachedResource::defaultPriorityForResourceType(type.value()));
     linkRequest.setInitiator("link");
 
-    if (!crossOriginMode.isNull()) {
-        ASSERT(document.securityOrigin());
-        StoredCredentials allowCredentials = equalLettersIgnoringASCIICase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials;
-        updateRequestForAccessControl(linkRequest.mutableResourceRequest(), *document.securityOrigin(), allowCredentials);
-    }
+    linkRequest.setAsPotentiallyCrossOrigin(crossOriginMode, document);
     linkRequest.setForPreload(true);
     CachedResourceHandle<CachedResource> cachedLinkResource = document.cachedResourceLoader().preload(type.value(), linkRequest, emptyString(), CachedResourceLoader::ExplicitPreload);
 
index 4d37e58..f884bdb 100644 (file)
@@ -76,19 +76,29 @@ String CachedCSSStyleSheet::encoding() const
 {
     return m_decoder->encoding().name();
 }
-    
+
 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
     return m_decoder->decodeAndFlush(m_data->data(), m_data->size());
 }
 
+void CachedCSSStyleSheet::setBodyDataFrom(const CachedResource& resource)
+{
+    ASSERT(resource.type() == type());
+    const CachedCSSStyleSheet& sheet = static_cast<const CachedCSSStyleSheet&>(resource);
+
+    m_decoder = sheet.m_decoder;
+    m_decodedSheetText = sheet.m_decodedSheetText;
+    m_parsedStyleSheetCache = sheet.m_parsedStyleSheetCache;
+}
+
 void CachedCSSStyleSheet::finishLoading(SharedBuffer* data)
 {
     m_data = data;
index 342a8ff..5444e9c 100644 (file)
@@ -48,18 +48,20 @@ namespace WebCore {
 
     private:
         bool canUseSheet(MIMETypeCheck, bool* hasValidMIMEType) const;
-        bool mayTryReplaceEncodedData() const override { return true; }
+        bool mayTryReplaceEncodedData() const final { return true; }
 
-        void didAddClient(CachedResourceClient*) override;
+        void didAddClient(CachedResourceClient*) final;
 
-        void setEncoding(const String&) override;
-        String encoding() const override;
-        const TextResourceDecoder* textResourceDecoder() const override { return m_decoder.get(); }
-        void finishLoading(SharedBuffer*) override;
-        void destroyDecodedData() 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;
+
+        void setBodyDataFrom(const CachedResource&) final;
 
     protected:
-        void checkNotify() override;
+        void checkNotify() final;
 
         RefPtr<TextResourceDecoder> m_decoder;
         String m_decodedSheetText;
index e937e37..23961bf 100644 (file)
@@ -546,7 +546,7 @@ bool CachedResourceLoader::updateCachedResourceWithCurrentRequest(CachedResource
     CachedResource& resource = *resourceHandle;
 
     // FIXME: We should progressively extend this to other reusable resources
-    if (resource.type() != CachedResource::Type::ImageResource && resource.type() != CachedResource::Type::Script && resource.type() != CachedResource::Type::TextTrackResource)
+    if (resource.type() != CachedResource::Type::ImageResource && resource.type() != CachedResource::Type::Script && resource.type() != CachedResource::Type::TextTrackResource && resource.type() != CachedResource::Type::CSSStyleSheet)
         return false;
 
     bool shouldUpdate = resource.options().mode != request.options().mode || request.resourceRequest().httpOrigin() != resource.resourceRequest().httpOrigin();