Store Ad Click Attribution requests in the network process
authorwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 Feb 2019 20:47:00 +0000 (20:47 +0000)
committerwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 13 Feb 2019 20:47:00 +0000 (20:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=194510
<rdar://problem/47650118>

Reviewed by Alex Christensen and Daniel Bates.

Source/WebCore:

Test: http/tests/adClickAttribution/store-ad-click-attribution.html

This patch adds support functions for validation and storage of
WebCore::AdClickAttribution objects. It also adds WTF::HashTraits so that
WebCore::AdClickAttribution::Source and WebCore::AdClickAttribution::Destination
can be used in a HashMap.

* loader/AdClickAttribution.cpp:
(WebCore::AdClickAttribution::toString const):
* loader/AdClickAttribution.h:
(WebCore::AdClickAttribution::Source::operator== const):
(WebCore::AdClickAttribution::Source::deletedValue):
(WebCore::AdClickAttribution::Source::constructDeletedValue):
(WebCore::AdClickAttribution::Source::deleteValue):
(WebCore::AdClickAttribution::Source::isDeletedValue const):
(WebCore::AdClickAttribution::SourceHash::hash):
(WebCore::AdClickAttribution::SourceHash::equal):
(WebCore::AdClickAttribution::Destination::operator== const):
(WebCore::AdClickAttribution::Destination::matches const):
    This convenience function allows matching of a WTF::URL object.
(WebCore::AdClickAttribution::Destination::deletedValue):
(WebCore::AdClickAttribution::Destination::constructDeletedValue):
(WebCore::AdClickAttribution::Destination::deleteValue):
(WebCore::AdClickAttribution::Destination::isDeletedValue const):
(WebCore::AdClickAttribution::DestinationHash::hash):
(WebCore::AdClickAttribution::DestinationHash::equal):
(WebCore::AdClickAttribution::source const):
(WebCore::AdClickAttribution::destination const):
    Getters added to support mapped storage based on source and destination.
(WTF::HashTraits<WebCore::AdClickAttribution::Source>::emptyValue):
(WTF::HashTraits<WebCore::AdClickAttribution::Source>::constructDeletedValue):
(WTF::HashTraits<WebCore::AdClickAttribution::Source>::isDeletedValue):
(WTF::HashTraits<WebCore::AdClickAttribution::Destination>::emptyValue):
(WTF::HashTraits<WebCore::AdClickAttribution::Destination>::constructDeletedValue):
(WTF::HashTraits<WebCore::AdClickAttribution::Destination>::isDeletedValue):
* loader/NavigationAction.h:
(WebCore::NavigationAction::adClickAttribution const):
(WebCore::NavigationAction::adClickAttribution): Deleted.
    Corrected the constness of this function.

Source/WebKit:

With this patch, WebPageProxy::didCommitLoadForFrame() now looks for
an AdClickAttribution object in its navigation state. If there is an
attribution, it sends it to the network process where the
WebKit::NetworkSession stores it in an object of a new class,
WebKit::NetworkAdClickAttribution.

This patch also covers test infrastructure to support two new
TestRunner functions:
- dumpAdClickAttribution()
- clearAdClickAttribution()

* NetworkProcess/Cookies/WebCookieManager.cpp:
    Added missing header include.
* NetworkProcess/Downloads/DownloadManager.cpp:
    Added missing header include.
* NetworkProcess/NetworkAdClickAttribution.cpp: Added.
(WebKit::NetworkAdClickAttribution::ensureDestinationMapForSource):
(WebKit::NetworkAdClickAttribution::store):
(WebKit::NetworkAdClickAttribution::clear):
(WebKit::NetworkAdClickAttribution::toString const):
* NetworkProcess/NetworkAdClickAttribution.h: Added.
    Stores WebCore::AdClickAttribution objects in a map structure.
* NetworkProcess/NetworkLoad.cpp:
    Added missing header includes.
* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::storeAdClickAttribution):
(WebKit::NetworkProcess::dumpAdClickAttribution):
(WebKit::NetworkProcess::clearAdClickAttribution):
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcess.messages.in:
* NetworkProcess/NetworkSession.cpp:
(WebKit::NetworkSession::NetworkSession):
(WebKit::NetworkSession::storeAdClickAttribution):
(WebKit::NetworkSession::dumpAdClickAttribution):
(WebKit::NetworkSession::clearAdClickAttribution):
* NetworkProcess/NetworkSession.h:
* NetworkProcess/soup/RemoteNetworkingContextSoup.cpp:
    Added missing header include.
* Sources.txt:
    Added NetworkProcess/NetworkAdClickAttribution.cpp.
* UIProcess/API/APINavigation.h:
(API::Navigation::adClickAttribution const):
* UIProcess/API/C/WKPage.cpp:
(WKPageDumpAdClickAttribution):
(WKPageClearAdClickAttribution):
    Test infrastructure.
* UIProcess/API/C/WKPagePrivate.h:
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::dumpAdClickAttribution):
(WebKit::NetworkProcessProxy::clearAdClickAttribution):
    Test infrastructure.
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::didFinishDocumentLoadForFrame):
    This is where pending Ad Click Attributions are forwarded to the
    network process.
(WebKit::WebPageProxy::dumpAdClickAttribution):
(WebKit::WebPageProxy::clearAdClickAttribution):
    Test infrastructure.
* UIProcess/WebPageProxy.h:
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::dumpAdClickAttribution):
(WebKit::WebProcessPool::clearAdClickAttribution):
    Test infrastructure.
* UIProcess/WebProcessPool.h:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
    Added missing data copying from navigationAction to navigationActionData.

Tools:

This patch adds two TestRunner functions:
- dumpAdClickAttribution()
- clearAdClickAttribution()

They call into the network process to dump and clear Ad Click
Attribution state, respectively.

* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::dumpAdClickAttribution):
(WTR::TestRunner::clearAdClickAttribution):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::resetStateToConsistentValues):
(WTR::AdClickAttributionStringResultCallbackContext::AdClickAttributionStringResultCallbackContext):
(WTR::adClickAttributionStringResultCallback):
(WTR::TestController::dumpAdClickAttribution):
(WTR::AdClickAttributionVoidCallbackContext::AdClickAttributionVoidCallbackContext):
(WTR::adClickAttributionVoidCallback):
(WTR::TestController::clearAdClickAttribution):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::dumpResults):
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
(WTR::TestInvocation::dumpAdClickAttribution):
* WebKitTestRunner/TestInvocation.h:

LayoutTests:

* TestExpectations:
    Marked "Skip" because the feature is not applicable to WK1.
