Send delayed Ad Click Attribution conversion requests to the click source
authorwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 15 Apr 2019 21:10:03 +0000 (21:10 +0000)
committerwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 15 Apr 2019 21:10:03 +0000 (21:10 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196838
<rdar://problem/47650157>

Reviewed by Chris Dumez and Youenn Fablet.

Source/WebCore:

WebCore::AdClickAttribution now:
- Sets m_earliestTimeToSend correctly based on WallTime::now().
- Allows for a test override of the base URL for conversions.
- Holds state for whether or not a conversion request has been sent.
- Outputs m_earliestTimeToSend and m_conversion->hasBeenSent in toString().
- Returns m_earliestTimeToSend as a result of a call to
convertAndGetEarliestTimeToSend() which used to be called setConversion().

Test: http/tests/adClickAttribution/send-attribution-conversion-request.html

* loader/AdClickAttribution.cpp:
(WebCore::AdClickAttribution::convertAndGetEarliestTimeToSend):
(WebCore::AdClickAttribution::url const):
(WebCore::AdClickAttribution::urlForTesting const):
(WebCore::AdClickAttribution::markConversionAsSent):
(WebCore::AdClickAttribution::wasConversionSent const):
(WebCore::AdClickAttribution::toString const):
(WebCore::AdClickAttribution::setConversion): Deleted.
    Renamed convertAndGetEarliestTimeToSend().
* loader/AdClickAttribution.h:
(WebCore::AdClickAttribution::Conversion::Conversion):
(WebCore::AdClickAttribution::Conversion::encode const):
(WebCore::AdClickAttribution::Conversion::decode):
* platform/Timer.h:
    Now exports nextFireInterval.

Source/WebKit:

This patch schedules a conversion request with appropriate data going to the
click source as a result of an ad click conversion.

WebKit::AdClickAttributionManager makes use of existing WebKit::PingLoad
infrastructure to make the request. This will probably be reworked into a
dedicated load class further on.

New test infrastructure allows for an override of both the conversion URL
and the 24-48 hour timer.

* NetworkProcess/AdClickAttributionManager.cpp: Added.
(WebKit::AdClickAttributionManager::ensureDestinationMapForSource):
(WebKit::AdClickAttributionManager::store):
(WebKit::AdClickAttributionManager::startTimer):
    Convenience function to support test override.
(WebKit::AdClickAttributionManager::convert):
    This function now sets the timer.
(WebKit::AdClickAttributionManager::fireConversionRequest):
    Fire an individual request.
(WebKit::AdClickAttributionManager::firePendingConversionRequests):
    This is the timer function that iterates over all pending attributions.
(WebKit::AdClickAttributionManager::clear):
    Now clears the two new test settings members.
(WebKit::AdClickAttributionManager::toString const):
* NetworkProcess/AdClickAttributionManager.h: Renamed from Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h.
(WebKit::AdClickAttributionManager::AdClickAttributionManager):
(WebKit::AdClickAttributionManager::setPingLoadFunction):
(WebKit::AdClickAttributionManager::setOverrideTimerForTesting):
(WebKit::AdClickAttributionManager::setConversionURLForTesting):
* NetworkProcess/NetworkAdClickAttribution.cpp: Renamed from Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp.
* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::storeAdClickAttribution):
(WebKit::NetworkProcess::dumpAdClickAttribution):
(WebKit::NetworkProcess::clearAdClickAttribution):
(WebKit::NetworkProcess::setAdClickAttributionOverrideTimerForTesting):
(WebKit::NetworkProcess::setAdClickAttributionConversionURLForTesting):
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcess.messages.in:
* NetworkProcess/NetworkSession.cpp:
(WebKit::NetworkSession::NetworkSession):
(WebKit::NetworkSession::setAdClickAttributionOverrideTimerForTesting):
(WebKit::NetworkSession::setAdClickAttributionConversionURLForTesting):
* NetworkProcess/NetworkSession.h:
* NetworkProcess/PingLoad.cpp:
(WebKit::PingLoad::PingLoad):
(WebKit::m_blobFiles):
(WebKit::PingLoad::initialize):
    The PingLoad constructor is now split in two to allow for construction
    without a WebKit::NetworkConnectionToWebProcess object. The body of
    the constructor was moved into the new initialize() function which is
    shared between constructors.
* NetworkProcess/PingLoad.h:
* Sources.txt:
    Removed NetworkProcess/NetworkAdClickAttribution.cpp and added
    NetworkProcess/NetworkAdClickAttribution.cpp.
* UIProcess/API/C/WKPage.cpp:
(WKPageSetAdClickAttributionOverrideTimerForTesting):
(WKPageSetAdClickAttributionConversionURLForTesting):
* UIProcess/API/C/WKPagePrivate.h:
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::dumpAdClickAttribution): Deleted.
(WebKit::NetworkProcessProxy::clearAdClickAttribution): Deleted.
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::dumpAdClickAttribution):
(WebKit::WebPageProxy::clearAdClickAttribution):
(WebKit::WebPageProxy::setAdClickAttributionOverrideTimerForTesting):
(WebKit::WebPageProxy::setAdClickAttributionConversionURLForTesting):
* UIProcess/WebPageProxy.h:
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::dumpAdClickAttribution): Deleted.
(WebKit::WebProcessPool::clearAdClickAttribution): Deleted.
* UIProcess/WebProcessPool.h:
* WebKit.xcodeproj/project.pbxproj:

Tools:

This patch adds test infrastructure to override the default behavior in
WebKit::NetworkAdClickAttribution.

* TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp:
(TestWebKitAPI::TEST):
* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::dumpAdClickAttribution):
(WTR::TestRunner::clearAdClickAttribution):
(WTR::TestRunner::setAdClickAttributionOverrideTimerForTesting):
(WTR::TestRunner::setAdClickAttributionConversionURLForTesting):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::setAdClickAttributionOverrideTimerForTesting):
(WTR::TestController::setAdClickAttributionConversionURLForTesting):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):

LayoutTests:

* http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt:
* http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt:
* http/tests/adClickAttribution/resources/conversionFilePath.php: Added.
* http/tests/adClickAttribution/resources/conversionReport.php: Added.
* http/tests/adClickAttribution/resources/getConversionData.php: Added.
* http/tests/adClickAttribution/send-attribution-conversion-request-expected.txt: Added.
* http/tests/adClickAttribution/send-attribution-conversion-request.html: Added.

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

41 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt
LayoutTests/http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt
LayoutTests/http/tests/adClickAttribution/resources/conversionFilePath.php [new file with mode: 0644]
LayoutTests/http/tests/adClickAttribution/resources/conversionReport.php [new file with mode: 0644]
LayoutTests/http/tests/adClickAttribution/resources/getConversionData.php [new file with mode: 0644]
LayoutTests/http/tests/adClickAttribution/send-attribution-conversion-request-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/adClickAttribution/send-attribution-conversion-request.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/loader/AdClickAttribution.cpp
Source/WebCore/loader/AdClickAttribution.h
Source/WebCore/platform/Timer.h
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/AdClickAttributionManager.cpp [new file with mode: 0644]
Source/WebKit/NetworkProcess/AdClickAttributionManager.h [moved from Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h with 60% similarity]
Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp [deleted file]
Source/WebKit/NetworkProcess/NetworkProcess.cpp
Source/WebKit/NetworkProcess/NetworkProcess.h
Source/WebKit/NetworkProcess/NetworkProcess.messages.in
Source/WebKit/NetworkProcess/NetworkSession.cpp
Source/WebKit/NetworkProcess/NetworkSession.h
Source/WebKit/NetworkProcess/PingLoad.cpp
Source/WebKit/NetworkProcess/PingLoad.h
Source/WebKit/Sources.txt
Source/WebKit/UIProcess/API/C/WKPage.cpp
Source/WebKit/UIProcess/API/C/WKPagePrivate.h
Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp
Source/WebKit/UIProcess/Network/NetworkProcessProxy.h
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/UIProcess/WebProcessPool.h
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp
Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestController.h
Tools/WebKitTestRunner/TestInvocation.cpp

index c599a1a..0bd7203 100644 (file)
@@ -1,3 +1,19 @@
+2019-04-15  John Wilander  <wilander@apple.com>
+
+        Send delayed Ad Click Attribution conversion requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=196838
+        <rdar://problem/47650157>
+
+        Reviewed by Chris Dumez and Youenn Fablet.
+
+        * http/tests/adClickAttribution/attribution-conversion-through-image-redirect-with-priority-expected.txt:
+        * http/tests/adClickAttribution/attribution-conversion-through-image-redirect-without-priority-expected.txt:
+        * http/tests/adClickAttribution/resources/conversionFilePath.php: Added.
+        * http/tests/adClickAttribution/resources/conversionReport.php: Added.
+        * http/tests/adClickAttribution/resources/getConversionData.php: Added.
+        * http/tests/adClickAttribution/send-attribution-conversion-request-expected.txt: Added.
+        * http/tests/adClickAttribution/send-attribution-conversion-request.html: Added.
+
 2019-04-15  Devin Rousso  <drousso@apple.com>
 
         REGRESSION (r240644): Layout Test inspector/page/overrideSetting-ICECandidateFilteringEnabled.html is a flaky timeout
