Add logging of Ad Click Attribution errors and events to a dedicated channel
authorwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 1 May 2019 02:28:07 +0000 (02:28 +0000)
committerwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 1 May 2019 02:28:07 +0000 (02:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197332
<rdar://problem/49918800>

Reviewed by Youenn Fablet.

Source/WebCore:

This patch adds an experimental Ad Click Attribution debug mode which
logs information.

No new tests.

* loader/AdClickAttribution.cpp:
(WebCore::AdClickAttribution::parseConversionRequest):
(WebCore::AdClickAttribution::debugModeEnabled):
* loader/AdClickAttribution.h:
* page/RuntimeEnabledFeatures.h:
(WebCore::RuntimeEnabledFeatures::adClickAttributionDebugModeEnabled const):
(WebCore::RuntimeEnabledFeatures::setAdClickAttributionDebugModeEnabled):
* platform/Logging.h:

Source/WebKit:

This patch adds an experimental Ad Click Attribution debug mode which
logs information. Most changes are just log output in the various
functions in WebKit::AdClickAttributionManager.

The constructor to WebKit::AdClickAttributionManager now takes a
PAL::SessionID so that the log functions can make sure they don't
output anything in ephemeral sessions.

WebProcessPool::platformInitializeNetworkProcess() now picks up the
debug mode setting from the incoming
WebKit::NetworkProcessCreationParameters object.

NetworkResourceLoader::handleAdClickAttributionConversion() was
moved to AdClickAttributionManager::handleConversion() to keep all
the logging in one file.

* NetworkProcess/AdClickAttributionManager.cpp:
(WebKit::AdClickAttributionManager::storeUnconverted):
(WebKit::AdClickAttributionManager::handleConversion):
(WebKit::AdClickAttributionManager::convert):
(WebKit::AdClickAttributionManager::fireConversionRequest):
(WebKit::AdClickAttributionManager::firePendingConversionRequests):
(WebKit::AdClickAttributionManager::clearExpired):
(WebKit::AdClickAttributionManager::debugModeEnabled const):
* NetworkProcess/AdClickAttributionManager.h:
(WebKit::AdClickAttributionManager::AdClickAttributionManager):
(WebKit::AdClickAttributionManager::m_sessionID):
* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::initializeNetworkProcess):
* NetworkProcess/NetworkProcessCreationParameters.cpp:
(WebKit::NetworkProcessCreationParameters::encode const):
(WebKit::NetworkProcessCreationParameters::decode):
* NetworkProcess/NetworkProcessCreationParameters.h:
* NetworkProcess/NetworkResourceLoader.cpp:
(WebKit::NetworkResourceLoader::continueWillSendRedirectedRequest):
(WebKit::NetworkResourceLoader::handleAdClickAttributionConversion): Deleted.
* NetworkProcess/NetworkSession.cpp:
(WebKit::NetworkSession::NetworkSession):
(WebKit::NetworkSession::handleAdClickAttributionConversion):
(WebKit::NetworkSession::convertAdClickAttribution): Deleted.
* NetworkProcess/NetworkSession.h:
* Platform/Logging.h:
* Shared/WebPreferences.yaml:
* UIProcess/Cocoa/WebProcessPoolCocoa.mm:
(WebKit::WebProcessPool::platformInitializeNetworkProcess):

Source/WTF:

Added missing RELEASE_LOG_INFO and RELEASE_LOG_INFO_IF dummies
for RELEASE_LOG_DISABLED.

* wtf/Assertions.h:

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

19 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/Assertions.h
Source/WebCore/ChangeLog
Source/WebCore/loader/AdClickAttribution.cpp
Source/WebCore/loader/AdClickAttribution.h
Source/WebCore/page/RuntimeEnabledFeatures.h
Source/WebCore/platform/Logging.h
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/AdClickAttributionManager.cpp
Source/WebKit/NetworkProcess/AdClickAttributionManager.h
Source/WebKit/NetworkProcess/NetworkProcess.cpp
Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.cpp
Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.h
Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp
Source/WebKit/NetworkProcess/NetworkSession.cpp
Source/WebKit/NetworkProcess/NetworkSession.h
Source/WebKit/Platform/Logging.h
Source/WebKit/Shared/WebPreferences.yaml
Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm

