<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[211758] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/211758">211758</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2017-02-06 16:25:00 -0800 (Mon, 06 Feb 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Allow some schemes to opt-out of CORS
https://bugs.webkit.org/show_bug.cgi?id=167795

Patch by Youenn Fablet &lt;youennf@gmail.com&gt; on 2017-02-06
Reviewed by Alex Christensen.

Source/WebCore:

Test: http/tests/security/bypassing-cors-checks-for-extension-urls.html

Adding the possibility to opt out of CORS for DocumentThreadableLoader clients (fetch and XHR).
This is made specific to the case of user extension URLs for pages running user scripts.
Introducing a boolean flag in Page for that purpose.
Introducing a helper routine in SchemeRegistry to centralize the various user script extension schemes.

* loader/DocumentThreadableLoader.cpp:
(WebCore::DocumentThreadableLoader::DocumentThreadableLoader):
* page/Frame.cpp:
(WebCore::Frame::injectUserScripts):
* page/Page.h:
(WebCore::Page::setAsRunningUserScripts):
(WebCore::Page::isRunningUserScripts):
* platform/SchemeRegistry.cpp:
(WebCore::SchemeRegistry::isUserExtensionScheme):
* platform/SchemeRegistry.h:
* testing/Internals.cpp:
(WebCore::Internals::setAsRunningUserScripts):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