diff --git a/LayoutTests/http/tests/adClickAttribution/resources/conversionFilePath.php b/LayoutTests/http/tests/adClickAttribution/resources/conversionFilePath.php
new file mode 100644 (file)
index 0000000..a553cf9
--- /dev/null
@@ -0,0 +1,6 @@
+<?php
+require_once '../../resources/portabilityLayer.php';
+
+$conversionFilePath = sys_get_temp_dir() . "/adClickConversion.txt";
+
+?>
diff --git a/LayoutTests/http/tests/adClickAttribution/resources/conversionReport.php b/LayoutTests/http/tests/adClickAttribution/resources/conversionReport.php
new file mode 100644 (file)
index 0000000..bf3f787
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+require_once 'conversionFilePath.php';
+
+$conversionFile = fopen($conversionFilePath . ".tmp", 'w');
+$httpHeaders = $_SERVER;
+$cookiesFound = false;
+foreach ($httpHeaders as $name => $value) {
+    if ($name === "HTTP_HOST" || $name === "REQUEST_URI")
+        fwrite($conversionFile, "$name: $value\n");
+    else if ($name === "HTTP_COOKIE") {
+        fwrite($conversionFile, "Cookies in conversion request: $value\n");
+        $cookiesFound = true;
+    }
+}
+if (!$cookiesFound) {
+    fwrite($conversionFile, "No cookies in conversion request.\n");
+}
+fclose($conversionFile);
+rename($conversionFilePath . ".tmp", $conversionFilePath);
+?>
diff --git a/LayoutTests/http/tests/adClickAttribution/resources/getConversionData.php b/LayoutTests/http/tests/adClickAttribution/resources/getConversionData.php
new file mode 100644 (file)
index 0000000..2996fa0
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+require_once 'conversionFilePath.php';
+
+$noTimeout = True;
+$timeoutMsecs = 0;
+if (isset($_GET['timeout_ms'])) {
+    $noTimeout = False;
+    $timeoutMsecs = (int) $_GET['timeout_ms'];
+}
+
+$conversionFileFound = False;
+while ($noTimeout || $timeoutMsecs > 0) {
+    if (file_exists($conversionFilePath)) {
+        $conversionFileFound = True;
+        break;
+    }
+    $sleepMsecs = 10;
+    usleep($sleepMsecs * 1000);
+    if (!$noTimeout) {
+        $timeoutMsecs -= $sleepMsecs;
+    }
+    // file_exists() caches results, we want to invalidate the cache.
+    clearstatcache();
+}
+
+
+echo "<html><body>\n";
+
+if ($conversionFileFound) {
+    echo "Conversion received.";
+    $conversionFile = fopen($conversionFilePath, 'r');
+    while ($line = fgets($conversionFile)) {
+        echo "<br>";
+        echo trim($line);
+    }
+    fclose($conversionFile);
+    unlink($conversionFilePath);
+} else {
+    echo "Conversion not received - timed out.";
+}
+
+if (isset($_GET['endTest'])) {
+    echo "<script>";
+    echo "if (window.testRunner)";
+    echo "    testRunner.notifyDone();";
+    echo "</script>";
+}
+
+echo "</body></html>";
+?>
diff --git a/LayoutTests/http/tests/adClickAttribution/send-attribution-conversion-request-expected.txt b/LayoutTests/http/tests/adClickAttribution/send-attribution-conversion-request-expected.txt
new file mode 100644 (file)
index 0000000..3ea9012
--- /dev/null
@@ -0,0 +1,19 @@
+Tests sending of ad click attribution requests after a conversion.
+
+
+
+--------
+Frame: '<!--frame1-->'
+--------
+Conversion received.
+HTTP_HOST: 127.0.0.1:8000
+REQUEST_URI: /adClickAttribution/resources/conversionReport.php?conversion=12&campaign=3
+No cookies in conversion request.
+WebCore::AdClickAttribution 1
+Source: 127.0.0.1
+Destination: localhost
+Campaign ID: 3
+Conversion data: 12
+Conversion priority: 0
+Conversion earliest time to send: Within 24-48 hours
+Conversion request sent: true
diff --git a/LayoutTests/http/tests/adClickAttribution/send-attribution-conversion-request.html b/LayoutTests/http/tests/adClickAttribution/send-attribution-conversion-request.html
new file mode 100644 (file)
index 0000000..3a0b80f
--- /dev/null
@@ -0,0 +1,64 @@
+<!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 sending of ad click attribution requests after a conversion.</div>
+<a id="targetLink" href="http://localhost:8000/adClickAttribution/send-attribution-conversion-request.html?stepTwo" adcampaignid="3" addestination="http://localhost:8000">Link</a><br>
+<div id="output"></div>
+<script>
+    if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpChildFramesAsText();
+        testRunner.setAllowsAnySSLCertificate(true);
+        testRunner.setAdClickAttributionOverrideTimerForTesting(true);
+        testRunner.setAdClickAttributionConversionURLForTesting("http://127.0.0.1:8000/adClickAttribution/resources/conversionReport.php");
+    }
+
+    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 appendConversionDataIframeAndFinish() {
+        testRunner.dumpAdClickAttribution();
+        document.body.removeChild(document.getElementById("targetLink"));
+        document.body.removeChild(document.getElementById("pixel"));
+
+        let iframeElement = document.createElement("iframe");
+        iframeElement.src = "http://127.0.0.1:8000/adClickAttribution/resources/getConversionData.php?endTest=true&timeout_ms=2000";
+        document.body.appendChild(iframeElement);
+    }
+
+    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() {
+                    appendConversionDataIframeAndFinish();
+                };
+                document.body.appendChild(imageElement);
+            } else {
+                activateElement("targetLink");
+            }
+        } else {
+            document.getElementById("output").innerText = "FAIL No testRunner.";
+        }
+    }
+</script>
+</body>
+</html>
index 035a9e8..4f1ad60 100644 (file)
@@ -1,3 +1,37 @@
+2019-04-15  John Wilander  <wilander@apple.com>
+
+        Send delayed Ad Click Attribution conversion requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=196838
+        <rdar://problem/47650157>
+
+        Reviewed by Chris Dumez and Youenn Fablet.
+
+        WebCore::AdClickAttribution now:
+        - Sets m_earliestTimeToSend correctly based on WallTime::now().
+        - Allows for a test override of the base URL for conversions.
+        - Holds state for whether or not a conversion request has been sent.
+        - Outputs m_earliestTimeToSend and m_conversion->hasBeenSent in toString().
+        - Returns m_earliestTimeToSend as a result of a call to
+        convertAndGetEarliestTimeToSend() which used to be called setConversion().
+
+        Test: http/tests/adClickAttribution/send-attribution-conversion-request.html
+
+        * loader/AdClickAttribution.cpp:
+        (WebCore::AdClickAttribution::convertAndGetEarliestTimeToSend):
+        (WebCore::AdClickAttribution::url const):
+        (WebCore::AdClickAttribution::urlForTesting const):
+        (WebCore::AdClickAttribution::markConversionAsSent):
+        (WebCore::AdClickAttribution::wasConversionSent const):
+        (WebCore::AdClickAttribution::toString const):
+        (WebCore::AdClickAttribution::setConversion): Deleted.
+            Renamed convertAndGetEarliestTimeToSend().
+        * loader/AdClickAttribution.h:
+        (WebCore::AdClickAttribution::Conversion::Conversion):
+        (WebCore::AdClickAttribution::Conversion::encode const):
+        (WebCore::AdClickAttribution::Conversion::decode):
+        * platform/Timer.h:
+            Now exports nextFireInterval.
+
 2019-04-15  Chris Dumez  <cdumez@apple.com>
 
         Regression(r237903) Speedometer 2 is 1-2% regressed on iOS
index 5d97faa..5264567 100644 (file)
@@ -80,15 +80,17 @@ Optional<AdClickAttribution::Conversion> AdClickAttribution::parseConversionRequ
     return { };
 }
 
-void AdClickAttribution::setConversion(Conversion&& conversion)
+Optional<Seconds> AdClickAttribution::convertAndGetEarliestTimeToSend(Conversion&& conversion)
 {
     if (!conversion.isValid() || (m_conversion && m_conversion->priority > conversion.priority))
-        return;
+        return { };
 
     m_conversion = WTFMove(conversion);
     // 24-48 hour delay before sending. This helps privacy since the conversion and the attribution
     // requests are detached and the time of the attribution does not reveal the time of the conversion.
-    m_earliestTimeToSend = m_timeOfAdClick + 24_h + Seconds(randomNumber() * (24_h).value());
+    auto seconds = 24_h + Seconds(randomNumber() * (24_h).value());
+    m_earliestTimeToSend = WallTime::now() + seconds;
+    return seconds;
 }
 
 URL AdClickAttribution::url() const
@@ -99,7 +101,7 @@ URL AdClickAttribution::url() const
     StringBuilder builder;
     builder.appendLiteral("https://");
     builder.append(m_source.registrableDomain.string());
-    builder.appendLiteral("/.well-known/ad-click-attribution/");
+    builder.appendLiteral(adClickAttributionPathPrefix);
     builder.appendNumber(m_conversion.value().data);
     builder.append('/');
     builder.appendNumber(m_campaign.id);