* http/tests/adClickAttribution/store-ad-click-attribution-expected.txt: Added.
* http/tests/adClickAttribution/store-ad-click-attribution.html: Added.
* platform/wk2/TestExpectations:
    Marked "Pass."

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

41 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/tests/adClickAttribution/store-ad-click-attribution-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/adClickAttribution/store-ad-click-attribution.html [new file with mode: 0644]
LayoutTests/platform/wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/loader/AdClickAttribution.cpp
Source/WebCore/loader/AdClickAttribution.h
Source/WebCore/loader/NavigationAction.h
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/Cookies/WebCookieManager.cpp
Source/WebKit/NetworkProcess/Downloads/DownloadManager.cpp
Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp [new file with mode: 0644]
Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h [new file with mode: 0644]
Source/WebKit/NetworkProcess/NetworkLoad.cpp
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/soup/RemoteNetworkingContextSoup.cpp
Source/WebKit/Sources.txt
Source/WebKit/UIProcess/API/APINavigation.h
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
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
Tools/ChangeLog
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
Tools/WebKitTestRunner/TestInvocation.h

index 52e92b4..5b04327 100644 (file)
@@ -1,3 +1,18 @@
+2019-02-13  John Wilander  <wilander@apple.com>
+
+        Store Ad Click Attribution requests in the network process
+        https://bugs.webkit.org/show_bug.cgi?id=194510
+        <rdar://problem/47650118>
+
+        Reviewed by Alex Christensen and Daniel Bates.
+
+        * TestExpectations:
+            Marked "Skip" because the feature is not applicable to WK1.
+        * http/tests/adClickAttribution/store-ad-click-attribution-expected.txt: Added.
+        * http/tests/adClickAttribution/store-ad-click-attribution.html: Added.
+        * platform/wk2/TestExpectations:
+            Marked "Pass."
+
 2019-02-13  Sihui Liu  <sihui_liu@apple.com>
 
         Add two regression tests for reference cycle in IndexedDB
index e5f294a..90e5118 100644 (file)
@@ -2054,6 +2054,8 @@ webkit.org/b/177440 imported/w3c/web-platform-tests/html/browsers/origin/relaxin
 webkit.org/b/157068 [ Debug ] imported/w3c/web-platform-tests/fetch/nosniff/importscripts.html [ Pass Crash ]
 webkit.org/b/157068 [ Release ] imported/w3c/web-platform-tests/fetch/nosniff/importscripts.html [ Pass Failure ]
 
+http/tests/adClickAttribution [ Skip ]
+
 # Application Manifest tests
 webkit.org/b/153152 http/tests/security/contentSecurityPolicy/manifest-src-allowed.html [ Skip ]
 webkit.org/b/153152 http/tests/security/contentSecurityPolicy/manifest-src-blocked.html [ Skip ]
diff --git a/LayoutTests/http/tests/adClickAttribution/store-ad-click-attribution-expected.txt b/LayoutTests/http/tests/adClickAttribution/store-ad-click-attribution-expected.txt
new file mode 100644 (file)
index 0000000..8ab7f9e
--- /dev/null
@@ -0,0 +1,8 @@
+Tests storage of ad click attribution.
+
+
+WebCore::AdClickAttribution 1
+Source: 127.0.0.1
+Destination: localhost
+Campaign ID: 3
+No conversion data.
diff --git a/LayoutTests/http/tests/adClickAttribution/store-ad-click-attribution.html b/LayoutTests/http/tests/adClickAttribution/store-ad-click-attribution.html
new file mode 100644 (file)
index 0000000..ae64d94
--- /dev/null
@@ -0,0 +1,47 @@
+<!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 storage of ad click attribution.</div>
+<a id="targetLink" href="http://localhost:8000/adClickAttribution/store-ad-click-attribution.html?stepTwo" adcampaignid="3" addestination="http://localhost:8000">Link</a><br>
+<div id="output"></div>
+<script>
+    if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+    }
+
+    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") {
+                testRunner.dumpAdClickAttribution();
+                document.body.removeChild(document.getElementById("targetLink"));
+                testRunner.notifyDone();
+            } else {
+                activateElement("targetLink");
+            }
+        } else {
+            document.getElementById("output").innerText = "FAIL No testRunner.";
+        }
+    }
+</script>
+</body>
+</html>
index 5e47d7c..4779d14 100644 (file)
@@ -749,6 +749,8 @@ storage/indexeddb/modern/opendatabase-after-storage-crash.html [ Pass ]
 
 fast/forms/call-text-did-change-in-text-field-when-typing.html [ Pass ]
 
+http/tests/adClickAttribution [ Pass ]
+
 ### END OF (5) Progressions, expected successes that are expected failures in WebKit1.
 ########################################
 
index ed4afe6..22adac6 100644 (file)
@@ -1,3 +1,51 @@
+2019-02-13  John Wilander  <wilander@apple.com>
+
+        Store Ad Click Attribution requests in the network process
+        https://bugs.webkit.org/show_bug.cgi?id=194510
+        <rdar://problem/47650118>
+
+        Reviewed by Alex Christensen and Daniel Bates.
+
+        Test: http/tests/adClickAttribution/store-ad-click-attribution.html
+
+        This patch adds support functions for validation and storage of
+        WebCore::AdClickAttribution objects. It also adds WTF::HashTraits so that
+        WebCore::AdClickAttribution::Source and WebCore::AdClickAttribution::Destination
+        can be used in a HashMap.
+
+        * loader/AdClickAttribution.cpp:
+        (WebCore::AdClickAttribution::toString const):
+        * loader/AdClickAttribution.h:
+        (WebCore::AdClickAttribution::Source::operator== const):
+        (WebCore::AdClickAttribution::Source::deletedValue):
+        (WebCore::AdClickAttribution::Source::constructDeletedValue):
+        (WebCore::AdClickAttribution::Source::deleteValue):
+        (WebCore::AdClickAttribution::Source::isDeletedValue const):
+        (WebCore::AdClickAttribution::SourceHash::hash):
+        (WebCore::AdClickAttribution::SourceHash::equal):
+        (WebCore::AdClickAttribution::Destination::operator== const):
+        (WebCore::AdClickAttribution::Destination::matches const):
+            This convenience function allows matching of a WTF::URL object.
+        (WebCore::AdClickAttribution::Destination::deletedValue):
+        (WebCore::AdClickAttribution::Destination::constructDeletedValue):
+        (WebCore::AdClickAttribution::Destination::deleteValue):
+        (WebCore::AdClickAttribution::Destination::isDeletedValue const):
+        (WebCore::AdClickAttribution::DestinationHash::hash):
+        (WebCore::AdClickAttribution::DestinationHash::equal):
+        (WebCore::AdClickAttribution::source const):
+        (WebCore::AdClickAttribution::destination const):
+            Getters added to support mapped storage based on source and destination.
+        (WTF::HashTraits<WebCore::AdClickAttribution::Source>::emptyValue):
+        (WTF::HashTraits<WebCore::AdClickAttribution::Source>::constructDeletedValue):
+        (WTF::HashTraits<WebCore::AdClickAttribution::Source>::isDeletedValue):
+        (WTF::HashTraits<WebCore::AdClickAttribution::Destination>::emptyValue):
+        (WTF::HashTraits<WebCore::AdClickAttribution::Destination>::constructDeletedValue):
+        (WTF::HashTraits<WebCore::AdClickAttribution::Destination>::isDeletedValue):
+        * loader/NavigationAction.h:
+        (WebCore::NavigationAction::adClickAttribution const):
+        (WebCore::NavigationAction::adClickAttribution): Deleted.
+            Corrected the constness of this function.
+
 2019-02-13  Eric Carlson  <eric.carlson@apple.com>
 
         Revert r240434