* http/tests/security/bypassing-cors-checks-for-extension-urls-expected.txt: Added.
* http/tests/security/bypassing-cors-checks-for-extension-urls.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreloaderDocumentThreadableLoadercpp">trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp</a></li>
<li><a href="#trunkSourceWebCorepageFramecpp">trunk/Source/WebCore/page/Frame.cpp</a></li>
<li><a href="#trunkSourceWebCorepagePageh">trunk/Source/WebCore/page/Page.h</a></li>
<li><a href="#trunkSourceWebCoreplatformSchemeRegistrycpp">trunk/Source/WebCore/platform/SchemeRegistry.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformSchemeRegistryh">trunk/Source/WebCore/platform/SchemeRegistry.h</a></li>
<li><a href="#trunkSourceWebCoretestingInternalscpp">trunk/Source/WebCore/testing/Internals.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsh">trunk/Source/WebCore/testing/Internals.h</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsidl">trunk/Source/WebCore/testing/Internals.idl</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestshttptestssecuritybypassingcorschecksforextensionurlsexpectedtxt">trunk/LayoutTests/http/tests/security/bypassing-cors-checks-for-extension-urls-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestssecuritybypassingcorschecksforextensionurlshtml">trunk/LayoutTests/http/tests/security/bypassing-cors-checks-for-extension-urls.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/LayoutTests/ChangeLog        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2017-02-06  Youenn Fablet  &lt;youennf@gmail.com&gt;
+
+        Allow some schemes to opt-out of CORS
+        https://bugs.webkit.org/show_bug.cgi?id=167795
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/security/bypassing-cors-checks-for-extension-urls-expected.txt: Added.
+        * http/tests/security/bypassing-cors-checks-for-extension-urls.html: Added.
+
</ins><span class="cx"> 2017-02-06  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Align [[OwnPropertyKeys]] with the HTML specification for cross-origin Window / Location objects
</span></span></pre></div>
<a id="trunkLayoutTestshttptestssecuritybypassingcorschecksforextensionurlsexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/security/bypassing-cors-checks-for-extension-urls-expected.txt (0 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/security/bypassing-cors-checks-for-extension-urls-expected.txt                                (rev 0)
+++ trunk/LayoutTests/http/tests/security/bypassing-cors-checks-for-extension-urls-expected.txt        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+CONSOLE MESSAGE: line 19: XMLHttpRequest cannot load safari-extension://test1. Cross origin requests are only supported for HTTP.
+This test ensures that XHR/Fetch will bypass CORS for user extension URLS in case the page is running user scripts.
+
+
+PASS Bypassing CORS on synchronous XHR - should trigger a CORS error message 
+PASS Bypassing CORS on synchronous XHR - should not trigger a CORS error message 
+PASS Bypassing CORS on asynchronous fetch - should not trigger a CORS error message 
+
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestssecuritybypassingcorschecksforextensionurlshtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/security/bypassing-cors-checks-for-extension-urls.html (0 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/security/bypassing-cors-checks-for-extension-urls.html                                (rev 0)
+++ trunk/LayoutTests/http/tests/security/bypassing-cors-checks-for-extension-urls.html        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;/resources/testharness.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;/resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;p&gt;
+    This test ensures that XHR/Fetch will bypass CORS for user extension URLS in case the page is running user scripts.
+&lt;/p&gt;
+&lt;script&gt;
+    if (!window.internals)
+        alert(&quot;This test requires WebKit internals API to work properly&quot;);
+
+    test(() =&gt; {
+        xhr = new XMLHttpRequest();
+        xhr.open(&quot;GET&quot;, &quot;safari-extension://test1&quot;, false);
+        assert_throws({name: &quot;NetworkError&quot;}, () =&gt; { xhr.send() });
+    }, &quot;Bypassing CORS on synchronous XHR - should trigger a CORS error message&quot;);
+
+    test(() =&gt; {
+        internals.setAsRunningUserScripts();
+
+        xhr = new XMLHttpRequest();
+        xhr.open(&quot;GET&quot;, &quot;safari-extension://test3&quot;, false);
+        assert_throws({name: &quot;NetworkError&quot;}, () =&gt; { xhr.send() });
+    }, &quot;Bypassing CORS on synchronous XHR - should not trigger a CORS error message&quot;);
+
+    promise_test((test) =&gt; {
+        return promise_rejects(test, new TypeError, fetch(&quot;safari-extension://test4&quot;));
+    }, &quot;Bypassing CORS on asynchronous fetch - should not trigger a CORS error message&quot;);
+&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/ChangeLog        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -1,3 +1,32 @@
</span><ins>+2017-02-06  Youenn Fablet  &lt;youennf@gmail.com&gt;
+
+        Allow some schemes to opt-out of CORS
+        https://bugs.webkit.org/show_bug.cgi?id=167795
+
+        Reviewed by Alex Christensen.
+
+        Test: http/tests/security/bypassing-cors-checks-for-extension-urls.html
+
+        Adding the possibility to opt out of CORS for DocumentThreadableLoader clients (fetch and XHR).
+        This is made specific to the case of user extension URLs for pages running user scripts.
+        Introducing a boolean flag in Page for that purpose.
+        Introducing a helper routine in SchemeRegistry to centralize the various user script extension schemes.
+
+        * loader/DocumentThreadableLoader.cpp:
+        (WebCore::DocumentThreadableLoader::DocumentThreadableLoader):
+        * page/Frame.cpp:
+        (WebCore::Frame::injectUserScripts):
+        * page/Page.h:
+        (WebCore::Page::setAsRunningUserScripts):
+        (WebCore::Page::isRunningUserScripts):
+        * platform/SchemeRegistry.cpp:
+        (WebCore::SchemeRegistry::isUserExtensionScheme):
+        * platform/SchemeRegistry.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::setAsRunningUserScripts):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
</ins><span class="cx"> 2017-02-06  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Align [[OwnPropertyKeys]] with the HTML specification for cross-origin Window / Location objects
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderDocumentThreadableLoadercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -110,6 +110,11 @@
</span><span class="cx">     if (m_async &amp;&amp; m_options.mode == FetchOptions::Mode::Cors)
</span><span class="cx">         m_originalHeaders = request.httpHeaderFields();
</span><span class="cx"> 
</span><ins>+    if (document.page() &amp;&amp; document.page()-&gt;isRunningUserScripts() &amp;&amp; SchemeRegistry::isUserExtensionScheme(request.url().protocol().toStringWithoutCopying())) {
+        m_options.mode = FetchOptions::Mode::NoCors;
+        m_options.filteringPolicy = ResponseFilteringPolicy::Disable;
+    }
+
</ins><span class="cx">     // As per step 11 of https://fetch.spec.whatwg.org/#main-fetch, data scheme (if same-origin data-URL flag is set) and about scheme are considered same-origin.
</span><span class="cx">     if (request.url().protocolIsData())
</span><span class="cx">         m_sameOriginRequest = options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set;
</span></span></pre></div>
<a id="trunkSourceWebCorepageFramecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/Frame.cpp (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/Frame.cpp        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/page/Frame.cpp        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -710,8 +710,10 @@
</span><span class="cx">         if (script.injectedFrames() == InjectInTopFrameOnly &amp;&amp; ownerElement())
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        if (script.injectionTime() == injectionTime &amp;&amp; UserContentURLPattern::matchesPatterns(document-&gt;url(), script.whitelist(), script.blacklist()))
</del><ins>+        if (script.injectionTime() == injectionTime &amp;&amp; UserContentURLPattern::matchesPatterns(document-&gt;url(), script.whitelist(), script.blacklist())) {
+            m_page-&gt;setAsRunningUserScripts();
</ins><span class="cx">             m_script-&gt;evaluateInWorld(ScriptSourceCode(script.source(), script.url()), world);
</span><ins>+        }
</ins><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorepagePageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/Page.h (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/Page.h        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/page/Page.h        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -391,6 +391,9 @@
</span><span class="cx">     void setResourceUsageOverlayVisible(bool);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    void setAsRunningUserScripts() { m_isRunningUserScripts = true; }
+    bool isRunningUserScripts() const { return m_isRunningUserScripts; }
+
</ins><span class="cx">     void setDebugger(JSC::Debugger*);
</span><span class="cx">     JSC::Debugger* debugger() const { return m_debugger; }
</span><span class="cx"> 
</span><span class="lines">@@ -775,6 +778,8 @@
</span><span class="cx">     std::optional&lt;EventThrottlingBehavior&gt; m_eventThrottlingBehaviorOverride;
</span><span class="cx"> 
</span><span class="cx">     std::unique_ptr&lt;PerformanceMonitor&gt; m_performanceMonitor;
</span><ins>+
+    bool m_isRunningUserScripts { false };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> inline PageGroup&amp; Page::group()
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformSchemeRegistrycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/SchemeRegistry.cpp (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/SchemeRegistry.cpp        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/platform/SchemeRegistry.cpp        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -357,4 +357,14 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+bool SchemeRegistry::isUserExtensionScheme(const String&amp; scheme)
+{
+    UNUSED_PARAM(scheme);
+#if PLATFORM(MAC)
+    if (scheme == &quot;safari-extension&quot;)
+        return true;
+#endif
+    return false;
+}
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformSchemeRegistryh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/SchemeRegistry.h (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/SchemeRegistry.h        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/platform/SchemeRegistry.h        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -99,6 +99,8 @@
</span><span class="cx">     WEBCORE_EXPORT static void registerURLSchemeAsCachePartitioned(const String&amp; scheme);
</span><span class="cx">     static bool shouldPartitionCacheForURLScheme(const String&amp; scheme);
</span><span class="cx"> #endif
</span><ins>+
+    static bool isUserExtensionScheme(const String&amp; scheme);
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.cpp (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.cpp        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/testing/Internals.cpp        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -3690,4 +3690,10 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+void Internals::setAsRunningUserScripts(Document&amp; document)
+{
+    if (document.page())
+        document.page()-&gt;setAsRunningUserScripts();
+}
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.h (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.h        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/testing/Internals.h        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -526,6 +526,8 @@
</span><span class="cx">     void setQuickLookPassword(const String&amp;);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    void setAsRunningUserScripts(Document&amp;);
+
</ins><span class="cx"> private:
</span><span class="cx">     explicit Internals(Document&amp;);
</span><span class="cx">     Document* contextDocument() const;
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsidl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.idl (211757 => 211758)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.idl        2017-02-06 23:56:09 UTC (rev 211757)
+++ trunk/Source/WebCore/testing/Internals.idl        2017-02-07 00:25:00 UTC (rev 211758)
</span><span class="lines">@@ -499,4 +499,6 @@
</span><span class="cx"> #if defined(WTF_PLATFORM_IOS) &amp;&amp; WTF_PLATFORM_IOS
</span><span class="cx">     void setQuickLookPassword(DOMString password);
</span><span class="cx"> #endif
</span><ins>+
+    [CallWith=Document] void setAsRunningUserScripts();
</ins><span class="cx"> };
</span></span></pre>
</div>
</div>

</body>
</html>