@@ -128,6 +130,32 @@ URL AdClickAttribution::referrer() const
     return URL();
 }
 
+URL AdClickAttribution::urlForTesting(const URL& baseURL) const
+{
+    auto host = m_source.registrableDomain.string();
+    if (host != "localhost" && host != "127.0.0.1")
+        return URL();
+
+    StringBuilder builder;
+    builder.appendLiteral("?conversion=");
+    builder.appendNumber(m_conversion.value().data);
+    builder.appendLiteral("&campaign=");
+    builder.appendNumber(m_campaign.id);
+    return URL(baseURL, builder.toString());
+}
+
+void AdClickAttribution::markConversionAsSent()
+{
+    ASSERT(m_conversion);
+    if (m_conversion)
+        m_conversion->wasSent = Conversion::WasSent::Yes;
+}
+
+bool AdClickAttribution::wasConversionSent() const
+{
+    return m_conversion && m_conversion->wasSent == Conversion::WasSent::Yes;
+}
+
 String AdClickAttribution::toString() const
 {
     StringBuilder builder;
@@ -142,6 +170,15 @@ String AdClickAttribution::toString() const
         builder.appendNumber(m_conversion.value().data);
         builder.appendLiteral("\nConversion priority: ");
         builder.appendNumber(m_conversion.value().priority);
+        builder.appendLiteral("\nConversion earliest time to send: ");
+        if (!m_earliestTimeToSend)
+            builder.appendLiteral("Not set");
+        else {
+            auto secondsUntilSend = *m_earliestTimeToSend - WallTime::now();
+            builder.append((secondsUntilSend >= 24_h && secondsUntilSend <= 48_h) ? "Within 24-48 hours" : "Outside 24-48 hours");
+        }
+        builder.appendLiteral("\nConversion request sent: ");
+        builder.append((wasConversionSent() ? "true" : "false"));
     } else
         builder.appendLiteral("\nNo conversion data.");
     builder.append('\n');
index 88db8c2..0ef30dd 100644 (file)
@@ -203,9 +203,12 @@ public:
     };
     
     struct Conversion {
-        explicit Conversion(ConversionData data, Priority priority)
+        enum class WasSent : bool { No, Yes };
+        
+        Conversion(ConversionData data, Priority priority, WasSent wasSent = WasSent::No)
             : data { data }
             , priority { priority.value }
+            , wasSent { wasSent }
         {
         }
 
@@ -216,6 +219,7 @@ public:
         
         ConversionData data;
         PriorityValue priority;
+        WasSent wasSent = WasSent::No;
 
         template<class Encoder> void encode(Encoder&) const;
         template<class Decoder> static Optional<Conversion> decode(Decoder&);
@@ -231,12 +235,15 @@ public:
     }
 
     WEBCORE_EXPORT static Optional<Conversion> parseConversionRequest(const URL& redirectURL);
-    WEBCORE_EXPORT void setConversion(Conversion&&);
+    WEBCORE_EXPORT Optional<Seconds> convertAndGetEarliestTimeToSend(Conversion&&);
     WEBCORE_EXPORT URL url() const;
+    WEBCORE_EXPORT URL urlForTesting(const URL& baseURLForTesting) const;
     WEBCORE_EXPORT URL referrer() const;
     const Source& source() const { return m_source; };
     const Destination& destination() const { return m_destination; };
     Optional<WallTime> earliestTimeToSend() const { return m_earliestTimeToSend; };
+    WEBCORE_EXPORT void markConversionAsSent();
+    WEBCORE_EXPORT bool wasConversionSent() const;
 
     WEBCORE_EXPORT String toString() const;
 
@@ -304,7 +311,7 @@ Optional<AdClickAttribution> AdClickAttribution::decode(Decoder& decoder)
 template<class Encoder>
 void AdClickAttribution::Conversion::encode(Encoder& encoder) const
 {
-    encoder << data << priority;
+    encoder << data << priority << wasSent;
 }
 
 template<class Decoder>
@@ -320,7 +327,12 @@ Optional<AdClickAttribution::Conversion> AdClickAttribution::Conversion::decode(
     if (!priority)
         return WTF::nullopt;
     
-    return Conversion { WTFMove(*data), Priority { WTFMove(*priority) } };
+    Optional<WasSent> wasSent;
+    decoder >> wasSent;
+    if (!wasSent)
+        return WTF::nullopt;
+    
+    return Conversion { WTFMove(*data), Priority { *priority }, *wasSent };
 }
 
 } // namespace WebCore
index 17ef021..e708156 100644 (file)
@@ -57,7 +57,7 @@ public:
     WEBCORE_EXPORT void stop();
     bool isActive() const;
 
-    Seconds nextFireInterval() const;
+    WEBCORE_EXPORT Seconds nextFireInterval() const;
     Seconds nextUnalignedFireInterval() const;
     Seconds repeatInterval() const { return m_repeatInterval; }
 
index 2dacca6..aaefc0a 100644 (file)
@@ -1,3 +1,86 @@
+2019-04-15  John Wilander  <wilander@apple.com>
+
+        Send delayed Ad Click Attribution conversion requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=196838
+        <rdar://problem/47650157>
+
+        Reviewed by Chris Dumez and Youenn Fablet.
+
+        This patch schedules a conversion request with appropriate data going to the
+        click source as a result of an ad click conversion.
+
+        WebKit::AdClickAttributionManager makes use of existing WebKit::PingLoad
+        infrastructure to make the request. This will probably be reworked into a
+        dedicated load class further on.
+
+        New test infrastructure allows for an override of both the conversion URL
+        and the 24-48 hour timer.
+
+        * NetworkProcess/AdClickAttributionManager.cpp: Added.
+        (WebKit::AdClickAttributionManager::ensureDestinationMapForSource):
+        (WebKit::AdClickAttributionManager::store):
+        (WebKit::AdClickAttributionManager::startTimer):
+            Convenience function to support test override.
+        (WebKit::AdClickAttributionManager::convert):
+            This function now sets the timer.
+        (WebKit::AdClickAttributionManager::fireConversionRequest):
+            Fire an individual request.
+        (WebKit::AdClickAttributionManager::firePendingConversionRequests):
+            This is the timer function that iterates over all pending attributions.
+        (WebKit::AdClickAttributionManager::clear):
+            Now clears the two new test settings members.
+        (WebKit::AdClickAttributionManager::toString const):
+        * NetworkProcess/AdClickAttributionManager.h: Renamed from Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h.
+        (WebKit::AdClickAttributionManager::AdClickAttributionManager):
+        (WebKit::AdClickAttributionManager::setPingLoadFunction):
+        (WebKit::AdClickAttributionManager::setOverrideTimerForTesting):
+        (WebKit::AdClickAttributionManager::setConversionURLForTesting):
+        * NetworkProcess/NetworkAdClickAttribution.cpp: Renamed from Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp.
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::storeAdClickAttribution):
+        (WebKit::NetworkProcess::dumpAdClickAttribution):
+        (WebKit::NetworkProcess::clearAdClickAttribution):
+        (WebKit::NetworkProcess::setAdClickAttributionOverrideTimerForTesting):
+        (WebKit::NetworkProcess::setAdClickAttributionConversionURLForTesting):
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcess.messages.in:
+        * NetworkProcess/NetworkSession.cpp:
+        (WebKit::NetworkSession::NetworkSession):
+        (WebKit::NetworkSession::setAdClickAttributionOverrideTimerForTesting):
+        (WebKit::NetworkSession::setAdClickAttributionConversionURLForTesting):
+        * NetworkProcess/NetworkSession.h:
+        * NetworkProcess/PingLoad.cpp:
+        (WebKit::PingLoad::PingLoad):
+        (WebKit::m_blobFiles):
+        (WebKit::PingLoad::initialize):
+            The PingLoad constructor is now split in two to allow for construction
+            without a WebKit::NetworkConnectionToWebProcess object. The body of
+            the constructor was moved into the new initialize() function which is
+            shared between constructors.
+        * NetworkProcess/PingLoad.h:
+        * Sources.txt:
+            Removed NetworkProcess/NetworkAdClickAttribution.cpp and added
+            NetworkProcess/NetworkAdClickAttribution.cpp.
+        * UIProcess/API/C/WKPage.cpp:
+        (WKPageSetAdClickAttributionOverrideTimerForTesting):
+        (WKPageSetAdClickAttributionConversionURLForTesting):
+        * UIProcess/API/C/WKPagePrivate.h:
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::dumpAdClickAttribution): Deleted.
+        (WebKit::NetworkProcessProxy::clearAdClickAttribution): Deleted.
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::dumpAdClickAttribution):
+        (WebKit::WebPageProxy::clearAdClickAttribution):
+        (WebKit::WebPageProxy::setAdClickAttributionOverrideTimerForTesting):
+        (WebKit::WebPageProxy::setAdClickAttributionConversionURLForTesting):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::dumpAdClickAttribution): Deleted.
+        (WebKit::WebProcessPool::clearAdClickAttribution): Deleted.
+        * UIProcess/WebProcessPool.h:
+        * WebKit.xcodeproj/project.pbxproj:
+
 2019-04-15  Devin Rousso  <drousso@apple.com>
 
         WebDriver: Set Cookie endpoint does not correctly set subdomain cookies