index bdc0639..460cc5c 100644 (file)
@@ -90,4 +90,23 @@ URL AdClickAttribution::referrer() const
     return URL();
 }
 
+String AdClickAttribution::toString() const
+{
+    StringBuilder builder;
+    builder.appendLiteral("Source: ");
+    builder.append(m_source.registrableDomain);
+    builder.appendLiteral("\nDestination: ");
+    builder.append(m_destination.registrableDomain);
+    builder.appendLiteral("\nCampaign ID: ");
+    builder.appendNumber(m_campaign.id);
+    if (m_conversion) {
+        builder.appendLiteral("\nConversion data: ");
+        builder.appendNumber(m_conversion.value().data);
+    } else
+        builder.appendLiteral("\nNo conversion data.");
+    builder.append('\n');
+
+    return builder.toString();
+}
+
 }
index 34af2f2..bec48e3 100644 (file)
 #pragma once
 
 #include "PublicSuffix.h"
-#include <wtf/Noncopyable.h>
 #include <wtf/Optional.h>
+#include <wtf/URL.h>
 #include <wtf/WallTime.h>
+#include <wtf/text/StringHash.h>
 #include <wtf/text/WTFString.h>
 
-namespace WTF {
-class URL;
-}
-
 namespace WebCore {
 
 class AdClickAttribution {
@@ -46,6 +43,7 @@ public:
     static constexpr uint32_t MaxEntropy = 64;
 
     struct Campaign {
+        Campaign() = default;
         explicit Campaign(CampaignId id)
             : id { id }
         {
@@ -56,35 +54,150 @@ public:
             return id < MaxEntropy;
         }
         
-        CampaignId id;
+        CampaignId id { 0 };
     };
 
     struct Source {
+        Source() = default;
         explicit Source(const String& host)
 #if ENABLE(PUBLIC_SUFFIX_LIST)
-            : registrableDomain { WebCore::topPrivatelyControlledDomain(host) }
+            : registrableDomain { topPrivatelyControlledDomain(host) }
 #else
             : registrableDomain { emptyString() }
 #endif
         {
         }
 
+        explicit Source(WTF::HashTableDeletedValueType)
+            : registrableDomain(WTF::HashTableDeletedValue)
+        {
+        }
+
+        bool operator==(const Source& other) const
+        {
+            return registrableDomain == other.registrableDomain;
+        }
+
+        bool matches(const URL& url) const
+        {
+#if ENABLE(PUBLIC_SUFFIX_LIST)
+            return registrableDomain == topPrivatelyControlledDomain(url.host().toString());
+#else
+            return registrableDomain == url.host().toString();
+#endif
+        }
+
+        bool isHashTableDeletedValue() const
+        {
+            return registrableDomain.isHashTableDeletedValue();
+        }
+
+        static Source deletedValue()
+        {
+            return Source { WTF::HashTableDeletedValue };
+        }
+
+        static void constructDeletedValue(Source& source)
+        {
+            new (&source) Source;
+            source = Source::deletedValue();
+        }
+
+        void deleteValue()
+        {
+            registrableDomain = String { WTF::HashTableDeletedValue };
+        }
+
+        bool isDeletedValue() const
+        {
+            return isHashTableDeletedValue();
+        }
+
         String registrableDomain;
     };
 
+    struct SourceHash {
+        static unsigned hash(const Source& source)
+        {
+            return source.registrableDomain.hash();
+        }
+        
+        static bool equal(const Source& a, const Source& b)
+        {
+            return a == b;
+        }
+
+        static const bool safeToCompareToEmptyOrDeleted = false;
+    };
+
     struct Destination {
+        Destination() = default;
         explicit Destination(const String& host)
 #if ENABLE(PUBLIC_SUFFIX_LIST)
-            : registrableDomain { WebCore::topPrivatelyControlledDomain(host) }
+            : registrableDomain { topPrivatelyControlledDomain(host) }
 #else
             : registrableDomain { emptyString() }
 #endif
         {
         }
 
+        bool operator==(const Destination& other) const
+        {
+            return registrableDomain == other.registrableDomain;
+        }
+
+        bool matches(const URL& url) const
+        {
+#if ENABLE(PUBLIC_SUFFIX_LIST)
+            return registrableDomain == topPrivatelyControlledDomain(url.host().toString());
+#else
+            return registrableDomain == url.host().toString();
+#endif
+        }
+        
+        bool isHashTableDeletedValue() const
+        {
+            return registrableDomain.isHashTableDeletedValue();
+        }
+
+        static Destination deletedValue()
+        {
+            return Destination { WTF::HashTableDeletedValue };
+        }
+
+        static void constructDeletedValue(Destination& destination)
+        {
+            new (&destination) Destination;
+            destination = Destination::deletedValue();
+        }
+
+        void deleteValue()
+        {
+            registrableDomain = String { WTF::HashTableDeletedValue };
+        }
+
+        bool isDeletedValue() const
+        {
+            return isHashTableDeletedValue();
+        }
+
         String registrableDomain;
     };
 
+    struct DestinationHash {
+        static unsigned hash(const Destination& destination)
+        {
+            return destination.registrableDomain.hash();
+        }
+        
+        static bool equal(const Destination& a, const Destination& b)
+        {
+            return a == b;
+        }
+
+        static const bool safeToCompareToEmptyOrDeleted = false;
+    };
+
     struct Priority {
         explicit Priority(PriorityValue value)
         : value { value }
@@ -113,6 +226,7 @@ public:
         template<class Decoder> static Optional<Conversion> decode(Decoder&);
     };
 
+    AdClickAttribution() = default;
     AdClickAttribution(Campaign campaign, const Source& source, const Destination& destination)
         : m_campaign { campaign }
         , m_source { source }
@@ -124,8 +238,12 @@ public:
     WEBCORE_EXPORT void setConversion(Conversion&&);
     WEBCORE_EXPORT URL url() 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 String toString() const;
+
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static Optional<AdClickAttribution> decode(Decoder&);
 
@@ -210,3 +328,25 @@ Optional<AdClickAttribution::Conversion> AdClickAttribution::Conversion::decode(
 }
 
 } // namespace WebCore
+
+namespace WTF {
+template<typename T> struct DefaultHash;
+
+template<> struct DefaultHash<WebCore::AdClickAttribution::Source> {
+    typedef WebCore::AdClickAttribution::SourceHash Hash;
+};
+template<> struct HashTraits<WebCore::AdClickAttribution::Source> : GenericHashTraits<WebCore::AdClickAttribution::Source> {
+    static WebCore::AdClickAttribution::Source emptyValue() { return { }; }
+    static void constructDeletedValue(WebCore::AdClickAttribution::Source& slot) { WebCore::AdClickAttribution::Source::constructDeletedValue(slot); }
+    static bool isDeletedValue(const WebCore::AdClickAttribution::Source& slot) { return slot.isDeletedValue(); }
+};
+
+template<> struct DefaultHash<WebCore::AdClickAttribution::Destination> {
+    typedef WebCore::AdClickAttribution::DestinationHash Hash;
+};
+template<> struct HashTraits<WebCore::AdClickAttribution::Destination> : GenericHashTraits<WebCore::AdClickAttribution::Destination> {
+    static WebCore::AdClickAttribution::Destination emptyValue() { return { }; }
+    static void constructDeletedValue(WebCore::AdClickAttribution::Destination& slot) { WebCore::AdClickAttribution::Destination::constructDeletedValue(slot); }
+    static bool isDeletedValue(const WebCore::AdClickAttribution::Destination& slot) { return slot.isDeletedValue(); }
+};
+}
index 9497e98..4de9d40 100644 (file)
@@ -135,7 +135,7 @@ public:
     LockBackForwardList lockBackForwardList() const { return m_lockBackForwardList; }
     void setLockBackForwardList(LockBackForwardList lockBackForwardList) { m_lockBackForwardList = lockBackForwardList; }
 
-    const Optional<AdClickAttribution>& adClickAttribution() { return m_adClickAttribution; };
+    const Optional<AdClickAttribution>& adClickAttribution() const { return m_adClickAttribution; };
     void setAdClickAttribution(AdClickAttribution&& adClickAttribution) { m_adClickAttribution = adClickAttribution; };
 
 private:
index 3641b3f..380a851 100644 (file)
@@ -1,3 +1,81 @@
+2019-02-13  John Wilander  <wilander@apple.com>
+
+        Store Ad Click Attribution requests in the network process
+        https://bugs.webkit.org/show_bug.cgi?id=194510
+        <rdar://problem/47650118>
+
+        Reviewed by Alex Christensen and Daniel Bates.
+
+        With this patch, WebPageProxy::didCommitLoadForFrame() now looks for
+        an AdClickAttribution object in its navigation state. If there is an
+        attribution, it sends it to the network process where the 
+        WebKit::NetworkSession stores it in an object of a new class,
+        WebKit::NetworkAdClickAttribution.
+
+        This patch also covers test infrastructure to support two new
+        TestRunner functions:
+        - dumpAdClickAttribution()
+        - clearAdClickAttribution()
+
+        * NetworkProcess/Cookies/WebCookieManager.cpp:
+            Added missing header include.
+        * NetworkProcess/Downloads/DownloadManager.cpp:
+            Added missing header include.
+        * NetworkProcess/NetworkAdClickAttribution.cpp: Added.
+        (WebKit::NetworkAdClickAttribution::ensureDestinationMapForSource):
+        (WebKit::NetworkAdClickAttribution::store):
+        (WebKit::NetworkAdClickAttribution::clear):
+        (WebKit::NetworkAdClickAttribution::toString const):
+        * NetworkProcess/NetworkAdClickAttribution.h: Added.
+            Stores WebCore::AdClickAttribution objects in a map structure.
+        * NetworkProcess/NetworkLoad.cpp:
+            Added missing header includes.
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::storeAdClickAttribution):
+        (WebKit::NetworkProcess::dumpAdClickAttribution):
+        (WebKit::NetworkProcess::clearAdClickAttribution):
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcess.messages.in:
+        * NetworkProcess/NetworkSession.cpp:
+        (WebKit::NetworkSession::NetworkSession):
+        (WebKit::NetworkSession::storeAdClickAttribution):
+        (WebKit::NetworkSession::dumpAdClickAttribution):
+        (WebKit::NetworkSession::clearAdClickAttribution):
+        * NetworkProcess/NetworkSession.h:
+        * NetworkProcess/soup/RemoteNetworkingContextSoup.cpp:
+            Added missing header include.
+        * Sources.txt:
+            Added NetworkProcess/NetworkAdClickAttribution.cpp.
+        * UIProcess/API/APINavigation.h:
+        (API::Navigation::adClickAttribution const):
+        * UIProcess/API/C/WKPage.cpp:
+        (WKPageDumpAdClickAttribution):
+        (WKPageClearAdClickAttribution):
+            Test infrastructure.
+        * UIProcess/API/C/WKPagePrivate.h:
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::dumpAdClickAttribution):
+        (WebKit::NetworkProcessProxy::clearAdClickAttribution):
+            Test infrastructure.
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::didFinishDocumentLoadForFrame):
+            This is where pending Ad Click Attributions are forwarded to the
+            network process.
+        (WebKit::WebPageProxy::dumpAdClickAttribution):
+        (WebKit::WebPageProxy::clearAdClickAttribution):
+            Test infrastructure.
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::dumpAdClickAttribution):
+        (WebKit::WebProcessPool::clearAdClickAttribution):
+            Test infrastructure.
+        * UIProcess/WebProcessPool.h:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
+            Added missing data copying from navigationAction to navigationActionData.
+
 2019-02-13  Antti Koivisto  <antti@apple.com>
 
         Crash in WebKit::CacheStorage::Engine::cachesRootPath