index a5b9334..cae1192 100644 (file)
@@ -1,3 +1,16 @@
+2019-04-30  John Wilander  <wilander@apple.com>
+
+        Add logging of Ad Click Attribution errors and events to a dedicated channel
+        https://bugs.webkit.org/show_bug.cgi?id=197332
+        <rdar://problem/49918800>
+
+        Reviewed by Youenn Fablet.
+
+        Added missing RELEASE_LOG_INFO and RELEASE_LOG_INFO_IF dummies
+        for RELEASE_LOG_DISABLED.
+
+        * wtf/Assertions.h:
+
 2019-04-30  Youenn Fablet  <youenn@apple.com>
 
         Make Document audio producers use WeakPtr
index ac1a077..dd64976 100644 (file)
@@ -490,9 +490,11 @@ WTF_EXPORT_PRIVATE NO_RETURN_DUE_TO_CRASH void WTFCrashWithSecurityImplication(v
 #define RELEASE_LOG(channel, ...) ((void)0)
 #define RELEASE_LOG_ERROR(channel, ...) LOG_ERROR(__VA_ARGS__)
 #define RELEASE_LOG_FAULT(channel, ...) LOG_ERROR(__VA_ARGS__)
+#define RELEASE_LOG_INFO(channel, ...) ((void)0)
 
 #define RELEASE_LOG_IF(isAllowed, channel, ...) ((void)0)
 #define RELEASE_LOG_ERROR_IF(isAllowed, channel, ...) do { if (isAllowed) RELEASE_LOG_ERROR(channel, __VA_ARGS__); } while (0)
+#define RELEASE_LOG_INFO_IF(isAllowed, channel, ...) ((void)0)
 
 #define RELEASE_LOG_WITH_LEVEL(channel, level, ...) ((void)0)
 #define RELEASE_LOG_WITH_LEVEL_IF(isAllowed, channel, level, ...) do { if (isAllowed) RELEASE_LOG_WITH_LEVEL(channel, level, __VA_ARGS__); } while (0)
index 839be1b..a00aff9 100644 (file)
@@ -1,3 +1,25 @@
+2019-04-30  John Wilander  <wilander@apple.com>
+
+        Add logging of Ad Click Attribution errors and events to a dedicated channel
+        https://bugs.webkit.org/show_bug.cgi?id=197332
+        <rdar://problem/49918800>
+
+        Reviewed by Youenn Fablet.
+
+        This patch adds an experimental Ad Click Attribution debug mode which
+        logs information.
+
+        No new tests.
+
+        * loader/AdClickAttribution.cpp:
+        (WebCore::AdClickAttribution::parseConversionRequest):
+        (WebCore::AdClickAttribution::debugModeEnabled):
+        * loader/AdClickAttribution.h:
+        * page/RuntimeEnabledFeatures.h:
+        (WebCore::RuntimeEnabledFeatures::adClickAttributionDebugModeEnabled const):
+        (WebCore::RuntimeEnabledFeatures::setAdClickAttributionDebugModeEnabled):
+        * platform/Logging.h:
+
 2019-04-30  Myles C. Maxfield  <mmaxfield@apple.com>
 
         font-weight: 1000 is not parsed successfully
index 5810094..37d6ad3 100644 (file)
@@ -26,6 +26,8 @@
 #include "config.h"
 #include "AdClickAttribution.h"
 
+#include "Logging.h"
+#include "RuntimeEnabledFeatures.h"
 #include <wtf/RandomNumber.h>
 #include <wtf/URL.h>
 #include <wtf/text/StringBuilder.h>
@@ -50,40 +52,51 @@ bool AdClickAttribution::isValid() const
 
 Optional<AdClickAttribution::Conversion> AdClickAttribution::parseConversionRequest(const URL& redirectURL)
 {
-    if (!redirectURL.protocolIs("https"_s) || redirectURL.hasUsername() || redirectURL.hasPassword() || redirectURL.hasQuery() || redirectURL.hasFragment())
+    if (!redirectURL.protocolIs("https"_s) || redirectURL.hasUsername() || redirectURL.hasPassword() || redirectURL.hasQuery() || redirectURL.hasFragment()) {
+        RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Conversion was not accepted because the URL's protocol is not HTTPS or the URL contains one or more of username, password, query string, and fragment.");
         return { };
+    }
 
     auto path = StringView(redirectURL.string()).substring(redirectURL.pathStart(), redirectURL.pathEnd() - redirectURL.pathStart());
-    if (path.isEmpty() || !path.startsWith(adClickAttributionPathPrefix))
+    if (path.isEmpty() || !path.startsWith(adClickAttributionPathPrefix)) {
+        RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Conversion was not accepted because the URL path did not start with %{public}s.", adClickAttributionPathPrefix);
         return { };
+    }
 
     auto prefixLength = sizeof(adClickAttributionPathPrefix) - 1;
     if (path.length() == prefixLength + adClickConversionDataPathSegmentSize) {
         auto conversionDataUInt64 = path.substring(prefixLength, adClickConversionDataPathSegmentSize).toUInt64Strict();
-        if (!conversionDataUInt64 || *conversionDataUInt64 > MaxEntropy)
+        if (!conversionDataUInt64 || *conversionDataUInt64 > MaxEntropy) {
+            RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Conversion was not accepted because the conversion data could not be parsed or was higher than the allowed maximum of %{public}u.", MaxEntropy);
             return { };
+        }
 
         return Conversion { static_cast<uint32_t>(*conversionDataUInt64), Priority { 0 } };
     }
     
     if (path.length() == prefixLength + adClickConversionDataPathSegmentSize + 1 + adClickPriorityPathSegmentSize) {
         auto conversionDataUInt64 = path.substring(prefixLength, adClickConversionDataPathSegmentSize).toUInt64Strict();
-        if (!conversionDataUInt64 || *conversionDataUInt64 > MaxEntropy)
+        if (!conversionDataUInt64 || *conversionDataUInt64 > MaxEntropy) {
+            RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Conversion was not accepted because the conversion data could not be parsed or was higher than the allowed maximum of %{public}u.", MaxEntropy);
             return { };
+        }
 
         auto conversionPriorityUInt64 = path.substring(prefixLength + adClickConversionDataPathSegmentSize + 1, adClickPriorityPathSegmentSize).toUInt64Strict();
-        if (!conversionPriorityUInt64 || *conversionPriorityUInt64 > MaxEntropy)
+        if (!conversionPriorityUInt64 || *conversionPriorityUInt64 > MaxEntropy) {
+            RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Conversion was not accepted because the priority could not be parsed or was higher than the allowed maximum of %{public}u.", MaxEntropy);
             return { };
+        }
 
         return Conversion { static_cast<uint32_t>(*conversionDataUInt64), Priority { static_cast<uint32_t>(*conversionPriorityUInt64) } };
     }
 
+    RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Conversion was not accepted because the URL path contained unrecognized parts.");
     return { };
 }
 
 Optional<Seconds> AdClickAttribution::convertAndGetEarliestTimeToSend(Conversion&& conversion)
 {
-    if (!conversion.isValid() || (m_conversion && m_conversion->priority > conversion.priority))
+    if (!conversion.isValid() || (m_conversion && m_conversion->priority >= conversion.priority))
         return { };
 
     m_conversion = WTFMove(conversion);
@@ -212,4 +225,9 @@ String AdClickAttribution::toString() const
     return builder.toString();
 }
 
+bool AdClickAttribution::debugModeEnabled()
+{
+    return RuntimeEnabledFeatures::sharedFeatures().adClickAttributionDebugModeEnabled();
+}
+
 }