diff --git a/Source/WebKit/NetworkProcess/AdClickAttributionManager.cpp b/Source/WebKit/NetworkProcess/AdClickAttributionManager.cpp
new file mode 100644 (file)
index 0000000..d74b006
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AdClickAttributionManager.h"
+
+#include <WebCore/FetchOptions.h>
+#include <WebCore/FormData.h>
+#include <WebCore/ResourceError.h>
+#include <WebCore/ResourceRequest.h>
+#include <WebCore/ResourceResponse.h>
+#include <WebCore/RuntimeApplicationChecks.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebKit {
+using namespace WebCore;
+
+using Source = AdClickAttribution::Source;
+using Destination = AdClickAttribution::Destination;
+using DestinationMap = HashMap<Destination, AdClickAttribution>;
+using Conversion = AdClickAttribution::Conversion;
+
+DestinationMap& AdClickAttributionManager::ensureDestinationMapForSource(const Source& source)
+{
+    return m_adClickAttributionMap.ensure(source, [] {
+        return DestinationMap { };
+    }).iterator->value;
+}
+
+void AdClickAttributionManager::store(AdClickAttribution&& adClickAttribution)
+{
+    auto& destinationMapForSource = ensureDestinationMapForSource(adClickAttribution.source());
+    destinationMapForSource.add(adClickAttribution.destination(), WTFMove(adClickAttribution));
+}
+
+void AdClickAttributionManager::startTimer(Seconds seconds)
+{
+    m_firePendingConversionRequestsTimer.startOneShot(m_isRunningTest ? 0_s : seconds);
+}
+
+void AdClickAttributionManager::convert(const Source& source, const Destination& destination, Conversion&& conversion)
+{
+    if (!conversion.isValid())
+        return;
+
+    auto sourceIter = m_adClickAttributionMap.find(source);
+    if (sourceIter == m_adClickAttributionMap.end())
+        return;
+
+    auto destinationIter = sourceIter->value.find(destination);
+    if (destinationIter == sourceIter->value.end())
+        return;
+
+    auto secondsUntilSend = destinationIter->value.convertAndGetEarliestTimeToSend(WTFMove(conversion));
+    
+    if (!secondsUntilSend)
+        return;
+    
+    if (m_firePendingConversionRequestsTimer.isActive() && m_firePendingConversionRequestsTimer.nextFireInterval() < *secondsUntilSend)
+        return;
+    
+    startTimer(*secondsUntilSend);
+}
+
+void AdClickAttributionManager::fireConversionRequest(const AdClickAttribution& attribution)
+{
+    auto conversionURL = m_conversionBaseURLForTesting ? attribution.urlForTesting(*m_conversionBaseURLForTesting) : attribution.url();
+    if (conversionURL.isEmpty() || !conversionURL.isValid())
+        return;
+
+    auto conversionReferrerURL = attribution.referrer();
+    if (conversionReferrerURL.isEmpty() || !conversionReferrerURL.isValid())
+        return;
+
+    ResourceRequest request { conversionURL };
+    
+    request.setHTTPMethod("POST"_s);
+    request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0"_s);
+    request.setHTTPReferrer(conversionReferrerURL.string());
+
+    FetchOptions options;
+    options.credentials = FetchOptions::Credentials::Omit;
+    options.redirect = FetchOptions::Redirect::Error;
+
+    static uint64_t identifier = 0;
+    
+    NetworkResourceLoadParameters loadParameters;
+    loadParameters.identifier = ++identifier;
+    loadParameters.request = request;
+    loadParameters.sourceOrigin = SecurityOrigin::create(conversionReferrerURL);
+    loadParameters.parentPID = presentingApplicationPID();
+    // FIXME: Switch to the use of an ephemeral, stateless session.
+    loadParameters.sessionID = PAL::SessionID::defaultSessionID();
+    loadParameters.storedCredentialsPolicy = StoredCredentialsPolicy::DoNotUse;
+    loadParameters.options = options;
+    loadParameters.shouldClearReferrerOnHTTPSToHTTPRedirect = true;
+    loadParameters.shouldRestrictHTTPResponseAccess = false;
+
+#if ENABLE(CONTENT_EXTENSIONS)
+    loadParameters.mainDocumentURL = WTFMove(conversionReferrerURL);
+#endif
+
+    m_pingLoadFunction(WTFMove(loadParameters), [](const WebCore::ResourceError& error, const WebCore::ResourceResponse& response) {
+        // FIXME: Add logging of errors to a dedicated channel.
+        UNUSED_PARAM(response);
+        UNUSED_PARAM(error);
+    });
+}
+
+void AdClickAttributionManager::firePendingConversionRequests()
+{
+    auto nextTimeToFire = Seconds::infinity();
+    for (auto& innerMap : m_adClickAttributionMap.values()) {
+        for (auto& attribution : innerMap.values()) {
+            if (attribution.wasConversionSent()) {
+                ASSERT_NOT_REACHED();
+                continue;
+            }
+            auto earliestTimeToSend = attribution.earliestTimeToSend();
+            if (!earliestTimeToSend)
+                continue;
+
+            auto now = WallTime::now();
+            if (*earliestTimeToSend <= now || m_isRunningTest) {
+                fireConversionRequest(attribution);                
+                attribution.markConversionAsSent();
+                continue;
+            }
+
+            auto seconds = *earliestTimeToSend - now;
+            nextTimeToFire = std::min(nextTimeToFire, seconds);
+        }
+    }
+
+    if (nextTimeToFire < Seconds::infinity())
+        startTimer(nextTimeToFire);
+
+    // FIXME: Add cleaning of sent conversions.
+}
+
+void AdClickAttributionManager::clear(CompletionHandler<void()>&& completionHandler)
+{
+    m_adClickAttributionMap.clear();
+    m_conversionBaseURLForTesting = { };
+    m_isRunningTest = false;
+    completionHandler();
+}
+
+void AdClickAttributionManager::toString(CompletionHandler<void(String)>&& completionHandler) const
+{
+    if (m_adClickAttributionMap.isEmpty())
+        return completionHandler("No stored Ad Click Attribution data."_s);
+
+    StringBuilder builder;
+    for (auto& innerMap : m_adClickAttributionMap.values()) {
+        unsigned attributionNumber = 1;
+        for (auto& attribution : innerMap.values()) {
+            builder.appendLiteral("WebCore::AdClickAttribution ");
+            builder.appendNumber(attributionNumber++);
+            builder.append('\n');
+            builder.append(attribution.toString());
+        }
+    }
+
+    completionHandler(builder.toString());
+}
+
+} // namespace WebKit
 
 #pragma once
 
+#include "NetworkResourceLoadParameters.h"
 #include <WebCore/AdClickAttribution.h>
 #include <WebCore/RegistrableDomain.h>
+#include <WebCore/ResourceError.h>
+#include <WebCore/ResourceResponse.h>
+#include <WebCore/Timer.h>
 #include <wtf/CompletionHandler.h>
 #include <wtf/HashMap.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebKit {
 
-class NetworkAdClickAttribution {
+class AdClickAttributionManager {
 public:
 
     using RegistrableDomain = WebCore::RegistrableDomain;
@@ -43,15 +47,34 @@ public:
     using DestinationMap = HashMap<Destination, AdClickAttribution>;
     using Conversion = WebCore::AdClickAttribution::Conversion;
 
+    AdClickAttributionManager()
+        : m_firePendingConversionRequestsTimer(*this, &AdClickAttributionManager::firePendingConversionRequests)
+    , m_pingLoadFunction([](NetworkResourceLoadParameters&& params, CompletionHandler<void(const WebCore::ResourceError&, const WebCore::ResourceResponse&)>&& completionHandler) {
+        UNUSED_PARAM(params);
+        completionHandler(WebCore::ResourceError(), WebCore::ResourceResponse());
+    })
+    {
+    }
+
     void store(AdClickAttribution&&);
     void convert(const Source&, const Destination&, Conversion&&);
     void clear(CompletionHandler<void()>&&);
     void toString(CompletionHandler<void(String)>&&) const;
+    void setPingLoadFunction(Function<void(NetworkResourceLoadParameters&&, CompletionHandler<void(const WebCore::ResourceError&, const WebCore::ResourceResponse&)>&&)>&& pingLoadFunction) { m_pingLoadFunction = WTFMove(pingLoadFunction); }
+    void setOverrideTimerForTesting(bool value) { m_isRunningTest = value; }
+    void setConversionURLForTesting(URL&& testURL) { m_conversionBaseURLForTesting = WTFMove(testURL); }
 
 private:
     DestinationMap& ensureDestinationMapForSource(const Source&);
+    void startTimer(Seconds);
+    void fireConversionRequest(const AdClickAttribution&);
+    void firePendingConversionRequests();
 
     HashMap<Source, DestinationMap> m_adClickAttributionMap;
+    WebCore::Timer m_firePendingConversionRequestsTimer;
+    Function<void(NetworkResourceLoadParameters&&, CompletionHandler<void(const WebCore::ResourceError&, const WebCore::ResourceResponse&)>&&)> m_pingLoadFunction;
+    bool m_isRunningTest { false };
+    Optional<URL> m_conversionBaseURLForTesting;
 };
     
 } // namespace WebKit