index 825849a..1ec5da0 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "WebCookieManager.h"
 
+#include "NetworkProcess.h"
 #include "WebCookieManagerMessages.h"
 #include "WebCookieManagerProxyMessages.h"
 #include "WebCoreArgumentCoders.h"
index e3fe3d6..a50b0a7 100644 (file)
@@ -27,6 +27,7 @@
 #include "DownloadManager.h"
 
 #include "Download.h"
+#include "NetworkBlobRegistry.h"
 #include "NetworkConnectionToWebProcess.h"
 #include "NetworkLoad.h"
 #include "NetworkSession.h"
diff --git a/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp b/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.cpp
new file mode 100644 (file)
index 0000000..8f77e84
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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 AdClickAttribution = WebCore::AdClickAttribution;
+using Source = WebCore::AdClickAttribution::Source;
+using Destination = WebCore::AdClickAttribution::Destination;
+using DestinationMap = HashMap<Destination, AdClickAttribution>;
+
+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::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
diff --git a/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h b/Source/WebKit/NetworkProcess/NetworkAdClickAttribution.h
new file mode 100644 (file)
index 0000000..93dd333
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <WebCore/AdClickAttribution.h>
+#include <wtf/CompletionHandler.h>
+#include <wtf/HashMap.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebKit {
+
+class NetworkAdClickAttribution {
+public:
+
+    using AdClickAttribution = WebCore::AdClickAttribution;
+    using Source = WebCore::AdClickAttribution::Source;
+    using Destination = WebCore::AdClickAttribution::Destination;
+    using DestinationMap = HashMap<Destination, AdClickAttribution>;
+
+    void store(AdClickAttribution&&);
+    void clear(CompletionHandler<void()>&&);
+    void toString(CompletionHandler<void(String)>&&) const;
+
+private:
+    DestinationMap& ensureDestinationMapForSource(const AdClickAttribution::Source&);
+
+    HashMap<Source, DestinationMap> m_adClickAttributionMap;
+};
+    
+} // namespace WebKit
index 5b2e948..3a6fa10 100644 (file)
 #include "NetworkLoad.h"
 
 #include "AuthenticationChallengeDisposition.h"