index 3e1ac74..38b947d 100644 (file)
@@ -262,6 +262,7 @@ public:
 
 private:
     bool isValid() const;
+    static bool debugModeEnabled();
 
     Campaign m_campaign;
     Source m_source;
index c61c8fd..86545a5 100644 (file)
@@ -341,6 +341,8 @@ public:
 
     bool adClickAttributionEnabled() const { return m_adClickAttributionEnabled; }
     void setAdClickAttributionEnabled(bool isEnabled) { m_adClickAttributionEnabled = isEnabled; }
+    bool adClickAttributionDebugModeEnabled() const { return m_adClickAttributionDebugModeEnabled; }
+    void setAdClickAttributionDebugModeEnabled(bool isEnabled) { m_adClickAttributionDebugModeEnabled = isEnabled; }
 
 #if ENABLE(TOUCH_EVENTS)
     bool mouseEventsSimulationEnabled() const { return m_mouseEventsSimulationEnabled; }
@@ -525,6 +527,7 @@ private:
     bool m_CSSLogicalEnabled { false };
 
     bool m_adClickAttributionEnabled { false };
+    bool m_adClickAttributionDebugModeEnabled { false };
 
 #if ENABLE(TOUCH_EVENTS)
     bool m_mouseEventsSimulationEnabled { false };