diff --git a/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp b/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp
deleted file mode 100644 (file)
index 391c098..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2019 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "NetworkAdClickAttribution.h"
-
-#include <wtf/text/StringBuilder.h>
-#include <wtf/text/StringHash.h>
-
-namespace WebKit {
-
-using RegistrableDomain = WebCore::RegistrableDomain;
-using AdClickAttribution = WebCore::AdClickAttribution;
-using Source = WebCore::AdClickAttribution::Source;
-using Destination = WebCore::AdClickAttribution::Destination;
-using DestinationMap = HashMap<Destination, AdClickAttribution>;
-using Conversion = WebCore::AdClickAttribution::Conversion;
-
-DestinationMap& NetworkAdClickAttribution::ensureDestinationMapForSource(const Source& source)
-{
-    return m_adClickAttributionMap.ensure(source, [] {
-        return DestinationMap { };
-    }).iterator->value;
-}
-
-void NetworkAdClickAttribution::store(AdClickAttribution&& adClickAttribution)
-{
-    auto& destinationMapForSource = ensureDestinationMapForSource(adClickAttribution.source());
-    destinationMapForSource.add(adClickAttribution.destination(), WTFMove(adClickAttribution));
-}
-
-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));
-}
-
-void NetworkAdClickAttribution::clear(CompletionHandler<void()>&& completionHandler)
-{
-    m_adClickAttributionMap.clear();
-    completionHandler();
-}
-
-void NetworkAdClickAttribution::toString(CompletionHandler<void(String)>&& completionHandler) const
-{
-    if (m_adClickAttributionMap.isEmpty())
-        return completionHandler("No stored Ad Click Attribution data.");
-
-    StringBuilder builder;
-    for (auto& innerMap : m_adClickAttributionMap.values()) {
-        unsigned attributionNumber = 1;
-        for (auto& attribution : innerMap.values()) {
-            builder.appendLiteral("WebCore::AdClickAttribution ");
-            builder.appendNumber(attributionNumber++);
-            builder.append('\n');
-            builder.append(attribution.toString());
-        }
-    }
-
-    completionHandler(builder.toString());
-}
-
-} // namespace WebKit
index ebcc9be..1fb955d 100644 (file)
@@ -2497,13 +2497,13 @@ void NetworkProcess::platformSyncAllCookies(CompletionHandler<void()>&& completi
 
 void NetworkProcess::storeAdClickAttribution(PAL::SessionID sessionID, WebCore::AdClickAttribution&& adClickAttribution)
 {
-    if (auto session = networkSession(sessionID))
+    if (auto* session = networkSession(sessionID))
         session->storeAdClickAttribution(WTFMove(adClickAttribution));
 }
 
 void NetworkProcess::dumpAdClickAttribution(PAL::SessionID sessionID, CompletionHandler<void(String)>&& completionHandler)
 {
-    if (auto session = networkSession(sessionID))
+    if (auto* session = networkSession(sessionID))
         return session->dumpAdClickAttribution(WTFMove(completionHandler));
 
     completionHandler({ });
@@ -2511,10 +2511,26 @@ void NetworkProcess::dumpAdClickAttribution(PAL::SessionID sessionID, Completion
 
 void NetworkProcess::clearAdClickAttribution(PAL::SessionID sessionID, CompletionHandler<void()>&& completionHandler)
 {
-    if (auto session = networkSession(sessionID))
+    if (auto* session = networkSession(sessionID))
         return session->clearAdClickAttribution(WTFMove(completionHandler));
     
     completionHandler();
 }
 
+void NetworkProcess::setAdClickAttributionOverrideTimerForTesting(PAL::SessionID sessionID, bool value, CompletionHandler<void()>&& completionHandler)
+{
+    if (auto* session = networkSession(sessionID))
+        session->setAdClickAttributionOverrideTimerForTesting(value);
+    
+    completionHandler();
+}
+
+void NetworkProcess::setAdClickAttributionConversionURLForTesting(PAL::SessionID sessionID, URL&& url, CompletionHandler<void()>&& completionHandler)
+{
+    if (auto* session = networkSession(sessionID))
+        session->setAdClickAttributionConversionURLForTesting(WTFMove(url));
+    
+    completionHandler();
+}
+
 } // namespace WebKit
index 0bfe3dc..a62ccb8 100644 (file)
@@ -328,6 +328,8 @@ public:
     void storeAdClickAttribution(PAL::SessionID, WebCore::AdClickAttribution&&);
     void dumpAdClickAttribution(PAL::SessionID, CompletionHandler<void(String)>&&);
     void clearAdClickAttribution(PAL::SessionID, CompletionHandler<void()>&&);
+    void setAdClickAttributionOverrideTimerForTesting(PAL::SessionID, bool value, CompletionHandler<void()>&&);
+    void setAdClickAttributionConversionURLForTesting(PAL::SessionID, URL&&, CompletionHandler<void()>&&);
 
     WebCore::StorageQuotaManager& storageQuotaManager(PAL::SessionID, const WebCore::ClientOrigin&);
 
index dbf3ca7..8764688 100644 (file)
@@ -169,4 +169,6 @@ messages -> NetworkProcess LegacyReceiver {
     StoreAdClickAttribution(PAL::SessionID sessionID, WebCore::AdClickAttribution adClickAttribution)
     DumpAdClickAttribution(PAL::SessionID sessionID) -> (String adClickAttributionState) Async
     ClearAdClickAttribution(PAL::SessionID sessionID) -> () Async
+    SetAdClickAttributionOverrideTimerForTesting(PAL::SessionID sessionID, bool value) -> () Async
+    SetAdClickAttributionConversionURLForTesting(PAL::SessionID sessionID, URL url) -> () Async
 }
index 98cd05b..ed138c2 100644 (file)
 #include "config.h"
 #include "NetworkSession.h"
 
-#include "NetworkAdClickAttribution.h"
+#include "AdClickAttributionManager.h"
 #include "NetworkProcess.h"
 #include "NetworkProcessProxyMessages.h"
+#include "NetworkResourceLoadParameters.h"
+#include "PingLoad.h"
 #include "WebPageProxy.h"
 #include "WebPageProxyMessages.h"
 #include "WebProcessProxy.h"
@@ -71,8 +73,14 @@ NetworkStorageSession& NetworkSession::networkStorageSession() const
 NetworkSession::NetworkSession(NetworkProcess& networkProcess, PAL::SessionID sessionID)
     : m_sessionID(sessionID)
     , m_networkProcess(networkProcess)
-    , m_adClickAttribution(makeUniqueRef<NetworkAdClickAttribution>())
+    , m_adClickAttribution(makeUniqueRef<AdClickAttributionManager>())
 {
+    m_adClickAttribution->setPingLoadFunction([this, weakThis = makeWeakPtr(this)](NetworkResourceLoadParameters&& loadParameters, CompletionHandler<void(const WebCore::ResourceError&, const WebCore::ResourceResponse&)>&& completionHandler) {
+        if (!weakThis)
+            return;
+        // PingLoad manages its own lifetime, deleting itself when its purpose has been fulfilled.
+        new PingLoad(m_networkProcess, WTFMove(loadParameters), WTFMove(completionHandler));
+    });
 }
 
 NetworkSession::~NetworkSession()
@@ -155,4 +163,14 @@ void NetworkSession::clearAdClickAttribution(CompletionHandler<void()>&& complet
     m_adClickAttribution->clear(WTFMove(completionHandler));
 }
 
+void NetworkSession::setAdClickAttributionOverrideTimerForTesting(bool value)
+{
+    m_adClickAttribution->setOverrideTimerForTesting(value);
+}
+
+void NetworkSession::setAdClickAttributionConversionURLForTesting(URL&& url)
+{
+    m_adClickAttribution->setConversionURLForTesting(WTFMove(url));
+}
+
 } // namespace WebKit
index a9ea287..56ea2a0 100644 (file)
@@ -45,7 +45,7 @@ enum class ShouldSample : bool;
 
 namespace WebKit {
 
-class NetworkAdClickAttribution;
+class AdClickAttributionManager;
 class NetworkDataTask;
 class NetworkProcess;
 class WebResourceLoadStatisticsStore;
@@ -83,6 +83,8 @@ public:
     void convertAdClickAttribution(const WebCore::AdClickAttribution::Source&, const WebCore::AdClickAttribution::Destination&, WebCore::AdClickAttribution::Conversion&&);
     void dumpAdClickAttribution(CompletionHandler<void(String)>&&);
     void clearAdClickAttribution(CompletionHandler<void()>&&);
+    void setAdClickAttributionOverrideTimerForTesting(bool value);
+    void setAdClickAttributionConversionURLForTesting(URL&&);
 
 protected:
     NetworkSession(NetworkProcess&, PAL::SessionID);
@@ -97,7 +99,7 @@ protected:
     EnableResourceLoadStatisticsDebugMode m_enableResourceLoadStatisticsDebugMode { EnableResourceLoadStatisticsDebugMode::No };
     WebCore::RegistrableDomain m_resourceLoadStatisticsManualPrevalentResource;
 #endif
-    UniqueRef<NetworkAdClickAttribution> m_adClickAttribution;
+    UniqueRef<AdClickAttributionManager> m_adClickAttribution;
 };
 
 } // namespace WebKit