+#include "AuthenticationManager.h"
+#include "NetworkDataTaskBlob.h"
 #include "NetworkProcess.h"
 #include "NetworkSession.h"
+#include "WebErrors.h"
 #include <WebCore/ResourceRequest.h>
 #include <WebCore/SharedBuffer.h>
 #include <wtf/Seconds.h>
index 12dd686..3cef6a5 100644 (file)
@@ -2288,4 +2288,26 @@ void NetworkProcess::platformSyncAllCookies(CompletionHandler<void()>&& completi
 
 #endif
 
+void NetworkProcess::storeAdClickAttribution(PAL::SessionID sessionID, WebCore::AdClickAttribution&& adClickAttribution)
+{
+    if (auto session = networkSession(sessionID))
+        session->storeAdClickAttribution(WTFMove(adClickAttribution));
+}
+
+void NetworkProcess::dumpAdClickAttribution(PAL::SessionID sessionID, CompletionHandler<void(String)>&& completionHandler)
+{
+    if (auto session = networkSession(sessionID))
+        return session->dumpAdClickAttribution(WTFMove(completionHandler));
+
+    completionHandler({ });
+}
+
+void NetworkProcess::clearAdClickAttribution(PAL::SessionID sessionID, CompletionHandler<void()>&& completionHandler)
+{
+    if (auto session = networkSession(sessionID))
+        return session->clearAdClickAttribution(WTFMove(completionHandler));
+    
+    completionHandler();
+}
+
 } // namespace WebKit
index 28351b2..4ae4c66 100644 (file)
@@ -32,6 +32,7 @@
 #include "NetworkContentRuleListManager.h"
 #include "NetworkHTTPSUpgradeChecker.h"
 #include "SandboxExtension.h"
+#include <WebCore/AdClickAttribution.h>
 #include <WebCore/DiagnosticLoggingClient.h>
 #include <WebCore/FetchIdentifier.h>
 #include <WebCore/IDBKeyData.h>
@@ -294,6 +295,10 @@ public:
 
     NetworkBlobRegistry& networkBlobRegistry() override { return m_networkBlobRegistry; }
 
+    void storeAdClickAttribution(PAL::SessionID, WebCore::AdClickAttribution&&);
+    void dumpAdClickAttribution(PAL::SessionID, CompletionHandler<void(String)>&&);
+    void clearAdClickAttribution(PAL::SessionID, CompletionHandler<void()>&&);
+
 private:
     void platformInitializeNetworkProcess(const NetworkProcessCreationParameters&);
     std::unique_ptr<WebCore::NetworkStorageSession> platformCreateDefaultStorageSession() const;
index 283b599..fc65a27 100644 (file)
@@ -163,4 +163,8 @@ messages -> NetworkProcess LegacyReceiver {
 #if ENABLE(INDEXED_DATABASE)
     SetIDBPerOriginQuota(uint64_t quota)
 #endif
+
+    StoreAdClickAttribution(PAL::SessionID sessionID, WebCore::AdClickAttribution adClickAttribution)
+    DumpAdClickAttribution(PAL::SessionID sessionID) -> (String adClickAttributionState) Async
+    ClearAdClickAttribution(PAL::SessionID sessionID) -> () Async
 }
index 20d09ba..26bd6f0 100644 (file)
 #include "config.h"
 #include "NetworkSession.h"
 
+#include "NetworkAdClickAttribution.h"
 #include "NetworkProcess.h"
 #include "NetworkProcessProxyMessages.h"
 #include "WebProcessProxy.h"
 #include "WebResourceLoadStatisticsStore.h"
+#include <WebCore/AdClickAttribution.h>
 #include <WebCore/NetworkStorageSession.h>
 
 #if PLATFORM(COCOA)
@@ -68,6 +70,7 @@ NetworkStorageSession& NetworkSession::networkStorageSession() const
 NetworkSession::NetworkSession(NetworkProcess& networkProcess, PAL::SessionID sessionID)
     : m_sessionID(sessionID)
     , m_networkProcess(networkProcess)
+    , m_adClickAttribution(makeUniqueRef<NetworkAdClickAttribution>())
 {
 }
 
@@ -125,4 +128,19 @@ void NetworkSession::topPrivatelyControlledDomainsWithWebsiteData(OptionSet<Webs
 }
 #endif
 
+void NetworkSession::storeAdClickAttribution(WebCore::AdClickAttribution&& adClickAttribution)
+{
+    m_adClickAttribution->store(WTFMove(adClickAttribution));
+}
+
+void NetworkSession::dumpAdClickAttribution(CompletionHandler<void(String)>&& completionHandler)
+{
+    m_adClickAttribution->toString(WTFMove(completionHandler));
+}
+
+void NetworkSession::clearAdClickAttribution(CompletionHandler<void()>&& completionHandler)
+{
+    m_adClickAttribution->clear(WTFMove(completionHandler));
+}
+
 } // namespace WebKit
index e84107a..822f043 100644 (file)
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
 #include <wtf/Seconds.h>