index abf079d..0171229 100644 (file)
@@ -38,6 +38,7 @@ namespace WebCore {
 #endif
 
 #define WEBCORE_LOG_CHANNELS(M) \
+    M(AdClickAttribution) \
     M(Animations) \
     M(ApplePay) \
     M(Archives) \
index 1c4bbc1..55e07e3 100644 (file)
@@ -1,3 +1,57 @@
+2019-04-30  John Wilander  <wilander@apple.com>
+
+        Add logging of Ad Click Attribution errors and events to a dedicated channel
+        https://bugs.webkit.org/show_bug.cgi?id=197332
+        <rdar://problem/49918800>
+
+        Reviewed by Youenn Fablet.
+
+        This patch adds an experimental Ad Click Attribution debug mode which
+        logs information. Most changes are just log output in the various
+        functions in WebKit::AdClickAttributionManager.
+
+        The constructor to WebKit::AdClickAttributionManager now takes a
+        PAL::SessionID so that the log functions can make sure they don't
+        output anything in ephemeral sessions.
+
+        WebProcessPool::platformInitializeNetworkProcess() now picks up the
+        debug mode setting from the incoming
+        WebKit::NetworkProcessCreationParameters object.
+
+        NetworkResourceLoader::handleAdClickAttributionConversion() was
+        moved to AdClickAttributionManager::handleConversion() to keep all
+        the logging in one file.
+
+        * NetworkProcess/AdClickAttributionManager.cpp:
+        (WebKit::AdClickAttributionManager::storeUnconverted):
+        (WebKit::AdClickAttributionManager::handleConversion):
+        (WebKit::AdClickAttributionManager::convert):
+        (WebKit::AdClickAttributionManager::fireConversionRequest):
+        (WebKit::AdClickAttributionManager::firePendingConversionRequests):
+        (WebKit::AdClickAttributionManager::clearExpired):
+        (WebKit::AdClickAttributionManager::debugModeEnabled const):
+        * NetworkProcess/AdClickAttributionManager.h:
+        (WebKit::AdClickAttributionManager::AdClickAttributionManager):
+        (WebKit::AdClickAttributionManager::m_sessionID):
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::initializeNetworkProcess):
+        * NetworkProcess/NetworkProcessCreationParameters.cpp:
+        (WebKit::NetworkProcessCreationParameters::encode const):
+        (WebKit::NetworkProcessCreationParameters::decode):
+        * NetworkProcess/NetworkProcessCreationParameters.h:
+        * NetworkProcess/NetworkResourceLoader.cpp:
+        (WebKit::NetworkResourceLoader::continueWillSendRedirectedRequest):
+        (WebKit::NetworkResourceLoader::handleAdClickAttributionConversion): Deleted.
+        * NetworkProcess/NetworkSession.cpp:
+        (WebKit::NetworkSession::NetworkSession):
+        (WebKit::NetworkSession::handleAdClickAttributionConversion):
+        (WebKit::NetworkSession::convertAdClickAttribution): Deleted.
+        * NetworkProcess/NetworkSession.h:
+        * Platform/Logging.h:
+        * Shared/WebPreferences.yaml:
+        * UIProcess/Cocoa/WebProcessPoolCocoa.mm:
+        (WebKit::WebProcessPool::platformInitializeNetworkProcess):
+
 2019-04-30  Devin Rousso  <drousso@apple.com>
 
         Crash when running test wpt/tests/element_click/bubbling.py::test_element_disappears_during_click
index 08b00fb..1f081b8 100644 (file)
 #include "config.h"
 #include "AdClickAttributionManager.h"
 
+#include "Logging.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 <WebCore/RuntimeEnabledFeatures.h>
 #include <wtf/text/StringBuilder.h>
 #include <wtf/text/StringHash.h>
 
@@ -43,13 +45,37 @@ using Destination = AdClickAttribution::Destination;
 using DestinationMap = HashMap<Destination, AdClickAttribution>;
 using Conversion = AdClickAttribution::Conversion;
 
+constexpr Seconds debugModeSecondsUntilSend { 60_s };
+
 void AdClickAttributionManager::storeUnconverted(AdClickAttribution&& attribution)
 {
     clearExpired();
-    
+
+    RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Storing an ad click.");
     m_unconvertedAdClickAttributionMap.set(std::make_pair(attribution.source(), attribution.destination()), WTFMove(attribution));
 }
 