index 29685e7..4955302 100644 (file)
@@ -40,6 +40,15 @@ namespace WebKit {
 
 using namespace WebCore;
 
+PingLoad::PingLoad(NetworkProcess& networkProcess, NetworkResourceLoadParameters&& parameters, CompletionHandler<void(const ResourceError&, const ResourceResponse&)>&& completionHandler)
+    : m_parameters(WTFMove(parameters))
+    , m_completionHandler(WTFMove(completionHandler))
+    , m_timeoutTimer(*this, &PingLoad::timeoutTimerFired)
+    , m_networkLoadChecker(makeUniqueRef<NetworkLoadChecker>(networkProcess, FetchOptions { m_parameters.options}, m_parameters.sessionID, m_parameters.webPageID, m_parameters.webFrameID, WTFMove(m_parameters.originalRequestHeaders), URL { m_parameters.request.url() }, m_parameters.sourceOrigin.copyRef(), m_parameters.preflightPolicy, m_parameters.request.httpReferrer()))
+{
+    initialize(networkProcess);
+}
+
 PingLoad::PingLoad(NetworkConnectionToWebProcess& connection, NetworkProcess& networkProcess, NetworkResourceLoadParameters&& parameters, CompletionHandler<void(const ResourceError&, const ResourceResponse&)>&& completionHandler)
     : m_parameters(WTFMove(parameters))
     , m_completionHandler(WTFMove(completionHandler))
@@ -52,6 +61,11 @@ PingLoad::PingLoad(NetworkConnectionToWebProcess& connection, NetworkProcess& ne
             file->prepareForFileAccess();
     }
 
+    initialize(networkProcess);
+}
+
+void PingLoad::initialize(NetworkProcess& networkProcess)
+{
     m_networkLoadChecker->enableContentExtensionsCheck();
     if (m_parameters.cspResponseHeaders)
         m_networkLoadChecker->setCSPResponseHeaders(WTFMove(m_parameters.cspResponseHeaders.value()));
index 90aaae4..c7e12bb 100644 (file)
@@ -42,9 +42,11 @@ class NetworkProcess;
 class PingLoad final : public CanMakeWeakPtr<PingLoad>, private NetworkDataTaskClient {
 public:
     PingLoad(NetworkConnectionToWebProcess&, NetworkProcess&, NetworkResourceLoadParameters&&, CompletionHandler<void(const WebCore::ResourceError&, const WebCore::ResourceResponse&)>&&);
-    
+    PingLoad(NetworkProcess&, NetworkResourceLoadParameters&&, CompletionHandler<void(const WebCore::ResourceError&, const WebCore::ResourceResponse&)>&&);
+
 private:
     ~PingLoad();
+    void initialize(NetworkProcess&);
 
     const URL& currentURL() const;
 
index 881d14a..3c7281e 100644 (file)
@@ -21,7 +21,7 @@
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 // THE POSSIBILITY OF SUCH DAMAGE.
 
-NetworkProcess/NetworkAdClickAttribution.cpp
+NetworkProcess/AdClickAttributionManager.cpp
 NetworkProcess/NetworkActivityTracker.cpp
 NetworkProcess/NetworkCORSPreflightChecker.cpp
 NetworkProcess/NetworkConnectionToWebProcess.cpp
index 5f665a5..414dc49 100644 (file)
@@ -2738,3 +2738,17 @@ void WKPageClearAdClickAttribution(WKPageRef page, WKPageClearAdClickAttribution
         callback(callbackContext);
     });
 }
+
+void WKPageSetAdClickAttributionOverrideTimerForTesting(WKPageRef page, bool value, WKPageSetAdClickAttributionOverrideTimerForTestingFunction callback, void* callbackContext)
+{
+    toImpl(page)->setAdClickAttributionOverrideTimerForTesting(value, [callbackContext, callback] () {
+        callback(callbackContext);
+    });
+}
+
+void WKPageSetAdClickAttributionConversionURLForTesting(WKPageRef page, WKURLRef URLRef, WKPageSetAdClickAttributionOverrideTimerForTestingFunction callback, void* callbackContext)
+{
+    toImpl(page)->setAdClickAttributionConversionURLForTesting(URL(URL(), toWTFString(URLRef)), [callbackContext, callback] () {
+        callback(callbackContext);
+    });
+}
index b076e75..c99f732 100644 (file)
@@ -187,6 +187,10 @@ typedef void (*WKPageDumpAdClickAttributionFunction)(WKStringRef adClickAttribut
 WK_EXPORT void WKPageDumpAdClickAttribution(WKPageRef, WKPageDumpAdClickAttributionFunction, void* callbackContext);
 typedef void (*WKPageClearAdClickAttributionFunction)(void* functionContext);
 WK_EXPORT void WKPageClearAdClickAttribution(WKPageRef, WKPageClearAdClickAttributionFunction, void* callbackContext);
+typedef void (*WKPageSetAdClickAttributionOverrideTimerForTestingFunction)(void* functionContext);
+WK_EXPORT void WKPageSetAdClickAttributionOverrideTimerForTesting(WKPageRef page, bool value, WKPageSetAdClickAttributionOverrideTimerForTestingFunction callback, void* callbackContext);
+typedef void (*WKPageSetAdClickAttributionConversionURLForTestingFunction)(void* functionContext);
+WK_EXPORT void WKPageSetAdClickAttributionConversionURLForTesting(WKPageRef page, WKURLRef urlString, WKPageSetAdClickAttributionConversionURLForTestingFunction callback, void* callbackContext);
 
 #ifdef __cplusplus
 }
index 472fdee..c344d16 100644 (file)
@@ -1162,26 +1162,6 @@ void NetworkProcessProxy::didDestroyWebUserContentControllerProxy(WebUserContent
 }
 #endif
 
