<!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>[244086] 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/244086">244086</a></dd>
<dt>Author</dt> <dd>wilander@apple.com</dd>
<dt>Date</dt> <dd>2019-04-09 11:19:59 -0700 (Tue, 09 Apr 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>Pick up Ad Click Attribution conversions in NetworkResourceLoader::willSendRedirectedRequest()
https://bugs.webkit.org/show_bug.cgi?id=196558
<rdar://problem/47650245>

Reviewed by Youenn Fablet.

Source/WebCore:

Tests: http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html
       http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html
       http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html

The existing API tests were expanded too.

* html/HTMLAnchorElement.cpp:
(WebCore::HTMLAnchorElement::parseAdClickAttribution const):
   Enhanced the use of AdClickAttribution::MaxEntropy.
* loader/AdClickAttribution.cpp:
(WebCore::AdClickAttribution::parseConversionRequest):
    New function to parse and validate URLs with a path starting with
    /.well-known/ad-click-attribution/.
(WebCore::AdClickAttribution::toString const):
    Added output for the conversion priority for testing purposes.
* loader/AdClickAttribution.h:
(WebCore::AdClickAttribution::Campaign::isValid const):
(WebCore::AdClickAttribution::Conversion::isValid const):
   Enhanced the use of AdClickAttribution::MaxEntropy.

Source/WebKit:

So called pixel requests have traditionally been used to send ad click
attribution data to click sources. The privacy implications of such
pixel requests are severe which is in part why browsers have started to
block cookies from being sent in such third-party requests.

To allow for a smooth transition to more privacy-friendly ad click
attribution, we should allow servers to make a redirect to
https://click-source.example/.well-known/ad-click-attribution/ to
trigger a so called conversion.

This patch checks for the well-known location in the path component of
the redirect URL. If the request indeed goes to the well-known location,
we parse the conversion data and send it to the storage in the network
session.

* NetworkProcess/NetworkAdClickAttribution.cpp:
(WebKit::NetworkAdClickAttribution::convert):
    Reporting function.
* NetworkProcess/NetworkAdClickAttribution.h:
* NetworkProcess/NetworkResourceLoader.cpp:
(WebKit::NetworkResourceLoader::willSendRedirectedRequest):
    Now checks for the well-known location through a call to
    WebCore::AdClickAttribution::parseConversionRequest().
* NetworkProcess/NetworkSession.cpp:
(WebKit::NetworkSession::convertAdClickAttribution):
    Piping to WebKit::NetworkAdClickAttribution::convert().
* NetworkProcess/NetworkSession.h:

Tools:

* TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp:
(TestWebKitAPI::TEST):
    Added tests of WebCore::AdClickAttribution::parseConversionRequest().

LayoutTests:

* http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt:
   Enhanced the use of AdClickAttribution::MaxEntropy.
* http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect-expected.txt: Added.
* http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html: Added.
* http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt: Added.
* http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html: Added.
* http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt: Added.
* http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html: Added.
* http/tests/adClickAttribution/resources/redirectToConversion.php: Added.
* http/tests/adClickAttribution/resources/redirectToConversionOnIPAddress.php: Added.
* platform/ios-wk2/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt:
   Enhanced the use of AdClickAttribution::MaxEntropy.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionanchortagattributesvalidationexpectedtxt">trunk/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt</a></li>
<li><a href="#trunkLayoutTestsplatformioswk2httptestsadClickAttributionanchortagattributesvalidationexpectedtxt">trunk/LayoutTests/platform/ios-wk2/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLAnchorElementcpp">trunk/Source/WebCore/html/HTMLAnchorElement.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderAdClickAttributioncpp">trunk/Source/WebCore/loader/AdClickAttribution.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderAdClickAttributionh">trunk/Source/WebCore/loader/AdClickAttribution.h</a></li>
<li><a href="#trunkSourceWebKitChangeLog">trunk/Source/WebKit/ChangeLog</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessNetworkAdClickAttributioncpp">trunk/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessNetworkAdClickAttributionh">trunk/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessNetworkResourceLoadercpp">trunk/Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessNetworkSessioncpp">trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessNetworkSessionh">trunk/Source/WebKit/NetworkProcess/NetworkSession.h</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoreAdClickAttributioncpp">trunk/Tools/TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughcrosssiteimageredirectexpectedtxt">trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughcrosssiteimageredirecthtml">trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html</a></li>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughimageredirectwithpriorityexpectedtxt">trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughimageredirectwithpriorityhtml">trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html</a></li>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughimageredirectwithoutpriorityexpectedtxt">trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughimageredirectwithoutpriorityhtml">trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html</a></li>
<li>trunk/LayoutTests/http/tests/adClickAttribution/resources/</li>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionresourcesredirectToConversionphp">trunk/LayoutTests/http/tests/adClickAttribution/resources/redirectToConversion.php</a></li>
<li><a href="#trunkLayoutTestshttptestsadClickAttributionresourcesredirectToConversionOnIPAddressphp">trunk/LayoutTests/http/tests/adClickAttribution/resources/redirectToConversionOnIPAddress.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/LayoutTests/ChangeLog 2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -1,3 +1,24 @@
</span><ins>+2019-04-09  John Wilander  <wilander@apple.com>
+
+        Pick up Ad Click Attribution conversions in NetworkResourceLoader::willSendRedirectedRequest()
+        https://bugs.webkit.org/show_bug.cgi?id=196558
+        <rdar://problem/47650245>
+
+        Reviewed by Youenn Fablet.
+
+        * http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt:
+           Enhanced the use of AdClickAttribution::MaxEntropy.
+        * http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect-expected.txt: Added.
+        * http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html: Added.
+        * http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt: Added.
+        * http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html: Added.
+        * http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt: Added.
+        * http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html: Added.
+        * http/tests/adClickAttribution/resources/redirectToConversion.php: Added.
+        * http/tests/adClickAttribution/resources/redirectToConversionOnIPAddress.php: Added.
+        * platform/ios-wk2/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt:
+           Enhanced the use of AdClickAttribution::MaxEntropy.
+
</ins><span class="cx"> 2019-04-09  Shawn Roberts  <sroberts@apple.com>
</span><span class="cx"> 
</span><span class="cx">         inspector/canvas/css-canvas-clients.html is a flaky failure
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionanchortagattributesvalidationexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt    2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt       2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><del>-CONSOLE MESSAGE: line 165: adcampaignid must have a non-negative value less than 64 for Ad Click Attribution.
-CONSOLE MESSAGE: line 165: adcampaignid must have a non-negative value less than 64 for Ad Click Attribution.
</del><ins>+CONSOLE MESSAGE: line 165: adcampaignid must have a non-negative value less than or equal to 63 for Ad Click Attribution.
+CONSOLE MESSAGE: line 165: adcampaignid must have a non-negative value less than or equal to 63 for Ad Click Attribution.
</ins><span class="cx"> CONSOLE MESSAGE: line 165: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
</span><span class="cx"> CONSOLE MESSAGE: line 165: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
</span><span class="cx"> CONSOLE MESSAGE: line 165: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughcrosssiteimageredirectexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect-expected.txt (0 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect-expected.txt                            (rev 0)
+++ trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect-expected.txt       2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+Tests that triggering of ad click attribution conversions through cross-site redirects do not work.
+
+
+WebCore::AdClickAttribution 1
+Source: 127.0.0.1
+Destination: localhost
+Campaign ID: 3
+No conversion data.
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughcrosssiteimageredirecthtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html (0 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html                            (rev 0)
+++ trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html       2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AdClickAttributionEnabled=true ] -->
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+    <script src="/js-test-resources/ui-helper.js"></script>
+</head>
+<body onload="setTimeout(runTest, 0)">
+<div id="description">Tests that triggering of ad click attribution conversions through cross-site redirects do not work.</div>
+<a id="targetLink" href="http://localhost:8000/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html?stepTwo" adcampaignid="3" addestination="http://localhost:8000">Link</a><br>
+<div id="output"></div>
+<script>
+    if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+        testRunner.setAllowsAnySSLCertificate(true);
+    }
+
+    function activateElement(elementID) {
+        var element = document.getElementById(elementID);
+        var centerX = element.offsetLeft + element.offsetWidth / 2;
+        var centerY = element.offsetTop + element.offsetHeight / 2;
+        UIHelper.activateAt(centerX, centerY).then(
+            function () {
+            },
+            function () {
+                document.getElementById("output").innerText = "FAIL Promise rejected.";
+                testRunner.notifyDone();
+            }
+        );
+    }
+
+    function runTest() {
+        if (window.testRunner) {
+            if (window.location.search === "?stepTwo") {
+                let imageElement = document.createElement("img");
+                imageElement.src = "https://localhost:8443/adClickAttribution/resources/redirectToConversionOnIPAddress.php?conversionData=12";
+                imageElement.id = "pixel";
+                imageElement.onerror = function(e) {
+                    testRunner.dumpAdClickAttribution();
+                    document.body.removeChild(document.getElementById("targetLink"));
+                    document.body.removeChild(document.getElementById("pixel"));
+                    testRunner.notifyDone();
+                };
+                document.body.appendChild(imageElement);
+            } else {
+                activateElement("targetLink");
+            }
+        } else {
+            document.getElementById("output").innerText = "FAIL No testRunner.";
+        }
+    }
+</script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughimageredirectwithpriorityexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt (0 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt                         (rev 0)
+++ trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt    2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -0,0 +1,9 @@
</span><ins>+Tests triggering of ad click attribution conversions with priority.
+
+
+WebCore::AdClickAttribution 1
+Source: 127.0.0.1
+Destination: localhost
+Campaign ID: 3
+Conversion data: 12
+Conversion priority: 3
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughimageredirectwithpriorityhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html (0 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html                         (rev 0)
+++ trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html    2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AdClickAttributionEnabled=true ] -->
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+    <script src="/js-test-resources/ui-helper.js"></script>
+</head>
+<body onload="setTimeout(runTest, 0)">
+<div id="description">Tests triggering of ad click attribution conversions with priority.</div>
+<a id="targetLink" href="http://localhost:8000/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html?stepTwo" adcampaignid="3" addestination="http://localhost:8000">Link</a><br>
+<div id="output"></div>
+<script>
+    if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+        testRunner.setAllowsAnySSLCertificate(true);
+    }
+
+    function activateElement(elementID) {
+        var element = document.getElementById(elementID);
+        var centerX = element.offsetLeft + element.offsetWidth / 2;
+        var centerY = element.offsetTop + element.offsetHeight / 2;
+        UIHelper.activateAt(centerX, centerY).then(
+            function () {
+            },
+            function () {
+                document.getElementById("output").innerText = "FAIL Promise rejected.";
+                testRunner.notifyDone();
+            }
+        );
+    }
+
+    function runTest() {
+        if (window.testRunner) {
+            if (window.location.search === "?stepTwo") {
+                let imageElement = document.createElement("img");
+                imageElement.src = "https://127.0.0.1:8443/adClickAttribution/resources/redirectToConversion.php?conversionData=12&priority=03";
+                imageElement.id = "pixel";
+                imageElement.onerror = function(e) {
+                    testRunner.dumpAdClickAttribution();
+                    document.body.removeChild(document.getElementById("targetLink"));
+                    document.body.removeChild(document.getElementById("pixel"));
+                    testRunner.notifyDone();
+                };
+                document.body.appendChild(imageElement);
+            } else {
+                activateElement("targetLink");
+            }
+        } else {
+            document.getElementById("output").innerText = "FAIL No testRunner.";
+        }
+    }
+</script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughimageredirectwithoutpriorityexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt (0 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt                              (rev 0)
+++ trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt 2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -0,0 +1,9 @@
</span><ins>+Tests triggering of ad click attribution conversions without priority.
+
+
+WebCore::AdClickAttribution 1
+Source: 127.0.0.1
+Destination: localhost
+Campaign ID: 3
+Conversion data: 12
+Conversion priority: 0
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionattributionconversionthroughimageredirectwithoutpriorityhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html (0 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html                              (rev 0)
+++ trunk/LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html 2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AdClickAttributionEnabled=true ] -->
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+    <script src="/js-test-resources/ui-helper.js"></script>
+</head>
+<body onload="setTimeout(runTest, 0)">
+<div id="description">Tests triggering of ad click attribution conversions without priority.</div>
+<a id="targetLink" href="http://localhost:8000/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html?stepTwo" adcampaignid="3" addestination="http://localhost:8000">Link</a><br>
+<div id="output"></div>
+<script>
+    if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+        testRunner.setAllowsAnySSLCertificate(true);
+    }
+
+    function activateElement(elementID) {
+        var element = document.getElementById(elementID);
+        var centerX = element.offsetLeft + element.offsetWidth / 2;
+        var centerY = element.offsetTop + element.offsetHeight / 2;
+        UIHelper.activateAt(centerX, centerY).then(
+            function () {
+            },
+            function () {
+                document.getElementById("output").innerText = "FAIL Promise rejected.";
+                testRunner.notifyDone();
+            }
+        );
+    }
+
+    function runTest() {
+        if (window.testRunner) {
+            if (window.location.search === "?stepTwo") {
+                let imageElement = document.createElement("img");
+                imageElement.src = "https://127.0.0.1:8443/adClickAttribution/resources/redirectToConversion.php?conversionData=12";
+                imageElement.id = "pixel";
+                imageElement.onerror = function() {
+                    testRunner.dumpAdClickAttribution();
+                    document.body.removeChild(document.getElementById("targetLink"));
+                    document.body.removeChild(document.getElementById("pixel"));
+                    testRunner.notifyDone();
+                };
+                document.body.appendChild(imageElement);
+            } else {
+                activateElement("targetLink");
+            }
+        } else {
+            document.getElementById("output").innerText = "FAIL No testRunner.";
+        }
+    }
+</script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionresourcesredirectToConversionphp"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/adClickAttribution/resources/redirectToConversion.php (0 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/resources/redirectToConversion.php                               (rev 0)
+++ trunk/LayoutTests/http/tests/adClickAttribution/resources/redirectToConversion.php  2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+<?php
+header("HTTP/1.0 302 Found");
+if (isset($_GET["conversionData"]) && isset($_GET["priority"])) {
+  header("Location: /.well-known/ad-click-attribution/" . $_GET["conversionData"] . "/" . $_GET["priority"]);
+} else if (isset($_GET["conversionData"])) {
+  header("Location: /.well-known/ad-click-attribution/" . $_GET["conversionData"]);
+}
+?>
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsadClickAttributionresourcesredirectToConversionOnIPAddressphp"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/adClickAttribution/resources/redirectToConversionOnIPAddress.php (0 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/adClickAttribution/resources/redirectToConversionOnIPAddress.php                            (rev 0)
+++ trunk/LayoutTests/http/tests/adClickAttribution/resources/redirectToConversionOnIPAddress.php       2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+<?php
+header("HTTP/1.0 302 Found");
+if (isset($_GET["conversionData"]) && isset($_GET["priority"])) {
+  header("Location: https://127.0.0.1:8000/.well-known/ad-click-attribution/" . $_GET["conversionData"] . "/" . $_GET["priority"]);
+} else if (isset($_GET["conversionData"])) {
+  header("Location: https://127.0.0.1:8000/.well-known/ad-click-attribution/" . $_GET["conversionData"]);
+}
+?>
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformioswk2httptestsadClickAttributionanchortagattributesvalidationexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/ios-wk2/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/ios-wk2/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt   2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/LayoutTests/platform/ios-wk2/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt      2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><del>-CONSOLE MESSAGE: adcampaignid must have a non-negative value less than 64 for Ad Click Attribution.
-CONSOLE MESSAGE: adcampaignid must have a non-negative value less than 64 for Ad Click Attribution.
</del><ins>+CONSOLE MESSAGE: adcampaignid must have a non-negative value less than or equal to 63 for Ad Click Attribution.
+CONSOLE MESSAGE: adcampaignid must have a non-negative value less than or equal to 63 for Ad Click Attribution.
</ins><span class="cx"> CONSOLE MESSAGE: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
</span><span class="cx"> CONSOLE MESSAGE: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
</span><span class="cx"> CONSOLE MESSAGE: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebCore/ChangeLog      2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -1,3 +1,31 @@
</span><ins>+2019-04-09  John Wilander  <wilander@apple.com>
+
+        Pick up Ad Click Attribution conversions in NetworkResourceLoader::willSendRedirectedRequest()
+        https://bugs.webkit.org/show_bug.cgi?id=196558
+        <rdar://problem/47650245>
+
+        Reviewed by Youenn Fablet.
+
+        Tests: http/tests/adClickAttribution/attribution-conversion-through-cross-site-image-redirect.html
+               http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority.html
+               http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority.html
+
+        The existing API tests were expanded too.
+
+        * html/HTMLAnchorElement.cpp:
+        (WebCore::HTMLAnchorElement::parseAdClickAttribution const):
+           Enhanced the use of AdClickAttribution::MaxEntropy.
+        * loader/AdClickAttribution.cpp:
+        (WebCore::AdClickAttribution::parseConversionRequest):
+            New function to parse and validate URLs with a path starting with
+            /.well-known/ad-click-attribution/.
+        (WebCore::AdClickAttribution::toString const):
+            Added output for the conversion priority for testing purposes.
+        * loader/AdClickAttribution.h:
+        (WebCore::AdClickAttribution::Campaign::isValid const):
+        (WebCore::AdClickAttribution::Conversion::isValid const):
+           Enhanced the use of AdClickAttribution::MaxEntropy.
+
</ins><span class="cx"> 2019-04-09  Don Olmstead  <don.olmstead@sony.com>
</span><span class="cx"> 
</span><span class="cx">         [CMake] Apple builds should use ICU_INCLUDE_DIRS
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLAnchorElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLAnchorElement.cpp (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLAnchorElement.cpp  2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebCore/html/HTMLAnchorElement.cpp     2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -431,8 +431,8 @@
</span><span class="cx">         return WTF::nullopt;
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    if (adCampaignID.value() >= AdClickAttribution::MaxEntropy) {
-        document().addConsoleMessage(MessageSource::Other, MessageLevel::Warning, makeString("adcampaignid must have a non-negative value less than ", AdClickAttribution::MaxEntropy, " for Ad Click Attribution."));
</del><ins>+    if (adCampaignID.value() > AdClickAttribution::MaxEntropy) {
+        document().addConsoleMessage(MessageSource::Other, MessageLevel::Warning, makeString("adcampaignid must have a non-negative value less than or equal to ", AdClickAttribution::MaxEntropy, " for Ad Click Attribution."));
</ins><span class="cx">         return WTF::nullopt;
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderAdClickAttributioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/AdClickAttribution.cpp (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/AdClickAttribution.cpp       2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebCore/loader/AdClickAttribution.cpp  2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -29,9 +29,14 @@
</span><span class="cx"> #include <wtf/RandomNumber.h>
</span><span class="cx"> #include <wtf/URL.h>
</span><span class="cx"> #include <wtf/text/StringBuilder.h>
</span><ins>+#include <wtf/text/StringView.h>
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+static const char adClickAttributionPathPrefix[] = "/.well-known/ad-click-attribution/";
+const size_t adClickConversionDataPathSegmentSize = 2;
+const size_t adClickPriorityPathSegmentSize = 2;
+
</ins><span class="cx"> bool AdClickAttribution::isValid() const
</span><span class="cx"> {
</span><span class="cx">     return m_conversion
</span><span class="lines">@@ -42,6 +47,39 @@
</span><span class="cx">         && m_earliestTimeToSend;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+Optional<AdClickAttribution::Conversion> AdClickAttribution::parseConversionRequest(const URL& redirectURL)
+{
+    if (!redirectURL.protocolIs("https"_s) || redirectURL.hasUsername() || redirectURL.hasPassword() || redirectURL.hasQuery() || redirectURL.hasFragment())
+        return { };
+
+    auto path = StringView(redirectURL.string()).substring(redirectURL.pathStart(), redirectURL.pathEnd() - redirectURL.pathStart());
+    if (path.isEmpty() || !path.startsWith(adClickAttributionPathPrefix))
+        return { };
+
+    auto prefixLength = sizeof(adClickAttributionPathPrefix) - 1;
+    if (path.length() == prefixLength + adClickConversionDataPathSegmentSize) {
+        auto conversionDataUInt64 = path.substring(prefixLength, adClickConversionDataPathSegmentSize).toUInt64Strict();
+        if (!conversionDataUInt64 || *conversionDataUInt64 > MaxEntropy)
+            return { };
+
+        return Conversion { static_cast<uint32_t>(*conversionDataUInt64), Priority { 0 } };
+    }
+    
+    if (path.length() == prefixLength + adClickConversionDataPathSegmentSize + 1 + adClickPriorityPathSegmentSize) {
+        auto conversionDataUInt64 = path.substring(prefixLength, adClickConversionDataPathSegmentSize).toUInt64Strict();
+        if (!conversionDataUInt64 || *conversionDataUInt64 > MaxEntropy)
+            return { };
+
+        auto conversionPriorityUInt64 = path.substring(prefixLength + adClickConversionDataPathSegmentSize + 1, adClickPriorityPathSegmentSize).toUInt64Strict();
+        if (!conversionPriorityUInt64 || *conversionPriorityUInt64 > MaxEntropy)
+            return { };
+
+        return Conversion { static_cast<uint32_t>(*conversionDataUInt64), Priority { static_cast<uint32_t>(*conversionPriorityUInt64) } };
+    }
+
+    return { };
+}
+
</ins><span class="cx"> void AdClickAttribution::setConversion(Conversion&& conversion)
</span><span class="cx"> {
</span><span class="cx">     if (!conversion.isValid() || (m_conversion && m_conversion->priority > conversion.priority))
</span><span class="lines">@@ -102,6 +140,8 @@
</span><span class="cx">     if (m_conversion) {
</span><span class="cx">         builder.appendLiteral("\nConversion data: ");
</span><span class="cx">         builder.appendNumber(m_conversion.value().data);
</span><ins>+        builder.appendLiteral("\nConversion priority: ");
+        builder.appendNumber(m_conversion.value().priority);
</ins><span class="cx">     } else
</span><span class="cx">         builder.appendLiteral("\nNo conversion data.");
</span><span class="cx">     builder.append('\n');
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderAdClickAttributionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/AdClickAttribution.h (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/AdClickAttribution.h 2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebCore/loader/AdClickAttribution.h    2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><span class="cx"> #include "RegistrableDomain.h"
</span><ins>+#include <wtf/CompletionHandler.h>
</ins><span class="cx"> #include <wtf/Optional.h>
</span><span class="cx"> #include <wtf/URL.h>
</span><span class="cx"> #include <wtf/WallTime.h>
</span><span class="lines">@@ -40,7 +41,7 @@
</span><span class="cx">     using ConversionData = uint32_t;
</span><span class="cx">     using PriorityValue = uint32_t;
</span><span class="cx"> 
</span><del>-    static constexpr uint32_t MaxEntropy = 64;
</del><ins>+    static constexpr uint32_t MaxEntropy = 63;
</ins><span class="cx"> 
</span><span class="cx">     struct Campaign {
</span><span class="cx">         Campaign() = default;
</span><span class="lines">@@ -51,7 +52,7 @@
</span><span class="cx">         
</span><span class="cx">         bool isValid() const
</span><span class="cx">         {
</span><del>-            return id < MaxEntropy;
</del><ins>+            return id <= MaxEntropy;
</ins><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         CampaignId id { 0 };
</span><span class="lines">@@ -210,7 +211,7 @@
</span><span class="cx"> 
</span><span class="cx">         bool isValid() const
</span><span class="cx">         {
</span><del>-            return data < MaxEntropy && priority < MaxEntropy;
</del><ins>+            return data <= MaxEntropy && priority <= MaxEntropy;
</ins><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         ConversionData data;
</span><span class="lines">@@ -229,6 +230,7 @@
</span><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    WEBCORE_EXPORT static Optional<Conversion> parseConversionRequest(const URL& redirectURL);
</ins><span class="cx">     WEBCORE_EXPORT void setConversion(Conversion&&);
</span><span class="cx">     WEBCORE_EXPORT URL url() const;
</span><span class="cx">     WEBCORE_EXPORT URL referrer() const;
</span></span></pre></div>
<a id="trunkSourceWebKitChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/ChangeLog (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/ChangeLog    2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebKit/ChangeLog       2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -1,3 +1,39 @@
</span><ins>+2019-04-09  John Wilander  <wilander@apple.com>
+
+        Pick up Ad Click Attribution conversions in NetworkResourceLoader::willSendRedirectedRequest()
+        https://bugs.webkit.org/show_bug.cgi?id=196558
+        <rdar://problem/47650245>
+
+        Reviewed by Youenn Fablet.
+
+        So called pixel requests have traditionally been used to send ad click
+        attribution data to click sources. The privacy implications of such
+        pixel requests are severe which is in part why browsers have started to
+        block cookies from being sent in such third-party requests.
+
+        To allow for a smooth transition to more privacy-friendly ad click
+        attribution, we should allow servers to make a redirect to
+        https://click-source.example/.well-known/ad-click-attribution/ to
+        trigger a so called conversion.
+
+        This patch checks for the well-known location in the path component of
+        the redirect URL. If the request indeed goes to the well-known location,
+        we parse the conversion data and send it to the storage in the network
+        session.
+
+        * NetworkProcess/NetworkAdClickAttribution.cpp:
+        (WebKit::NetworkAdClickAttribution::convert):
+            Reporting function.
+        * NetworkProcess/NetworkAdClickAttribution.h:
+        * NetworkProcess/NetworkResourceLoader.cpp:
+        (WebKit::NetworkResourceLoader::willSendRedirectedRequest):
+            Now checks for the well-known location through a call to
+            WebCore::AdClickAttribution::parseConversionRequest().
+        * NetworkProcess/NetworkSession.cpp:
+        (WebKit::NetworkSession::convertAdClickAttribution):
+            Piping to WebKit::NetworkAdClickAttribution::convert().
+        * NetworkProcess/NetworkSession.h:
+
</ins><span class="cx"> 2019-04-09  Chris Dumez  <cdumez@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [iOS] WebContent processes should be marked as "Foreground Running" when their view is visible
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessNetworkAdClickAttributioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp 2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp    2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -31,10 +31,12 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><ins>+using RegistrableDomain = WebCore::RegistrableDomain;
</ins><span class="cx"> using AdClickAttribution = WebCore::AdClickAttribution;
</span><span class="cx"> using Source = WebCore::AdClickAttribution::Source;
</span><span class="cx"> using Destination = WebCore::AdClickAttribution::Destination;
</span><span class="cx"> using DestinationMap = HashMap<Destination, AdClickAttribution>;
</span><ins>+using Conversion = WebCore::AdClickAttribution::Conversion;
</ins><span class="cx"> 
</span><span class="cx"> DestinationMap& NetworkAdClickAttribution::ensureDestinationMapForSource(const Source& source)
</span><span class="cx"> {
</span><span class="lines">@@ -49,6 +51,19 @@
</span><span class="cx">     destinationMapForSource.add(adClickAttribution.destination(), WTFMove(adClickAttribution));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void NetworkAdClickAttribution::convert(const Source& source, const Destination& destination, Conversion&& conversion)
+{
+    auto sourceIter = m_adClickAttributionMap.find(source);
+    if (sourceIter == m_adClickAttributionMap.end())
+        return;
+
+    auto destinationIter = sourceIter->value.find(destination);
+    if (destinationIter == sourceIter->value.end())
+        return;
+
+    destinationIter->value.setConversion(WTFMove(conversion));
+}
+
</ins><span class="cx"> void NetworkAdClickAttribution::clear(CompletionHandler<void()>&& completionHandler)
</span><span class="cx"> {
</span><span class="cx">     m_adClickAttributionMap.clear();
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessNetworkAdClickAttributionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h   2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h      2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><span class="cx"> #include <WebCore/AdClickAttribution.h>
</span><ins>+#include <WebCore/RegistrableDomain.h>
</ins><span class="cx"> #include <wtf/CompletionHandler.h>
</span><span class="cx"> #include <wtf/HashMap.h>
</span><span class="cx"> #include <wtf/text/WTFString.h>
</span><span class="lines">@@ -35,17 +36,20 @@
</span><span class="cx"> class NetworkAdClickAttribution {
</span><span class="cx"> public:
</span><span class="cx"> 
</span><ins>+    using RegistrableDomain = WebCore::RegistrableDomain;
</ins><span class="cx">     using AdClickAttribution = WebCore::AdClickAttribution;
</span><span class="cx">     using Source = WebCore::AdClickAttribution::Source;
</span><span class="cx">     using Destination = WebCore::AdClickAttribution::Destination;
</span><span class="cx">     using DestinationMap = HashMap<Destination, AdClickAttribution>;
</span><ins>+    using Conversion = WebCore::AdClickAttribution::Conversion;
</ins><span class="cx"> 
</span><span class="cx">     void store(AdClickAttribution&&);
</span><ins>+    void convert(const Source&, const Destination&, Conversion&&);
</ins><span class="cx">     void clear(CompletionHandler<void()>&&);
</span><span class="cx">     void toString(CompletionHandler<void(String)>&&) const;
</span><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    DestinationMap& ensureDestinationMapForSource(const AdClickAttribution::Source&);
</del><ins>+    DestinationMap& ensureDestinationMapForSource(const Source&);
</ins><span class="cx"> 
</span><span class="cx">     HashMap<Source, DestinationMap> m_adClickAttributionMap;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessNetworkResourceLoadercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp     2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp        2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx"> #include "WebPageMessages.h"
</span><span class="cx"> #include "WebResourceLoaderMessages.h"
</span><span class="cx"> #include "WebsiteDataStoreParameters.h"
</span><ins>+#include <WebCore/AdClickAttribution.h>
</ins><span class="cx"> #include <WebCore/BlobDataFileReference.h>
</span><span class="cx"> #include <WebCore/CertificateInfo.h>
</span><span class="cx"> #include <WebCore/ContentSecurityPolicy.h>
</span><span class="lines">@@ -583,6 +584,16 @@
</span><span class="cx"> {
</span><span class="cx">     ++m_redirectCount;
</span><span class="cx"> 
</span><ins>+    auto& redirectURL = redirectRequest.url();
+    if (auto adClickConversion = AdClickAttribution::parseConversionRequest(redirectURL)) {
+        RegistrableDomain redirectDomain { redirectURL };
+        auto& firstPartyURL = redirectRequest.firstPartyForCookies();
+        NetworkSession* networkSession;
+        // The redirect has to be done by the same registrable domain and it has to be a third-party request.
+        if (redirectDomain.matches(request.url()) && !redirectDomain.matches(firstPartyURL) && (networkSession = m_connection->networkProcess().networkSession(sessionID())))
+            networkSession->convertAdClickAttribution(AdClickAttribution::Source { WTFMove(redirectDomain) }, AdClickAttribution::Destination { firstPartyURL }, WTFMove(*adClickConversion));
+    }
+
</ins><span class="cx">     auto maxAgeCap = validateCacheEntryForMaxAgeCapValidation(request, redirectRequest, redirectResponse);
</span><span class="cx">     if (redirectResponse.source() == ResourceResponse::Source::Network && canUseCachedRedirect(request))
</span><span class="cx">         m_cache->storeRedirect(request, redirectResponse, redirectRequest, maxAgeCap);
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessNetworkSessioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp    2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp       2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -32,7 +32,6 @@
</span><span class="cx"> #include "WebPageProxy.h"
</span><span class="cx"> #include "WebPageProxyMessages.h"
</span><span class="cx"> #include "WebProcessProxy.h"
</span><del>-#include <WebCore/AdClickAttribution.h>
</del><span class="cx"> #include <WebCore/CookieJar.h>
</span><span class="cx"> #include <WebCore/NetworkStorageSession.h>
</span><span class="cx"> 
</span><span class="lines">@@ -141,6 +140,11 @@
</span><span class="cx">     m_adClickAttribution->store(WTFMove(adClickAttribution));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void NetworkSession::convertAdClickAttribution(const WebCore::AdClickAttribution::Source& source, const WebCore::AdClickAttribution::Destination& destination, WebCore::AdClickAttribution::Conversion&& conversion)
+{
+    m_adClickAttribution->convert(source, destination, WTFMove(conversion));
+}
+
</ins><span class="cx"> void NetworkSession::dumpAdClickAttribution(CompletionHandler<void(String)>&& completionHandler)
</span><span class="cx"> {
</span><span class="cx">     m_adClickAttribution->toString(WTFMove(completionHandler));
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessNetworkSessionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/NetworkSession.h (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/NetworkSession.h      2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSession.h 2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><span class="cx"> #include "WebResourceLoadStatisticsStore.h"
</span><ins>+#include <WebCore/AdClickAttribution.h>
</ins><span class="cx"> #include <WebCore/RegistrableDomain.h>
</span><span class="cx"> #include <pal/SessionID.h>
</span><span class="cx"> #include <wtf/HashSet.h>
</span><span class="lines">@@ -37,7 +38,6 @@
</span><span class="cx"> #include <wtf/text/WTFString.h>
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><del>-class AdClickAttribution;
</del><span class="cx"> class NetworkStorageSession;
</span><span class="cx"> enum class IncludeHttpOnlyCookies : bool;
</span><span class="cx"> enum class ShouldSample : bool;
</span><span class="lines">@@ -80,6 +80,7 @@
</span><span class="cx">     void notifyPageStatisticsTelemetryFinished(unsigned totalPrevalentResources, unsigned totalPrevalentResourcesWithUserInteraction, unsigned top3SubframeUnderTopFrameOrigins);
</span><span class="cx"> #endif
</span><span class="cx">     void storeAdClickAttribution(WebCore::AdClickAttribution&&);
</span><ins>+    void convertAdClickAttribution(const WebCore::AdClickAttribution::Source&, const WebCore::AdClickAttribution::Destination&, WebCore::AdClickAttribution::Conversion&&);
</ins><span class="cx">     void dumpAdClickAttribution(CompletionHandler<void(String)>&&);
</span><span class="cx">     void clearAdClickAttribution(CompletionHandler<void()>&&);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog    2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Tools/ChangeLog       2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2019-04-09  John Wilander  <wilander@apple.com>
+
+        Pick up Ad Click Attribution conversions in NetworkResourceLoader::willSendRedirectedRequest()
+        https://bugs.webkit.org/show_bug.cgi?id=196558
+        <rdar://problem/47650245>
+
+        Reviewed by Youenn Fablet.
+
+        * TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp:
+        (TestWebKitAPI::TEST):
+            Added tests of WebCore::AdClickAttribution::parseConversionRequest().
+
</ins><span class="cx"> 2019-04-09  Don Olmstead  <don.olmstead@sony.com>
</span><span class="cx"> 
</span><span class="cx">         [CMake] Apple builds should use ICU_INCLUDE_DIRS
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreAdClickAttributioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp (244085 => 244086)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp   2019-04-09 18:08:27 UTC (rev 244085)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp      2019-04-09 18:19:59 UTC (rev 244086)
</span><span class="lines">@@ -34,7 +34,6 @@
</span><span class="cx"> namespace TestWebKitAPI {
</span><span class="cx"> 
</span><span class="cx"> constexpr uint32_t min6BitValue { 0 };
</span><del>-constexpr uint32_t max6BitValue { 63 };
</del><span class="cx"> 
</span><span class="cx"> const URL webKitURL { { }, "https://webkit.org"_s };
</span><span class="cx"> const URL exampleURL { { }, "https://example.com"_s };
</span><span class="lines">@@ -68,8 +67,8 @@
</span><span class="cx"> 
</span><span class="cx"> TEST(AdClickAttribution, ValidMaxValues)
</span><span class="cx"> {
</span><del>-    AdClickAttribution attribution { AdClickAttribution::Campaign(max6BitValue), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(max6BitValue, AdClickAttribution::Priority(max6BitValue)));
</del><ins>+    AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
+    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
</ins><span class="cx"> 
</span><span class="cx">     auto attributionURL = attribution.url();
</span><span class="cx">     auto referrerURL = attribution.referrer();
</span><span class="lines">@@ -80,20 +79,56 @@
</span><span class="cx"> 
</span><span class="cx"> TEST(AdClickAttribution, EarliestTimeToSendAttributionMinimumDelay)
</span><span class="cx"> {
</span><del>-    AdClickAttribution attribution { AdClickAttribution::Campaign(max6BitValue), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
</del><ins>+    AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
</ins><span class="cx">     auto now = WallTime::now();
</span><del>-    attribution.setConversion(AdClickAttribution::Conversion(max6BitValue, AdClickAttribution::Priority(max6BitValue)));
</del><ins>+    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
</ins><span class="cx">     auto earliestTimeToSend = attribution.earliestTimeToSend();
</span><span class="cx">     ASSERT_TRUE(earliestTimeToSend);
</span><span class="cx">     ASSERT_TRUE(earliestTimeToSend.value().secondsSinceEpoch() - 24_h >= now.secondsSinceEpoch());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TEST(AdClickAttribution, ValidConversionURLs)
+{
+    const URL conversionURLWithoutPriority { { }, "https://webkit.org/.well-known/ad-click-attribution/22"_s };
+    auto optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithoutPriority);
+    ASSERT_TRUE(optionalConversion);
+    ASSERT_EQ(optionalConversion->data, (uint32_t)22);
+
+    const URL conversionURLWithoutPriorityMaxEntropy { { }, "https://webkit.org/.well-known/ad-click-attribution/63"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithoutPriorityMaxEntropy);
+    ASSERT_TRUE(optionalConversion);
+    ASSERT_EQ(optionalConversion->data, (uint32_t)63);
+    
+    const URL conversionURLWithoutPriorityAndLeadingZero { { }, "https://webkit.org/.well-known/ad-click-attribution/02"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithoutPriorityAndLeadingZero);
+    ASSERT_TRUE(optionalConversion);
+    ASSERT_EQ(optionalConversion->data, (uint32_t)2);
+
+    const URL conversionURLWithPriority { { }, "https://webkit.org/.well-known/ad-click-attribution/22/12"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithPriority);
+    ASSERT_TRUE(optionalConversion);
+    ASSERT_EQ(optionalConversion->data, (uint32_t)22);
+    ASSERT_EQ(optionalConversion->priority, (uint32_t)12);
+
+    const URL conversionURLWithPriorityMaxEntropy { { }, "https://webkit.org/.well-known/ad-click-attribution/63/63"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithPriorityMaxEntropy);
+    ASSERT_TRUE(optionalConversion);
+    ASSERT_EQ(optionalConversion->data, (uint32_t)63);
+    ASSERT_EQ(optionalConversion->priority, (uint32_t)63);
+    
+    const URL conversionURLWithPriorityAndLeadingZero { { }, "https://webkit.org/.well-known/ad-click-attribution/22/02"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithPriorityAndLeadingZero);
+    ASSERT_TRUE(optionalConversion);
+    ASSERT_EQ(optionalConversion->data, (uint32_t)22);
+    ASSERT_EQ(optionalConversion->priority, (uint32_t)2);
+}
+
</ins><span class="cx"> // Negative test cases.
</span><span class="cx"> 
</span><span class="cx"> TEST(AdClickAttribution, InvalidCampaignId)
</span><span class="cx"> {
</span><del>-    AdClickAttribution attribution { AdClickAttribution::Campaign(max6BitValue + 1), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(max6BitValue, AdClickAttribution::Priority(max6BitValue)));
</del><ins>+    AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy + 1), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
+    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
</ins><span class="cx"> 
</span><span class="cx">     auto attributionURL = attribution.url();
</span><span class="cx">     auto referrerURL = attribution.referrer();
</span><span class="lines">@@ -104,8 +139,8 @@
</span><span class="cx"> 
</span><span class="cx"> TEST(AdClickAttribution, InvalidSourceHost)
</span><span class="cx"> {
</span><del>-    AdClickAttribution attribution { AdClickAttribution::Campaign(max6BitValue), AdClickAttribution::Source { emptyURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(max6BitValue, AdClickAttribution::Priority(max6BitValue)));
</del><ins>+    AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { emptyURL }, AdClickAttribution::Destination { exampleURL } };
+    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
</ins><span class="cx"> 
</span><span class="cx">     auto attributionURL = attribution.url();
</span><span class="cx">     auto referrerURL = attribution.referrer();
</span><span class="lines">@@ -116,8 +151,8 @@
</span><span class="cx"> 
</span><span class="cx"> TEST(AdClickAttribution, InvalidDestinationHost)
</span><span class="cx"> {
</span><del>-    AdClickAttribution attribution { AdClickAttribution::Campaign(max6BitValue + 1), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { emptyURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(max6BitValue, AdClickAttribution::Priority(max6BitValue)));
</del><ins>+    AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy + 1), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { emptyURL } };
+    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
</ins><span class="cx"> 
</span><span class="cx">     auto attributionURL = attribution.url();
</span><span class="cx">     auto referrerURL = attribution.referrer();
</span><span class="lines">@@ -128,8 +163,8 @@
</span><span class="cx"> 
</span><span class="cx"> TEST(AdClickAttribution, InvalidConversionData)
</span><span class="cx"> {
</span><del>-    AdClickAttribution attribution { AdClickAttribution::Campaign(max6BitValue), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion((max6BitValue + 1), AdClickAttribution::Priority(max6BitValue)));
</del><ins>+    AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
+    attribution.setConversion(AdClickAttribution::Conversion((AdClickAttribution::MaxEntropy + 1), AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
</ins><span class="cx"> 
</span><span class="cx">     auto attributionURL = attribution.url();
</span><span class="cx">     auto referrerURL = attribution.referrer();
</span><span class="lines">@@ -140,8 +175,8 @@
</span><span class="cx"> 
</span><span class="cx"> TEST(AdClickAttribution, InvalidPriority)
</span><span class="cx"> {
</span><del>-    AdClickAttribution attribution { AdClickAttribution::Campaign(max6BitValue), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(max6BitValue, AdClickAttribution::Priority(max6BitValue + 1)));
</del><ins>+    AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
+    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy + 1)));
</ins><span class="cx"> 
</span><span class="cx">     auto attributionURL = attribution.url();
</span><span class="cx">     auto referrerURL = attribution.referrer();
</span><span class="lines">@@ -152,7 +187,7 @@
</span><span class="cx"> 
</span><span class="cx"> TEST(AdClickAttribution, InvalidMissingConversion)
</span><span class="cx"> {
</span><del>-    AdClickAttribution attribution { AdClickAttribution::Campaign(max6BitValue), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
</del><ins>+    AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
</ins><span class="cx"> 
</span><span class="cx">     auto attributionURL = attribution.url();
</span><span class="cx">     auto referrerURL = attribution.referrer();
</span><span class="lines">@@ -162,4 +197,106 @@
</span><span class="cx">     ASSERT_FALSE(attribution.earliestTimeToSend());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TEST(AdClickAttribution, InvalidConversionURLs)
+{
+    const URL conversionURLWithSingleDigitConversionData { { }, "https://webkit.org/.well-known/ad-click-attribution/2"_s };
+    auto optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithSingleDigitConversionData);
+    ASSERT_FALSE(optionalConversion);
+    
+    const URL conversionURLWithNonNumeralConversionData { { }, "https://webkit.org/.well-known/ad-click-attribution/2s"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithNonNumeralConversionData);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithNegativeConversionData { { }, "https://webkit.org/.well-known/ad-click-attribution/-2"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithNegativeConversionData);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithTooLargeConversionData { { }, "https://webkit.org/.well-known/ad-click-attribution/64"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithTooLargeConversionData);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithSingleDigitPriority { { }, "https://webkit.org/.well-known/ad-click-attribution/22/2"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithSingleDigitPriority);
+    ASSERT_FALSE(optionalConversion);
+    
+    const URL conversionURLWithNonNumeralPriority { { }, "https://webkit.org/.well-known/ad-click-attribution/22/2s"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithNonNumeralPriority);
+    ASSERT_FALSE(optionalConversion);
+    
+    const URL conversionURLWithNegativePriority { { }, "https://webkit.org/.well-known/ad-click-attribution/22/-2"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithNegativePriority);
+    ASSERT_FALSE(optionalConversion);
+    
+    const URL conversionURLWithTooLargePriority { { }, "https://webkit.org/.well-known/ad-click-attribution/22/64"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithTooLargePriority);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithTooLargeConversionDataAndPriority { { }, "https://webkit.org/.well-known/ad-click-attribution/64/22"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithTooLargeConversionDataAndPriority);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithTooLargeConversionDataAndTooLargePriority { { }, "https://webkit.org/.well-known/ad-click-attribution/64/64"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithTooLargeConversionDataAndTooLargePriority);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithExtraLeadingSlash = { { }, "https://webkit.org/.well-known/ad-click-attribution//22/12"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithExtraLeadingSlash);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithExtraTrailingSlash = { { }, "https://webkit.org/.well-known/ad-click-attribution/22/12/"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithExtraTrailingSlash);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithTrailingQuestionMark = { { }, "https://webkit.org/.well-known/ad-click-attribution/22/12?"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithTrailingQuestionMark);
+    ASSERT_FALSE(optionalConversion);
+}
+
+TEST(AdClickAttribution, InvalidConversionWithDisallowedURLComponents)
+{
+    // Protocol.
+    const URL conversionURLWithHttpProtocol { { }, "http://webkit.org/.well-known/ad-click-attribution/2"_s };
+    auto optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithHttpProtocol);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithWssProtocol { { }, "wss://webkit.org/.well-known/ad-click-attribution/2"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithWssProtocol);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithFileProtocol { { }, "file:///.well-known/ad-click-attribution/2"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithFileProtocol);
+    ASSERT_FALSE(optionalConversion);
+
+    // Username and password.
+    const URL conversionURLWithUserName { { }, "https://user@webkit.org/.well-known/ad-click-attribution/2"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithUserName);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithPassword = { { }, "https://:pwd@webkit.org/.well-known/ad-click-attribution/22/12?"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithPassword);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithUsernameAndPassword = { { }, "https://user:pwd@webkit.org/.well-known/ad-click-attribution/22/12?"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithUsernameAndPassword);
+    ASSERT_FALSE(optionalConversion);
+
+    // Query string.
+    const URL conversionURLWithTrailingQuestionMark = { { }, "https://webkit.org/.well-known/ad-click-attribution/22/12?"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithTrailingQuestionMark);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithQueryString = { { }, "https://webkit.org/.well-known/ad-click-attribution/22/12?extra=data"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithQueryString);
+    ASSERT_FALSE(optionalConversion);
+    
+    // Fragment.
+    const URL conversionURLWithTrailingHash = { { }, "https://webkit.org/.well-known/ad-click-attribution/22/12#"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithTrailingHash);
+    ASSERT_FALSE(optionalConversion);
+
+    const URL conversionURLWithFragment = { { }, "https://webkit.org/.well-known/ad-click-attribution/22/12#fragment"_s };
+    optionalConversion = AdClickAttribution::parseConversionRequest(conversionURLWithFragment);
+    ASSERT_FALSE(optionalConversion);
+}
+
</ins><span class="cx"> } // namespace TestWebKitAPI
</span></span></pre>
</div>
</div>

</body>
</html>