+#include <wtf/UniqueRef.h>
 #include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
+class AdClickAttribution;
 class NetworkStorageSession;
 enum class ShouldSample : bool;
 }
 
 namespace WebKit {
 
+class NetworkAdClickAttribution;
 class NetworkDataTask;
 class NetworkProcess;
 class WebResourceLoadStatisticsStore;
@@ -73,6 +76,9 @@ public:
     void logDiagnosticMessageWithValue(const String& message, const String& description, unsigned value, unsigned significantFigures, WebCore::ShouldSample);
     void notifyPageStatisticsTelemetryFinished(unsigned totalPrevalentResources, unsigned totalPrevalentResourcesWithUserInteraction, unsigned top3SubframeUnderTopFrameOrigins);
 #endif
+    void storeAdClickAttribution(WebCore::AdClickAttribution&&);
+    void dumpAdClickAttribution(CompletionHandler<void(String)>&&);
+    void clearAdClickAttribution(CompletionHandler<void()>&&);
 
 protected:
     NetworkSession(NetworkProcess&, PAL::SessionID);
@@ -84,6 +90,7 @@ protected:
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
     RefPtr<WebResourceLoadStatisticsStore> m_resourceLoadStatistics;
 #endif
+    UniqueRef<NetworkAdClickAttribution> m_adClickAttribution;
 };
 
 } // namespace WebKit
index 0f6c9d1..c9eea5a 100644 (file)
@@ -28,6 +28,7 @@
 #include "config.h"
 #include "RemoteNetworkingContext.h"
 
+#include "NetworkProcess.h"
 #include "NetworkSession.h"
 #include "WebsiteDataStoreParameters.h"
 #include <WebCore/NetworkStorageSession.h>
index 3b6066e..6673f3d 100644 (file)
@@ -21,6 +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/NetworkActivityTracker.cpp
 NetworkProcess/NetworkCORSPreflightChecker.cpp
 NetworkProcess/NetworkConnectionToWebProcess.cpp
index 9f4f1f8..c996c36 100644 (file)
 #include "FrameInfoData.h"
 #include "NavigationActionData.h"
 #include "WebBackForwardListItem.h"
+#include <WebCore/AdClickAttribution.h>
 #include <WebCore/ProcessIdentifier.h>
 #include <WebCore/ResourceRequest.h>
 #include <WebCore/SecurityOriginData.h>
+#include <wtf/Optional.h>
 #include <wtf/Ref.h>
 
 namespace WebCore {
@@ -142,6 +144,8 @@ public:
 
     const std::unique_ptr<SubstituteData>& substituteData() const { return m_substituteData; }
 
+    const Optional<WebCore::AdClickAttribution>& adClickAttribution() const { return m_lastNavigationAction.adClickAttribution; }
+
 private:
     explicit Navigation(WebKit::WebNavigationState&);
     Navigation(WebKit::WebNavigationState&, WebCore::ResourceRequest&&, WebKit::WebBackForwardListItem* fromItem);
index f05bf0a..563a438 100644 (file)
@@ -2711,3 +2711,16 @@ void WKPageGetApplicationManifest_b(WKPageRef page, WKPageGetApplicationManifest
 }
 #endif
 
+void WKPageDumpAdClickAttribution(WKPageRef page, WKPageDumpAdClickAttributionFunction callback, void* callbackContext)
+{
+    toImpl(page)->dumpAdClickAttribution([callbackContext, callback] (const String& adClickAttribution) {
+        callback(WebKit::toAPI(adClickAttribution.impl()), callbackContext);
+    });
+}
+
+void WKPageClearAdClickAttribution(WKPageRef page, WKPageClearAdClickAttributionFunction callback, void* callbackContext)
+{
+    toImpl(page)->clearAdClickAttribution([callbackContext, callback] () {
+        callback(callbackContext);
+    });
+}
index 2d898b8..abdd361 100644 (file)
@@ -182,6 +182,11 @@ typedef void (^WKPageGetApplicationManifestBlock)(void);
 WK_EXPORT void WKPageGetApplicationManifest_b(WKPageRef page, WKPageGetApplicationManifestBlock block);
 #endif
 
+typedef void (*WKPageDumpAdClickAttributionFunction)(WKStringRef adClickAttributionRepresentation, void* functionContext);
+WK_EXPORT void WKPageDumpAdClickAttribution(WKPageRef, WKPageDumpAdClickAttributionFunction, void* callbackContext);
+typedef void (*WKPageClearAdClickAttributionFunction)(void* functionContext);
+WK_EXPORT void WKPageClearAdClickAttribution(WKPageRef, WKPageClearAdClickAttributionFunction, void* callbackContext);
+
 #ifdef __cplusplus
 }
 #endif
index d2a9289..7ea9e4e 100644 (file)
@@ -1067,7 +1067,27 @@ void NetworkProcessProxy::didDestroyWebUserContentControllerProxy(WebUserContent
     m_webUserContentControllerProxies.remove(&proxy);
 }
 #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 5fc62f0..b07e6db 100644 (file)
@@ -147,6 +147,9 @@ 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 fd4cde0..da13f2b 100644 (file)
 #include "WebURLSchemeHandler.h"
 #include "WebUserContentControllerProxy.h"
 #include "WebsiteDataStore.h"
+#include <WebCore/AdClickAttribution.h>
 #include <WebCore/BitmapImage.h>
 #include <WebCore/DeprecatedGlobalSettings.h>
 #include <WebCore/DiagnosticLoggingClient.h>