-void NetworkProcessProxy::dumpAdClickAttribution(PAL::SessionID sessionID, CompletionHandler<void(const String&)>&& completionHandler)
-{
-    if (!canSendMessage()) {
-        completionHandler(emptyString());
-        return;
-    }
-
-    sendWithAsyncReply(Messages::NetworkProcess::DumpAdClickAttribution(sessionID), WTFMove(completionHandler));
-}
-
-void NetworkProcessProxy::clearAdClickAttribution(PAL::SessionID sessionID, CompletionHandler<void()>&& completionHandler)
-{
-    if (!canSendMessage()) {
-        completionHandler();
-        return;
-    }
-    
-    sendWithAsyncReply(Messages::NetworkProcess::ClearAdClickAttribution(sessionID), WTFMove(completionHandler));
-}
-
 void NetworkProcessProxy::sendProcessDidTransitionToForeground()
 {
     send(Messages::NetworkProcess::ProcessDidTransitionToForeground(), 0);
index daceeae..fcb2b78 100644 (file)
@@ -170,9 +170,6 @@ public:
     void didDestroyWebUserContentControllerProxy(WebUserContentControllerProxy&);
 #endif
 
-    void dumpAdClickAttribution(PAL::SessionID, CompletionHandler<void(const String&)>&&);
-    void clearAdClickAttribution(PAL::SessionID, CompletionHandler<void()>&&);
-
     void addSession(Ref<WebsiteDataStore>&&);
     void removeSession(PAL::SessionID);
     
index 3121f9b..52d679a 100644 (file)
@@ -8858,12 +8858,46 @@ void WebPageProxy::removeDataDetectedLinks(CompletionHandler<void(const DataDete
 
 void WebPageProxy::dumpAdClickAttribution(CompletionHandler<void(const String&)>&& completionHandler)
 {
-    m_process->processPool().dumpAdClickAttribution(m_websiteDataStore->sessionID(), WTFMove(completionHandler));
+    if (auto* networkProcess = m_process->processPool().networkProcess()) {
+        if (!networkProcess->canSendMessage()) {
+            completionHandler(emptyString());
+            return;
+        }
+        networkProcess->sendWithAsyncReply(Messages::NetworkProcess::DumpAdClickAttribution(m_websiteDataStore->sessionID()), WTFMove(completionHandler));
+    }
 }
 
 void WebPageProxy::clearAdClickAttribution(CompletionHandler<void()>&& completionHandler)
 {
-    m_process->processPool().clearAdClickAttribution(m_websiteDataStore->sessionID(), WTFMove(completionHandler));
+    if (auto* networkProcess = m_process->processPool().networkProcess()) {
+        if (!networkProcess->canSendMessage()) {
+            completionHandler();
+            return;
+        }
+        networkProcess->sendWithAsyncReply(Messages::NetworkProcess::ClearAdClickAttribution(m_websiteDataStore->sessionID()), WTFMove(completionHandler));
+    }
+}
+
+void WebPageProxy::setAdClickAttributionOverrideTimerForTesting(bool value, CompletionHandler<void()>&& completionHandler)
+{
+    if (auto* networkProcess = m_process->processPool().networkProcess()) {
+        if (!networkProcess->canSendMessage()) {
+            completionHandler();
+            return;
+        }
+        networkProcess->sendWithAsyncReply(Messages::NetworkProcess::SetAdClickAttributionOverrideTimerForTesting(m_websiteDataStore->sessionID(), value), WTFMove(completionHandler));
+    }
+}
+
+void WebPageProxy::setAdClickAttributionConversionURLForTesting(const URL& url, CompletionHandler<void()>&& completionHandler)
+{
+    if (auto* networkProcess = m_process->processPool().networkProcess()) {
+        if (!networkProcess->canSendMessage()) {
+            completionHandler();
+            return;
+        }
+        networkProcess->sendWithAsyncReply(Messages::NetworkProcess::SetAdClickAttributionConversionURLForTesting(m_websiteDataStore->sessionID(), url), WTFMove(completionHandler));
+    }
 }
 
 #if ENABLE(SPEECH_SYNTHESIS)
index 3e86af9..cf00c64 100644 (file)
@@ -1504,6 +1504,8 @@ public:
 
     void dumpAdClickAttribution(CompletionHandler<void(const String&)>&&);
     void clearAdClickAttribution(CompletionHandler<void()>&&);
+    void setAdClickAttributionOverrideTimerForTesting(bool value, CompletionHandler<void()>&&);
+    void setAdClickAttributionConversionURLForTesting(const URL&, CompletionHandler<void()>&&);
 
 #if ENABLE(SPEECH_SYNTHESIS)
     void speechSynthesisVoiceList(CompletionHandler<void(Vector<WebSpeechSynthesisVoice>&&)>&&);
index 2a60469..7cb1bb4 100644 (file)
@@ -2507,26 +2507,6 @@ void WebProcessPool::clearCurrentModifierStateForTesting()
     sendToAllProcesses(Messages::WebProcess::ClearCurrentModifierStateForTesting());
 }
 
-void WebProcessPool::dumpAdClickAttribution(PAL::SessionID sessionID, CompletionHandler<void(const String&)>&& completionHandler)
-{
-    if (!m_networkProcess) {
-        completionHandler(emptyString());
-        return;
-    }
-
-    m_networkProcess->dumpAdClickAttribution(sessionID, WTFMove(completionHandler));
-}
-
-void WebProcessPool::clearAdClickAttribution(PAL::SessionID sessionID, CompletionHandler<void()>&& completionHandler)
-{
-    if (!m_networkProcess) {
-        completionHandler();
-        return;
-    }
-    
-    m_networkProcess->clearAdClickAttribution(sessionID, WTFMove(completionHandler));
-}
-    
 void WebProcessPool::committedCrossSiteLoadWithLinkDecoration(PAL::SessionID sessionID, const RegistrableDomain& fromDomain, const RegistrableDomain& toDomain, uint64_t pageID)
 {
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
index 3bb1f75..49ef972 100644 (file)
@@ -493,8 +493,6 @@ public:
     void sendDisplayConfigurationChangedMessageForTesting();
     void clearCurrentModifierStateForTesting();
 
-    void dumpAdClickAttribution(PAL::SessionID, CompletionHandler<void(const String&)>&&);
-    void clearAdClickAttribution(PAL::SessionID, CompletionHandler<void()>&&);
     void committedCrossSiteLoadWithLinkDecoration(PAL::SessionID, const WebCore::RegistrableDomain& fromDomain, const WebCore::RegistrableDomain& toDomain, uint64_t pageID);
 
 #if PLATFORM(GTK) || PLATFORM(WPE)
index 4c94689..e10151a 100644 (file)
                63C32C281E98119000699BD0 /* _WKGeolocationPositionInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 63C32C271E98119000699BD0 /* _WKGeolocationPositionInternal.h */; };
                65B86F1E12F11DE300B7DD8A /* WKBundleInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 65B86F1812F11D7B00B7DD8A /* WKBundleInspector.h */; settings = {ATTRIBUTES = (Private, ); }; };
                6A5080BF1F0EDAAA00E617C5 /* WKWindowFeaturesPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A5080BE1F0EDAAA00E617C5 /* WKWindowFeaturesPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               6BD05865220CE8C2000BED5C /* NetworkAdClickAttribution.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD05863220CE8C2000BED5C /* NetworkAdClickAttribution.h */; };
+               6BD05865220CE8C2000BED5C /* AdClickAttributionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD05863220CE8C2000BED5C /* AdClickAttributionManager.h */; };
                6BE969C11E54D452008B7483 /* corePrediction_model in Resources */ = {isa = PBXBuildFile; fileRef = 6BE969C01E54D452008B7483 /* corePrediction_model */; };
                6BE969CB1E54D4CF008B7483 /* ResourceLoadStatisticsClassifierCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BE969C91E54D4CF008B7483 /* ResourceLoadStatisticsClassifierCocoa.h */; };
                6BE969CD1E54E054008B7483 /* ResourceLoadStatisticsClassifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BE969CC1E54E054008B7483 /* ResourceLoadStatisticsClassifier.h */; };
                65B86F1712F11D7B00B7DD8A /* WKBundleInspector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKBundleInspector.cpp; sourceTree = "<group>"; };
                65B86F1812F11D7B00B7DD8A /* WKBundleInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKBundleInspector.h; sourceTree = "<group>"; };
                6A5080BE1F0EDAAA00E617C5 /* WKWindowFeaturesPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKWindowFeaturesPrivate.h; sourceTree = "<group>"; };
-               6BD05863220CE8C2000BED5C /* NetworkAdClickAttribution.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NetworkAdClickAttribution.h; sourceTree = "<group>"; };
-               6BD05864220CE8C2000BED5C /* NetworkAdClickAttribution.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkAdClickAttribution.cpp; sourceTree = "<group>"; };
+               6BD05863220CE8C2000BED5C /* AdClickAttributionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AdClickAttributionManager.h; sourceTree = "<group>"; };
+               6BD05864220CE8C2000BED5C /* AdClickAttributionManager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AdClickAttributionManager.cpp; sourceTree = "<group>"; };
                6BE969C01E54D452008B7483 /* corePrediction_model */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = corePrediction_model; sourceTree = "<group>"; };
                6BE969C61E54D4B6008B7483 /* ResourceLoadStatisticsClassifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResourceLoadStatisticsClassifier.cpp; sourceTree = "<group>"; };
                6BE969C81E54D4CF008B7483 /* ResourceLoadStatisticsClassifierCocoa.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResourceLoadStatisticsClassifierCocoa.cpp; sourceTree = "<group>"; };
                                510CC7DC16138E2900D03ED3 /* mac */,
                                93BA04D92151ADCD007F455F /* ServiceWorker */,
                                413075971DE84ED70039EC69 /* webrtc */,
+                               6BD05864220CE8C2000BED5C /* AdClickAttributionManager.cpp */,
+                               6BD05863220CE8C2000BED5C /* AdClickAttributionManager.h */,
                                53F3CAA5206C443E0086490E /* NetworkActivityTracker.cpp */,
                                535BCB902069C49C00CCCE02 /* NetworkActivityTracker.h */,
-                               6BD05864220CE8C2000BED5C /* NetworkAdClickAttribution.cpp */,
-                               6BD05863220CE8C2000BED5C /* NetworkAdClickAttribution.h */,
                                513A16491630A9BF005D7D22 /* NetworkConnectionToWebProcess.cpp */,
                                513A164A1630A9BF005D7D22 /* NetworkConnectionToWebProcess.h */,
                                513A164B1630A9BF005D7D22 /* NetworkConnectionToWebProcess.messages.in */,
                                A182D5B51BE6BD250087A7CC /* AccessibilityIOS.h in Headers */,
                                2DB7667121B5E48A0045DDB1 /* AccessibilitySupportSPI.h in Headers */,
                                A7D792D81767CCA300881CBE /* ActivityAssertion.h in Headers */,
+                               6BD05865220CE8C2000BED5C /* AdClickAttributionManager.h in Headers */,
                                634842511FB26E7100946E3C /* APIApplicationManifest.h in Headers */,
                                BC64697011DBE603006455B0 /* APIArray.h in Headers */,
                                2E5C770E1FA7D429005932C3 /* APIAttachment.h in Headers */,
                                1A4A9C5612B816CF008FE984 /* NetscapePluginModule.h in Headers */,
                                1AA5889211EE70400061B882 /* NetscapePluginStream.h in Headers */,
                                535BCB922069C49C00CCCE02 /* NetworkActivityTracker.h in Headers */,
-                               6BD05865220CE8C2000BED5C /* NetworkAdClickAttribution.h in Headers */,
                                A105D0D1212734B20034F6C7 /* NetworkBlobRegistry.h in Headers */,
                                E4436ECC1A0D040B00EAD204 /* NetworkCache.h in Headers */,
                                E49D40D71AD3FB170066B7B9 /* NetworkCacheBlobStorage.h in Headers */,
index 87cd8a6..9bddb95 100644 (file)
@@ -1,3 +1,30 @@
+2019-04-15  John Wilander  <wilander@apple.com>
+
+        Send delayed Ad Click Attribution conversion requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=196838
+        <rdar://problem/47650157>
+
+        Reviewed by Chris Dumez and Youenn Fablet.
+
+        This patch adds test infrastructure to override the default behavior in
+        WebKit::NetworkAdClickAttribution.
+
+        * TestWebKitAPI/Tests/WebCore/AdClickAttribution.cpp:
+        (TestWebKitAPI::TEST):
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::dumpAdClickAttribution):
+        (WTR::TestRunner::clearAdClickAttribution):
+        (WTR::TestRunner::setAdClickAttributionOverrideTimerForTesting):
+        (WTR::TestRunner::setAdClickAttributionConversionURLForTesting):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::setAdClickAttributionOverrideTimerForTesting):
+        (WTR::TestController::setAdClickAttributionConversionURLForTesting):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+
 2019-04-15  Aakash Jain  <aakash_jain@apple.com>
 
         [ews-app] status-bubble should display position in queue