+void AdClickAttributionManager::handleConversion(Conversion&& conversion, const URL& requestURL, const WebCore::ResourceRequest& redirectRequest)
+{
+    if (m_sessionID.isEphemeral())
+        return;
+
+    RegistrableDomain redirectDomain { redirectRequest.url() };
+    auto& firstPartyURL = redirectRequest.firstPartyForCookies();
+
+    if (!redirectDomain.matches(requestURL)) {
+        RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Conversion was not accepted because the HTTP redirect was not same-site.");
+        return;
+    }
+
+    if (redirectDomain.matches(firstPartyURL)) {
+        RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Conversion was not accepted because it was requested in an HTTP redirect that is same-site as the first-party.");
+        return;
+    }
+
+    convert(AdClickAttribution::Source { WTFMove(redirectDomain) }, AdClickAttribution::Destination { firstPartyURL }, WTFMove(conversion));
+}
+
 void AdClickAttributionManager::startTimer(Seconds seconds)
 {
     m_firePendingConversionRequestsTimer.startOneShot(m_isRunningTest ? 0_s : seconds);
@@ -59,8 +85,17 @@ void AdClickAttributionManager::convert(const Source& source, const Destination&
 {
     clearExpired();
 
-    if (!conversion.isValid())
+    if (!conversion.isValid()) {
+        RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Got an invalid conversion.");
         return;
+    }
+
+#if !RELEASE_LOG_DISABLED
+    auto conversionData = conversion.data;
+    auto conversionPriority = conversion.priority;
+#endif
+
+    RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Got a conversion with conversion data: %{public}u and priority: %{public}u.", conversionData, conversionPriority);
 
     auto secondsUntilSend = Seconds::infinity();
 
@@ -73,17 +108,22 @@ void AdClickAttributionManager::convert(const Source& source, const Destination&
         if (auto optionalSecondsUntilSend = previouslyUnconvertedAttribution.convertAndGetEarliestTimeToSend(WTFMove(conversion))) {
             secondsUntilSend = *optionalSecondsUntilSend;
             ASSERT(secondsUntilSend != Seconds::infinity());
+            RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Converted a stored ad click with conversion data: %{public}u and priority: %{public}u.", conversionData, conversionPriority);
         }
-        // If there is no previously converted attribution for this pair, add the new one.
-        // If the newly converted attribution has higher priority, replace the old one.
-        if (previouslyConvertedAttributionIter == m_convertedAdClickAttributionMap.end()
-            || previouslyUnconvertedAttribution.hasHigherPriorityThan(previouslyConvertedAttributionIter->value))
+
+        if (previouslyConvertedAttributionIter == m_convertedAdClickAttributionMap.end())
+            m_convertedAdClickAttributionMap.add(pair, WTFMove(previouslyUnconvertedAttribution));
+        else if (previouslyUnconvertedAttribution.hasHigherPriorityThan(previouslyConvertedAttributionIter->value)) {
+            // If the newly converted attribution has higher priority, replace the old one.
             m_convertedAdClickAttributionMap.set(pair, WTFMove(previouslyUnconvertedAttribution));
+            RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Replaced a previously converted ad click with a new one with conversion data: %{public}u and priority: %{public}u because it had higher priority.", conversionData, conversionPriority);
+        }
     } else if (previouslyConvertedAttributionIter != m_convertedAdClickAttributionMap.end()) {
         // If we have no newly converted attribution, re-convert the old one to respect the new priority.
         if (auto optionalSecondsUntilSend = previouslyConvertedAttributionIter->value.convertAndGetEarliestTimeToSend(WTFMove(conversion))) {
             secondsUntilSend = *optionalSecondsUntilSend;
             ASSERT(secondsUntilSend != Seconds::infinity());
+            RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "Re-converted an ad click with a new one with conversion data: %{public}u and priority: %{public}u because it had higher priority.", conversionData, conversionPriority);
         }
     }
 
@@ -92,7 +132,12 @@ void AdClickAttributionManager::convert(const Source& source, const Destination&
     
     if (m_firePendingConversionRequestsTimer.isActive() && m_firePendingConversionRequestsTimer.nextFireInterval() < secondsUntilSend)
         return;
-    
+
+    if (debugModeEnabled()) {
+        RELEASE_LOG_INFO(AdClickAttribution, "Setting timer for firing conversion requests to the debug mode timeout of %{public}f seconds where the regular timeout would have been %{public}f seconds.", debugModeSecondsUntilSend.seconds(), secondsUntilSend.seconds());
+        secondsUntilSend = debugModeSecondsUntilSend;
+    }
+
     startTimer(secondsUntilSend);
 }
 
@@ -133,8 +178,10 @@ void AdClickAttributionManager::fireConversionRequest(const AdClickAttribution&
     loadParameters.mainDocumentURL = WTFMove(conversionReferrerURL);
 #endif
 
+    RELEASE_LOG_INFO_IF(debugModeEnabled(), AdClickAttribution, "About to fire an attribution request for a conversion.");
+
     m_pingLoadFunction(WTFMove(loadParameters), [](const WebCore::ResourceError& error, const WebCore::ResourceResponse& response) {
-        // FIXME: Add logging of errors to a dedicated channel.
+        RELEASE_LOG_ERROR_IF(!error.isNull(), AdClickAttribution, "Received error: '%{public}s' for ad click attribution request.", error.localizedDescription().utf8().data());
         UNUSED_PARAM(response);
         UNUSED_PARAM(error);
     });
@@ -155,7 +202,7 @@ void AdClickAttributionManager::firePendingConversionRequests()
         }
 
         auto now = WallTime::now();
-        if (*earliestTimeToSend <= now || m_isRunningTest) {
+        if (*earliestTimeToSend <= now || m_isRunningTest || debugModeEnabled()) {
             fireConversionRequest(attribution);
             attribution.markConversionAsSent();
             continue;
@@ -247,4 +294,9 @@ void AdClickAttributionManager::markAllUnconvertedAsExpiredForTesting()
         attribution.markAsExpired();
 }
 
+bool AdClickAttributionManager::debugModeEnabled() const
+{
+    return RuntimeEnabledFeatures::sharedFeatures().adClickAttributionDebugModeEnabled() && !m_sessionID.isEphemeral();
+}
+
 } // namespace WebKit
index 250748c..b62f12e 100644 (file)
 #include <WebCore/Timer.h>
 #include <wtf/CompletionHandler.h>
 #include <wtf/HashMap.h>
+#include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebKit {
 
-class AdClickAttributionManager {
+class AdClickAttributionManager : public CanMakeWeakPtr<AdClickAttributionManager> {
 public:
 
     using RegistrableDomain = WebCore::RegistrableDomain;
@@ -46,17 +47,18 @@ public:
     using Destination = WebCore::AdClickAttribution::Destination;
     using Conversion = WebCore::AdClickAttribution::Conversion;
 
-    AdClickAttributionManager()
+    explicit AdClickAttributionManager(PAL::SessionID sessionID)
         : 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());
         })
+        , m_sessionID(sessionID)
     {
     }
 
     void storeUnconverted(AdClickAttribution&&);
-    void convert(const Source&, const Destination&, Conversion&&);
+    void handleConversion(Conversion&&, const URL& requestURL, const WebCore::ResourceRequest& redirectRequest);
     void clear();
     void clearForRegistrableDomain(const RegistrableDomain&);
     void toString(CompletionHandler<void(String)>&&) const;
@@ -67,9 +69,11 @@ public:
 
 private:
     void startTimer(Seconds);
+    void convert(const Source&, const Destination&, Conversion&&);
     void fireConversionRequest(const AdClickAttribution&);
     void firePendingConversionRequests();
     void clearExpired();
+    bool debugModeEnabled() const;
 
     HashMap<std::pair<Source, Destination>, AdClickAttribution> m_unconvertedAdClickAttributionMap;
     HashMap<std::pair<Source, Destination>, AdClickAttribution> m_convertedAdClickAttributionMap;
@@ -77,6 +81,7 @@ private:
     Function<void(NetworkResourceLoadParameters&&, CompletionHandler<void(const WebCore::ResourceError&, const WebCore::ResourceResponse&)>&&)> m_pingLoadFunction;
     bool m_isRunningTest { false };
     Optional<URL> m_conversionBaseURLForTesting;
+    PAL::SessionID m_sessionID;
 };
     
 } // namespace WebKit