@@ -4117,8 +4118,13 @@ void WebPageProxy::didFinishDocumentLoadForFrame(uint64_t frameID, uint64_t navi
 
     // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
     RefPtr<API::Navigation> navigation;
-    if (frame->isMainFrame() && navigationID)
+    if (frame->isMainFrame() && navigationID) {
         navigation = navigationState().navigation(navigationID);
+        if (auto& adClickAttribution = navigation->adClickAttribution()) {
+            if (adClickAttribution->destination().matches(frame->url()))
+                m_process->processPool().sendToNetworkingProcess(Messages::NetworkProcess::StoreAdClickAttribution(m_websiteDataStore->sessionID(), *adClickAttribution));
+        }
+    }
 
     if (frame->isMainFrame())
         m_navigationClient->didFinishDocumentLoad(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get());
@@ -8660,6 +8666,16 @@ void WebPageProxy::removeDataDetectedLinks(CompletionHandler<void(const DataDete
 
 #endif
 
+void WebPageProxy::dumpAdClickAttribution(CompletionHandler<void(const String&)>&& completionHandler)
+{
+    m_process->processPool().dumpAdClickAttribution(m_websiteDataStore->sessionID(), WTFMove(completionHandler));
+}
+
+void WebPageProxy::clearAdClickAttribution(CompletionHandler<void()>&& completionHandler)
+{
+    m_process->processPool().clearAdClickAttribution(m_websiteDataStore->sessionID(), WTFMove(completionHandler));
+}
+
 } // namespace WebKit
 
 #undef MERGE_WHEEL_EVENTS
index 139dd16..b4d8af3 100644 (file)
@@ -1455,6 +1455,9 @@ public:
     void loadRequestWithNavigationShared(Ref<WebProcessProxy>&&, API::Navigation&, WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, API::Object* userData, WebCore::ShouldTreatAsContinuingLoad, Optional<WebsitePoliciesData>&& = WTF::nullopt);
     void backForwardGoToItemShared(Ref<WebProcessProxy>&&, const WebCore::BackForwardItemIdentifier&, SandboxExtension::Handle&);
 
+    void dumpAdClickAttribution(CompletionHandler<void(const String&)>&&);
+    void clearAdClickAttribution(CompletionHandler<void()>&&);
+
     // IPC::MessageReceiver
     // Implemented in generated WebPageProxyMessageReceiver.cpp
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
index b1e1fe7..c9de7fb 100644 (file)
@@ -2384,4 +2384,24 @@ 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));
+}
+
 } // namespace WebKit
index a86edd8..e574e1d 100644 (file)
@@ -479,6 +479,9 @@ public:
     void sendDisplayConfigurationChangedMessageForTesting();
     void clearCurrentModifierStateForTesting();
 
+    void dumpAdClickAttribution(PAL::SessionID, CompletionHandler<void(const String&)>&&);
+    void clearAdClickAttribution(PAL::SessionID, CompletionHandler<void()>&&);
+
 #if PLATFORM(GTK) || PLATFORM(WPE)
     void setSandboxEnabled(bool enabled) { m_sandboxEnabled = enabled; };
     void addSandboxPath(const CString& path, SandboxPermission permission) { m_extraSandboxPaths.add(path, permission); };
index e9ee2e4..821a06a 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 */; };
                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>"; };
                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>"; };
                                413075971DE84ED70039EC69 /* webrtc */,
                                53F3CAA5206C443E0086490E /* NetworkActivityTracker.cpp */,
                                535BCB902069C49C00CCCE02 /* NetworkActivityTracker.h */,
+                               6BD05864220CE8C2000BED5C /* NetworkAdClickAttribution.cpp */,
+                               6BD05863220CE8C2000BED5C /* NetworkAdClickAttribution.h */,
                                513A16491630A9BF005D7D22 /* NetworkConnectionToWebProcess.cpp */,
                                513A164A1630A9BF005D7D22 /* NetworkConnectionToWebProcess.h */,
                                513A164B1630A9BF005D7D22 /* NetworkConnectionToWebProcess.messages.in */,
                A1E6886E1F6E2B82007006A6 /* mac */ = {
                        isa = PBXGroup;
                        children = (
+                               29D04E2821F7C73D0076741D /* AccessibilityPrivSPI.h */,
                                F48D2A8421583A0200C6752B /* AppKitSPI.h */,
                                A1E6886F1F6E2BAB007006A6 /* QuarantineSPI.h */,
-                               29D04E2821F7C73D0076741D /* AccessibilityPrivSPI.h */,
                        );
                        path = mac;
                        sourceTree = "<group>";
                                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 */,
                                2D913444212CF9F000128AFD /* NetscapePlugin.cpp in Sources */,
                                2D92A796212B6ADA00F493FD /* NetscapePluginModule.cpp in Sources */,
                                2D913445212CF9F000128AFD /* NetscapePluginStream.cpp in Sources */,
+                               6BD05866220CE8C2000BED5C /* NetworkAdClickAttribution.cpp in Sources */,
                                51DD9F2816367DA2001578E9 /* NetworkConnectionToWebProcessMessageReceiver.cpp in Sources */,
                                52F060E11654318500F3281B /* NetworkContentRuleListManagerMessageReceiver.cpp in Sources */,
                                51F060E11654318500F3283F /* NetworkMDNSRegisterMessageReceiver.cpp in Sources */,
index 9a02318..c5a1855 100644 (file)
@@ -882,6 +882,7 @@ void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const Navigat
     navigationActionData.targetBackForwardItemIdentifier = navigationAction.targetBackForwardItemIdentifier();
     navigationActionData.lockHistory = navigationAction.lockHistory();
     navigationActionData.lockBackForwardList = navigationAction.lockBackForwardList();
+    navigationActionData.adClickAttribution = navigationAction.adClickAttribution();
 
     WebCore::Frame* coreFrame = m_frame->coreFrame();
     if (!coreFrame)
index 0dbbdc9..ab48431 100644 (file)
@@ -1,3 +1,38 @@
+2019-02-13  John Wilander  <wilander@apple.com>
+
+        Store Ad Click Attribution requests in the network process
+        https://bugs.webkit.org/show_bug.cgi?id=194510
+        <rdar://problem/47650118>
+
+        Reviewed by Alex Christensen and Daniel Bates.
+
+        This patch adds two TestRunner functions:
+        - dumpAdClickAttribution()
+        - clearAdClickAttribution()
+
+        They call into the network process to dump and clear Ad Click
+        Attribution state, respectively.
+
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::dumpAdClickAttribution):
+        (WTR::TestRunner::clearAdClickAttribution):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::resetStateToConsistentValues):
+        (WTR::AdClickAttributionStringResultCallbackContext::AdClickAttributionStringResultCallbackContext):
+        (WTR::adClickAttributionStringResultCallback):
+        (WTR::TestController::dumpAdClickAttribution):
+        (WTR::AdClickAttributionVoidCallbackContext::AdClickAttributionVoidCallbackContext):
+        (WTR::adClickAttributionVoidCallback):
+        (WTR::TestController::clearAdClickAttribution):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::dumpResults):
+        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+        (WTR::TestInvocation::dumpAdClickAttribution):
+        * WebKitTestRunner/TestInvocation.h:
+
 2019-02-13  Aakash Jain  <aakash_jain@apple.com>
 
         [ews-app] Generate status-bubble