index 3a9813e..fa0a786 100644 (file)
@@ -44,7 +44,7 @@ const URL emptyURL { };
 TEST(AdClickAttribution, ValidMinValues)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign(min6BitValue), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(min6BitValue, AdClickAttribution::Priority(min6BitValue)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion(min6BitValue, AdClickAttribution::Priority(min6BitValue)));
 
     auto attributionURL = attribution.url();
     auto referrerURL = attribution.referrer();
@@ -56,7 +56,7 @@ TEST(AdClickAttribution, ValidMinValues)
 TEST(AdClickAttribution, ValidMidValues)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign((uint32_t)12), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion((uint32_t)44, AdClickAttribution::Priority((uint32_t)22)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion((uint32_t)44, AdClickAttribution::Priority((uint32_t)22)));
 
     auto attributionURL = attribution.url();
     auto referrerURL = attribution.referrer();
@@ -68,7 +68,7 @@ TEST(AdClickAttribution, ValidMidValues)
 TEST(AdClickAttribution, ValidMaxValues)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
 
     auto attributionURL = attribution.url();
     auto referrerURL = attribution.referrer();
@@ -81,7 +81,7 @@ TEST(AdClickAttribution, EarliestTimeToSendAttributionMinimumDelay)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
     auto now = WallTime::now();
-    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
     auto earliestTimeToSend = attribution.earliestTimeToSend();
     ASSERT_TRUE(earliestTimeToSend);
     ASSERT_TRUE(earliestTimeToSend.value().secondsSinceEpoch() - 24_h >= now.secondsSinceEpoch());
@@ -128,7 +128,7 @@ TEST(AdClickAttribution, ValidConversionURLs)
 TEST(AdClickAttribution, InvalidCampaignId)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy + 1), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
 
     auto attributionURL = attribution.url();
     auto referrerURL = attribution.referrer();
@@ -140,7 +140,7 @@ TEST(AdClickAttribution, InvalidCampaignId)
 TEST(AdClickAttribution, InvalidSourceHost)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { emptyURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
 
     auto attributionURL = attribution.url();
     auto referrerURL = attribution.referrer();
@@ -152,7 +152,7 @@ TEST(AdClickAttribution, InvalidSourceHost)
 TEST(AdClickAttribution, InvalidDestinationHost)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy + 1), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { emptyURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
 
     auto attributionURL = attribution.url();
     auto referrerURL = attribution.referrer();
@@ -164,7 +164,7 @@ TEST(AdClickAttribution, InvalidDestinationHost)
 TEST(AdClickAttribution, InvalidConversionData)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion((AdClickAttribution::MaxEntropy + 1), AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion((AdClickAttribution::MaxEntropy + 1), AdClickAttribution::Priority(AdClickAttribution::MaxEntropy)));
 
     auto attributionURL = attribution.url();
     auto referrerURL = attribution.referrer();
@@ -176,7 +176,7 @@ TEST(AdClickAttribution, InvalidConversionData)
 TEST(AdClickAttribution, InvalidPriority)
 {
     AdClickAttribution attribution { AdClickAttribution::Campaign(AdClickAttribution::MaxEntropy), AdClickAttribution::Source { webKitURL }, AdClickAttribution::Destination { exampleURL } };
-    attribution.setConversion(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy + 1)));
+    attribution.convertAndGetEarliestTimeToSend(AdClickAttribution::Conversion(AdClickAttribution::MaxEntropy, AdClickAttribution::Priority(AdClickAttribution::MaxEntropy + 1)));
 
     auto attributionURL = attribution.url();
     auto referrerURL = attribution.referrer();
index 087da84..e7b5813 100644 (file)
@@ -381,4 +381,6 @@ interface TestRunner {
 
     // Ad Click Attribution
     void clearAdClickAttribution();
+    void setAdClickAttributionOverrideTimerForTesting(boolean value);
+    void setAdClickAttributionConversionURLForTesting(DOMString url);
 };
index 1c9af33..50b999e 100644 (file)
@@ -2795,14 +2795,29 @@ void TestRunner::setShouldDismissJavaScriptAlertsAsynchronously(bool shouldDismi
 
 void TestRunner::dumpAdClickAttribution()
 {
-    auto messageName = adoptWK(WKStringCreateWithUTF8CString("dumpAdClickAttribution"));
+    auto messageName = adoptWK(WKStringCreateWithUTF8CString("DumpAdClickAttribution"));
     WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), nullptr, nullptr);
 }
 
 void TestRunner::clearAdClickAttribution()
 {
-    auto messageName = adoptWK(WKStringCreateWithUTF8CString("clearAdClickAttribution"));
+    auto messageName = adoptWK(WKStringCreateWithUTF8CString("ClearAdClickAttribution"));
     WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), nullptr, nullptr);
 }
 
+void TestRunner::setAdClickAttributionOverrideTimerForTesting(bool value)
+{
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAdClickAttributionOverrideTimerForTesting"));
+    WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(value));
+    WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get(), nullptr);
+}
+
+void TestRunner::setAdClickAttributionConversionURLForTesting(JSStringRef urlString)
+{
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAdClickAttributionConversionURLForTesting"));
+    auto wtfURLString = toWTFString(WKStringCreateWithJSString(urlString));
+    WKRetainPtr<WKURLRef> messageBody(AdoptWK, WKURLCreateWithUTF8CString(wtfURLString.utf8().data()));
+    WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get(), nullptr);
+}
+
 } // namespace WTR
index 84ab219..09a95ea 100644 (file)
@@ -494,6 +494,8 @@ public:
     // Ad Click Attribution.
     void dumpAdClickAttribution();
     void clearAdClickAttribution();
+    void setAdClickAttributionOverrideTimerForTesting(bool value);
+    void setAdClickAttributionConversionURLForTesting(JSStringRef);
 
 private:
     TestRunner();
index 73db303..ae547b6 100644 (file)
@@ -3574,4 +3574,18 @@ void TestController::clearAdClickAttribution()
     runUntil(callbackContext.done, noTimeout);
 }
 
+void TestController::setAdClickAttributionOverrideTimerForTesting(bool value)
+{
+    AdClickAttributionVoidCallbackContext callbackContext(*this);
+    WKPageSetAdClickAttributionOverrideTimerForTesting(m_mainWebView->page(), value, adClickAttributionVoidCallback, &callbackContext);
+    runUntil(callbackContext.done, noTimeout);
+}
+
+void TestController::setAdClickAttributionConversionURLForTesting(WKURLRef url)
+{
+    AdClickAttributionVoidCallbackContext callbackContext(*this);
+    WKPageSetAdClickAttributionConversionURLForTesting(m_mainWebView->page(), url, adClickAttributionVoidCallback, &callbackContext);
+    runUntil(callbackContext.done, noTimeout);
+}
+
 } // namespace WTR
index de9130c..689526d 100644 (file)
@@ -306,6 +306,8 @@ public:
 
     String dumpAdClickAttribution();
     void clearAdClickAttribution();
+    void setAdClickAttributionOverrideTimerForTesting(bool value);
+    void setAdClickAttributionConversionURLForTesting(WKURLRef);
 
 private:
     WKRetainPtr<WKPageConfigurationRef> generatePageConfiguration(const TestOptions&);
index 4c7037b..0699cc7 100644 (file)
@@ -1606,16 +1606,30 @@ WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
         return nullptr;
     }
 
-    if (WKStringIsEqualToUTF8CString(messageName, "dumpAdClickAttribution")) {
+    if (WKStringIsEqualToUTF8CString(messageName, "DumpAdClickAttribution")) {
         dumpAdClickAttribution();
         return nullptr;
     }
     
-    if (WKStringIsEqualToUTF8CString(messageName, "clearAdClickAttribution")) {
+    if (WKStringIsEqualToUTF8CString(messageName, "ClearAdClickAttribution")) {
         TestController::singleton().clearAdClickAttribution();
         return nullptr;
     }
     
+    if (WKStringIsEqualToUTF8CString(messageName, "SetAdClickAttributionOverrideTimerForTesting")) {
+        ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+        WKBooleanRef value = static_cast<WKBooleanRef>(messageBody);
+        TestController::singleton().setAdClickAttributionOverrideTimerForTesting(WKBooleanGetValue(value));
+        return nullptr;
+    }
+    
+    if (WKStringIsEqualToUTF8CString(messageName, "SetAdClickAttributionConversionURLForTesting")) {
+        ASSERT(WKGetTypeID(messageBody) == WKURLGetTypeID());
+        WKURLRef url = static_cast<WKURLRef>(messageBody);
+        TestController::singleton().setAdClickAttributionConversionURLForTesting(url);
+        return nullptr;
+    }
+    
     ASSERT_NOT_REACHED();
     return nullptr;
 }