index 69226f0..6ea9e22 100644 (file)
@@ -306,6 +306,8 @@ void NetworkProcess::initializeNetworkProcess(NetworkProcessCreationParameters&&
     WebCore::RuntimeEnabledFeatures::sharedFeatures().setIsITPDatabaseEnabled(parameters.shouldEnableITPDatabase);
     WebCore::RuntimeEnabledFeatures::sharedFeatures().setIsITPFirstPartyWebsiteDataRemovalEnabled(parameters.isITPFirstPartyWebsiteDataRemovalEnabled);
 
+    WebCore::RuntimeEnabledFeatures::sharedFeatures().setAdClickAttributionDebugModeEnabled(parameters.enableAdClickAttributionDebugMode);
+
     SandboxExtension::consumePermanently(parameters.defaultDataStoreParameters.networkSessionParameters.resourceLoadStatisticsDirectoryExtensionHandle);
 
     auto sessionID = parameters.defaultDataStoreParameters.networkSessionParameters.sessionID;
index ed31997..4059319 100644 (file)
@@ -91,6 +91,7 @@ void NetworkProcessCreationParameters::encode(IPC::Encoder& encoder) const
     encoder << shouldEnableITPDatabase;
     encoder << downloadMonitorSpeedMultiplier;
     encoder << isITPFirstPartyWebsiteDataRemovalEnabled;
+    encoder << enableAdClickAttributionDebugMode;
 }
 
 bool NetworkProcessCreationParameters::decode(IPC::Decoder& decoder, NetworkProcessCreationParameters& result)