index 47cd9fc..0e79d80 100644 (file)
@@ -57,6 +57,7 @@ interface TestRunner {
     void dumpDOMAsWebArchive();
     void dumpPolicyDelegateCallbacks();
     void dumpResourceLoadStatistics();
+    void dumpAdClickAttribution();
 
     void clearDOMCaches();
     void clearDOMCache(DOMString origin);
@@ -370,4 +371,7 @@ interface TestRunner {
     void addTestKeyToKeychain(DOMString privateKeyBase64, DOMString attrLabel, DOMString applicationTagBase64);
     void cleanUpKeychain(DOMString attrLabel);
     boolean keyExistsInKeychain(DOMString attrLabel, DOMString applicationTagBase64);
+
+    // Ad Click Attribution
+    void clearAdClickAttribution();
 };
index df450b9..d462246 100644 (file)
@@ -2726,4 +2726,16 @@ void TestRunner::setShouldDismissJavaScriptAlertsAsynchronously(bool shouldDismi
     WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
 }
 
+void TestRunner::dumpAdClickAttribution()
+{
+    auto messageName = adoptWK(WKStringCreateWithUTF8CString("dumpAdClickAttribution"));
+    WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), nullptr, nullptr);
+}
+
+void TestRunner::clearAdClickAttribution()
+{
+    auto messageName = adoptWK(WKStringCreateWithUTF8CString("clearAdClickAttribution"));
+    WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), nullptr, nullptr);
+}
+
 } // namespace WTR
index 01fa7b8..713a943 100644 (file)
@@ -487,6 +487,10 @@ public:
     bool canDoServerTrustEvaluationInNetworkProcess();
     unsigned long serverTrustEvaluationCallbackCallsCount();
 
+    // Ad Click Attribution.
+    void dumpAdClickAttribution();
+    void clearAdClickAttribution();
+
 private:
     TestRunner();
 
index 07aee9f..42b70f6 100644 (file)
@@ -969,6 +969,8 @@ bool TestController::resetStateToConsistentValues(const TestOptions& options, Re
     m_openPanelFileURLs = nullptr;
     
     statisticsResetToConsistentState();
+    
+    clearAdClickAttribution();
 
     m_didReceiveServerRedirectForProvisionalNavigation = false;
     m_serverTrustEvaluationCallbackCallsCount = 0;
@@ -3401,4 +3403,55 @@ void TestController::setWebAuthenticationMockConfiguration(WKDictionaryRef confi
     WKWebsiteDataStoreSetWebAuthenticationMockConfiguration(WKContextGetWebsiteDataStore(platformContext()), configuration);
 }
 
+struct AdClickAttributionStringResultCallbackContext {
+    explicit AdClickAttributionStringResultCallbackContext(TestController& controller)
+        : testController(controller)
+    {
+    }
+    
+    TestController& testController;
+    bool done { false };
+    WKRetainPtr<WKStringRef> adClickAttributionRepresentation;
+};
+
+static void adClickAttributionStringResultCallback(WKStringRef adClickAttributionRepresentation, void* userData)
+{
+    auto* context = static_cast<AdClickAttributionStringResultCallbackContext*>(userData);
+    context->adClickAttributionRepresentation = adClickAttributionRepresentation;
+    context->done = true;
+    context->testController.notifyDone();
+}
+
+String TestController::dumpAdClickAttribution()
+{
+    AdClickAttributionStringResultCallbackContext callbackContext(*this);
+    WKPageDumpAdClickAttribution(m_mainWebView->page(), adClickAttributionStringResultCallback, &callbackContext);
+    runUntil(callbackContext.done, noTimeout);
+    return toWTFString(callbackContext.adClickAttributionRepresentation.get());
+}
+
+struct AdClickAttributionVoidCallbackContext {
+    explicit AdClickAttributionVoidCallbackContext(TestController& controller)
+        : testController(controller)
+    {
+    }
+    
+    TestController& testController;
+    bool done { false };
+};
+
+static void adClickAttributionVoidCallback(void* userData)
+{
+    auto* context = static_cast<AdClickAttributionVoidCallbackContext*>(userData);
+    context->done = true;
+    context->testController.notifyDone();
+}
+
+void TestController::clearAdClickAttribution()
+{
+    AdClickAttributionVoidCallbackContext callbackContext(*this);
+    WKPageClearAdClickAttribution(m_mainWebView->page(), adClickAttributionVoidCallback, &callbackContext);
+    runUntil(callbackContext.done, noTimeout);
+}
+
 } // namespace WTR
index 2b9577b..b43c55c 100644 (file)
@@ -294,6 +294,9 @@ public:
 
     bool isDoingMediaCapture() const;
 
+    String dumpAdClickAttribution();
+    void clearAdClickAttribution();
+
 private:
     WKRetainPtr<WKPageConfigurationRef> generatePageConfiguration(WKContextConfigurationRef);
     WKRetainPtr<WKContextConfigurationRef> generateContextConfiguration(const TestOptions&) const;
index 7af33b2..6660cdc 100644 (file)
@@ -259,6 +259,9 @@ void TestInvocation::dumpResults()
     if (m_shouldDumpResourceLoadStatistics)
         m_textOutput.append(m_savedResourceLoadStatistics.isNull() ? TestController::singleton().dumpResourceLoadStatistics() : m_savedResourceLoadStatistics);
 
+    if (m_shouldDumpAdClickAttribution)
+        m_textOutput.append(TestController::singleton().dumpAdClickAttribution());
+    
     if (m_textOutput.length() || !m_audioResult)
         dump(m_textOutput.toString().utf8().data());
     else
@@ -1550,6 +1553,16 @@ WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
         return nullptr;
     }
 
+    if (WKStringIsEqualToUTF8CString(messageName, "dumpAdClickAttribution")) {
+        dumpAdClickAttribution();
+        return nullptr;
+    }
+    
+    if (WKStringIsEqualToUTF8CString(messageName, "clearAdClickAttribution")) {
+        TestController::singleton().clearAdClickAttribution();
+        return nullptr;
+    }
+    
     ASSERT_NOT_REACHED();
     return nullptr;
 }
@@ -1700,4 +1713,9 @@ void TestInvocation::dumpResourceLoadStatistics()
     m_shouldDumpResourceLoadStatistics = true;
 }
 
+void TestInvocation::dumpAdClickAttribution()
+{
+    m_shouldDumpAdClickAttribution = true;
+}
+
 } // namespace WTR
index fe17897..2bdbaaa 100644 (file)
@@ -86,7 +86,9 @@ public:
     void dumpResourceLoadStatistics();
 
     bool canOpenWindows() const { return m_canOpenWindows; }
-    
+
+    void dumpAdClickAttribution();
+
 private:
     WKRetainPtr<WKMutableDictionaryRef> createTestSettingsDictionary();
 
@@ -135,6 +137,7 @@ private:
     bool m_pixelResultIsPending { false };
     bool m_shouldDumpResourceLoadStatistics { false };
     bool m_canOpenWindows { false };
+    bool m_shouldDumpAdClickAttribution { false };
     WhatToDump m_whatToDump { WhatToDump::RenderTree };
 
     StringBuilder m_textOutput;