@@ -220,6 +221,9 @@ bool NetworkProcessCreationParameters::decode(IPC::Decoder& decoder, NetworkProc
     if (!decoder.decode(result.isITPFirstPartyWebsiteDataRemovalEnabled))
         return false;
 
+    if (!decoder.decode(result.enableAdClickAttributionDebugMode))
+        return false;
+
     return true;
 }
 
index 0f1f440..203be69 100644 (file)
@@ -109,6 +109,7 @@ struct NetworkProcessCreationParameters {
     bool shouldEnableITPDatabase { false };
     bool isITPFirstPartyWebsiteDataRemovalEnabled { true };
     uint32_t downloadMonitorSpeedMultiplier { 1 };
+    bool enableAdClickAttributionDebugMode { false };
 };
 
 } // namespace WebKit
index 048f077..5ac46e5 100644 (file)
@@ -600,18 +600,6 @@ Optional<Seconds> NetworkResourceLoader::validateCacheEntryForMaxAgeCapValidatio
     return WTF::nullopt;
 }
 
-void NetworkResourceLoader::handleAdClickAttributionConversion(AdClickAttribution::Conversion&& conversion, const URL& requestURL, const WebCore::ResourceRequest& redirectRequest)
-{
-    ASSERT(!sessionID().isEphemeral());
-
-    RegistrableDomain redirectDomain { redirectRequest.url() };
-    auto& firstPartyURL = redirectRequest.firstPartyForCookies();
-    NetworkSession* networkSession = nullptr;
-    // The redirect has to be done by the same registrable domain and it has to be a third-party request.
-    if (redirectDomain.matches(requestURL) && !redirectDomain.matches(firstPartyURL) && (networkSession = m_connection->networkProcess().networkSession(sessionID())))
-        networkSession->convertAdClickAttribution(AdClickAttribution::Source { WTFMove(redirectDomain) }, AdClickAttribution::Destination { firstPartyURL }, WTFMove(conversion));
-}
-
 void NetworkResourceLoader::willSendRedirectedRequest(ResourceRequest&& request, ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
 {
     ++m_redirectCount;
@@ -672,8 +660,10 @@ void NetworkResourceLoader::continueWillSendRedirectedRequest(ResourceRequest&&
         return;
     }
 
-    if (adClickConversion)
-        handleAdClickAttributionConversion(WTFMove(*adClickConversion), request.url(), redirectRequest);
+    NetworkSession* networkSession = nullptr;
+    if (adClickConversion && (networkSession = m_connection->networkProcess().networkSession(sessionID())))
+        networkSession->handleAdClickAttributionConversion(WTFMove(*adClickConversion), request.url(), redirectRequest);
+
     send(Messages::WebResourceLoader::WillSendRequest(redirectRequest, sanitizeResponseIfPossible(WTFMove(redirectResponse), ResourceResponse::SanitizationType::Redirection)));
 }
 
index 5b442bf..9d354b1 100644 (file)
@@ -37,6 +37,7 @@
 #include "WebProcessProxy.h"
 #include <WebCore/CookieJar.h>
 #include <WebCore/NetworkStorageSession.h>
+#include <WebCore/ResourceRequest.h>
 
 #if PLATFORM(COCOA)
 #include "NetworkSessionCocoa.h"
@@ -74,7 +75,7 @@ NetworkStorageSession& NetworkSession::networkStorageSession() const
 NetworkSession::NetworkSession(NetworkProcess& networkProcess, PAL::SessionID sessionID)
     : m_sessionID(sessionID)
     , m_networkProcess(networkProcess)
-    , m_adClickAttribution(makeUniqueRef<AdClickAttributionManager>())
+    , m_adClickAttribution(makeUniqueRef<AdClickAttributionManager>(sessionID))
 {
     m_adClickAttribution->setPingLoadFunction([this, weakThis = makeWeakPtr(this)](NetworkResourceLoadParameters&& loadParameters, CompletionHandler<void(const WebCore::ResourceError&, const WebCore::ResourceResponse&)>&& completionHandler) {
         if (!weakThis)
@@ -149,9 +150,9 @@ void NetworkSession::storeAdClickAttribution(WebCore::AdClickAttribution&& adCli
     m_adClickAttribution->storeUnconverted(WTFMove(adClickAttribution));
 }
 
-void NetworkSession::convertAdClickAttribution(const WebCore::AdClickAttribution::Source& source, const WebCore::AdClickAttribution::Destination& destination, WebCore::AdClickAttribution::Conversion&& conversion)
+void NetworkSession::handleAdClickAttributionConversion(AdClickAttribution::Conversion&& conversion, const URL& requestURL, const WebCore::ResourceRequest& redirectRequest)
 {
-    m_adClickAttribution->convert(source, destination, WTFMove(conversion));
+    m_adClickAttribution->handleConversion(WTFMove(conversion), requestURL, redirectRequest);
 }
 
 void NetworkSession::dumpAdClickAttribution(CompletionHandler<void(String)>&& completionHandler)
index 7099db8..8140ad9 100644 (file)
@@ -39,6 +39,7 @@
 
 namespace WebCore {
 class NetworkStorageSession;
+class ResourceRequest;
 enum class IncludeHttpOnlyCookies : bool;
 enum class ShouldSample : bool;
 }
@@ -81,7 +82,7 @@ public:
     void notifyPageStatisticsTelemetryFinished(unsigned totalPrevalentResources, unsigned totalPrevalentResourcesWithUserInteraction, unsigned top3SubframeUnderTopFrameOrigins);
 #endif
     void storeAdClickAttribution(WebCore::AdClickAttribution&&);
-    void convertAdClickAttribution(const WebCore::AdClickAttribution::Source&, const WebCore::AdClickAttribution::Destination&, WebCore::AdClickAttribution::Conversion&&);
+    void handleAdClickAttributionConversion(WebCore::AdClickAttribution::Conversion&&, const URL& requestURL, const WebCore::ResourceRequest& redirectRequest);
     void dumpAdClickAttribution(CompletionHandler<void(String)>&&);
     void clearAdClickAttribution();
     void clearAdClickAttributionForRegistrableDomain(WebCore::RegistrableDomain&&);
index 4c5cc67..9d5a42d 100644 (file)
@@ -40,6 +40,7 @@ extern "C" {
 #endif
 
 #define WEBKIT2_LOG_CHANNELS(M) \
+    M(AdClickAttribution) \
     M(Automation) \
     M(ActivityState) \
     M(BackForward) \
index ff9da3b..c4fccfc 100644 (file)
@@ -1412,6 +1412,14 @@ AdClickAttributionEnabled:
     webcoreBinding: RuntimeEnabledFeatures
     category: experimental
 
+AdClickAttributionDebugModeEnabled:
+    type: bool
+    defaultValue: false
+    humanReadableName: "Ad Click Attribution Debug Mode"
+    humanReadableDescription: "Enable Ad Click Attribution Debug Mode"
+    webcoreBinding: RuntimeEnabledFeatures
+    category: experimental
+
 # For internal features:
 # The type should be boolean.
 # You must provide a humanReadableName and humanReadableDescription for all debug features. They
index ef92bad..807632f 100644 (file)
@@ -304,6 +304,8 @@ void WebProcessPool::platformInitializeNetworkProcess(NetworkProcessCreationPara
     auto isITPFirstPartyWebsiteDataRemovalEnabledStr = [defaults stringForKey:[NSString stringWithFormat:@"Experimental%@", WebPreferencesKey::isITPFirstPartyWebsiteDataRemovalEnabledKey().createCFString().get()]];
     if ([isITPFirstPartyWebsiteDataRemovalEnabledStr isEqual:@"0"])
         parameters.isITPFirstPartyWebsiteDataRemovalEnabled = false;
+
+    parameters.enableAdClickAttributionDebugMode = [defaults boolForKey:[NSString stringWithFormat:@"Experimental%@", WebPreferencesKey::adClickAttributionDebugModeEnabledKey().createCFString().get()]];
 }
 
 void WebProcessPool::platformInvalidateContext()