Diff
Modified: trunk/LayoutTests/ChangeLog (275136 => 275137)
--- trunk/LayoutTests/ChangeLog 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/LayoutTests/ChangeLog 2021-03-27 18:52:51 UTC (rev 275137)
@@ -1,3 +1,20 @@
+2021-03-27 Kate Cheney <[email protected]>
+
+ PCM: Send report to both click source and attribution destination website
+ https://bugs.webkit.org/show_bug.cgi?id=223615
+ <rdar://problem/75849443>
+
+ Reviewed by Brent Fulgham.
+
+ Layout test coverage.
+
+ * http/tests/privateClickMeasurement/resources/conversionFilePath.py:
+ * http/tests/privateClickMeasurement/resources/conversionReport.py:
+ * http/tests/privateClickMeasurement/resources/fraudPreventionTestURL.py:
+ * http/tests/privateClickMeasurement/resources/getConversionData.py:
+ * http/tests/privateClickMeasurement/send-attribution-conversion-request-expected.txt:
+ * http/tests/privateClickMeasurement/send-attribution-conversion-request.html:
+
2021-03-27 Zalan Bujtas <[email protected]>
[Multicolumn] Do not try to re-validate a multicol spanner when the renderer is moved internally
Modified: trunk/LayoutTests/http/tests/privateClickMeasurement/resources/conversionFilePath.py (275136 => 275137)
--- trunk/LayoutTests/http/tests/privateClickMeasurement/resources/conversionFilePath.py 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/resources/conversionFilePath.py 2021-03-27 18:52:51 UTC (rev 275137)
@@ -9,11 +9,17 @@
http_root = os.path.dirname(os.path.dirname(os.path.abspath(os.path.dirname(file))))
sys.path.insert(0, http_root)
+recipient = parse_qs(os.environ.get('QUERY_STRING', ''), keep_blank_values=True).get('recipient', [None])[0]
nonce = parse_qs(os.environ.get('QUERY_STRING', ''), keep_blank_values=True).get('nonce', [None])[0]
+if recipient is not None:
+ conversion_file_name = 'privateClickMeasurementConversion{}'.format(recipient)
+else:
+ conversion_file_name = 'privateClickMeasurementConversion'
+
if nonce is not None:
- conversion_file_name = 'privateClickMeasurementConversion{}.txt'.format(nonce)
+ conversion_file_name = conversion_file_name + '{}.txt'.format(nonce)
else:
- conversion_file_name = 'privateClickMeasurementConversion.txt'
+ conversion_file_name = conversion_file_name + '.txt'
-conversion_file_path = os.path.join(tempfile.gettempdir(), conversion_file_name)
\ No newline at end of file
+conversion_file_path = os.path.join(tempfile.gettempdir(), conversion_file_name)
Modified: trunk/LayoutTests/http/tests/privateClickMeasurement/resources/conversionReport.py (275136 => 275137)
--- trunk/LayoutTests/http/tests/privateClickMeasurement/resources/conversionReport.py 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/resources/conversionReport.py 2021-03-27 18:52:51 UTC (rev 275137)
@@ -29,11 +29,16 @@
if uri is not None:
position_of_nonce = uri.find('?nonce=')
+ position_of_nonce_alternate = uri.find('&nonce=')
+
if position_of_nonce == -1:
output_url = uri
else:
output_url = uri[0:position_of_nonce]
+ if position_of_nonce_alternate != -1:
+ output_url = uri[0:position_of_nonce_alternate]
+
conversion_file.write('REQUEST_URI: {}\n'.format(output_url))
if not cookies_found:
@@ -49,4 +54,4 @@
'status: 200\r\n'
'Set-Cookie: cookieSetInConversionReport=1; path=/\r\n'
'Content-Type: text/html\r\n\r\n'
-)
\ No newline at end of file
+)
Modified: trunk/LayoutTests/http/tests/privateClickMeasurement/resources/fraudPreventionTestURL.py (275136 => 275137)
--- trunk/LayoutTests/http/tests/privateClickMeasurement/resources/fraudPreventionTestURL.py 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/resources/fraudPreventionTestURL.py 2021-03-27 18:52:51 UTC (rev 275137)
@@ -60,4 +60,4 @@
'secret_token_signature: ABCD\r\n'
'Set-Cookie: cookieSetInTokenSigningResponse=1; path=/\r\n'
'Content-Type: text/html\r\n\r\n'
-)
\ No newline at end of file
+)
Modified: trunk/LayoutTests/http/tests/privateClickMeasurement/resources/getConversionData.py (275136 => 275137)
--- trunk/LayoutTests/http/tests/privateClickMeasurement/resources/getConversionData.py 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/resources/getConversionData.py 2021-03-27 18:52:51 UTC (rev 275137)
@@ -62,4 +62,4 @@
'</script>'
)
-sys.stdout.write('</body></html>')
\ No newline at end of file
+sys.stdout.write('</body></html>')
Modified: trunk/LayoutTests/http/tests/privateClickMeasurement/send-attribution-conversion-request-expected.txt (275136 => 275137)
--- trunk/LayoutTests/http/tests/privateClickMeasurement/send-attribution-conversion-request-expected.txt 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/send-attribution-conversion-request-expected.txt 2021-03-27 18:52:51 UTC (rev 275137)
@@ -13,7 +13,7 @@
Attribution received.
HTTP_HOST: 127.0.0.1:8000
Content type: application/json
-REQUEST_URI: /privateClickMeasurement/resources/conversionReport.py
+REQUEST_URI: /privateClickMeasurement/resources/conversionReport.py?recipient=ClickSource
No cookies in attribution request.
Request body:
{"source_engagement_type":"click","source_site":"127.0.0.1","source_id":3,"attributed_on_site":"localhost","trigger_data":12,"version":2}
@@ -22,6 +22,21 @@
--------
Frame: '<!--frame3-->'
--------
+Attribution received.
+HTTP_HOST: localhost:8000
+Content type: application/json
+REQUEST_URI: /privateClickMeasurement/resources/conversionReport.py?recipient=ClickDestination
+No cookies in attribution request.
+Request body:
+{"source_engagement_type":"click","source_site":"127.0.0.1","source_id":3,"attributed_on_site":"localhost","trigger_data":12,"version":2}
+
+
+--------
+Frame: '<!--frame4-->'
+--------
Cookies are: cookieSetAsFirstParty = 1
-No stored Private Click Measurement data.
+--------
+Frame: '<!--frame5-->'
+--------
+Cookies are: cookieSetAsFirstParty = 1
Modified: trunk/LayoutTests/http/tests/privateClickMeasurement/send-attribution-conversion-request.html (275136 => 275137)
--- trunk/LayoutTests/http/tests/privateClickMeasurement/send-attribution-conversion-request.html 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/send-attribution-conversion-request.html 2021-03-27 18:52:51 UTC (rev 275137)
@@ -43,23 +43,35 @@
document.body.appendChild(iframeElement);
}
+ let reportsReceived = 0;
function appendConversionDataIframeAndFinish() {
- testRunner.dumpPrivateClickMeasurement();
document.body.removeChild(document.getElementById("targetLink"));
document.body.removeChild(document.getElementById("pixel"));
appendIframe("http://127.0.0.1:8000/cookies/resources/echo-cookies.php");
- appendIframe("http://127.0.0.1:8000/privateClickMeasurement/resources/getConversionData.py?timeout_ms=2000&nonce=" + nonce, function() {
+ // Click source.
+ appendIframe("http://127.0.0.1:8000/privateClickMeasurement/resources/getConversionData.py?timeout_ms=2000&recipient=ClickSource&nonce=" + nonce, function() {
appendIframe("http://127.0.0.1:8000/cookies/resources/echo-cookies.php", function() {
- tearDownAndFinish();
+ reportsReceived++;
+ if (reportsReceived >= 2)
+ tearDownAndFinish();
});
});
+
+ // Click destination.
+ appendIframe("http://127.0.0.1:8000/privateClickMeasurement/resources/getConversionData.py?timeout_ms=2000&recipient=ClickDestination&nonce=" + nonce, function() {
+ appendIframe("http://127.0.0.1:8000/cookies/resources/echo-cookies.php", function() {
+ reportsReceived++;
+ if (reportsReceived >= 2)
+ tearDownAndFinish();
+ });
+ });
}
function runTest() {
if (window.testRunner) {
if (window.location.search === "?stepTwo") {
- testRunner.setPrivateClickMeasurementAttributionReportURLsForTesting("http://127.0.0.1:8000/privateClickMeasurement/resources/conversionReport.py?nonce=" + nonce, "http://localhost:8000/privateClickMeasurement/resources/conversionReport.py?nonce=" + nonce);
+ testRunner.setPrivateClickMeasurementAttributionReportURLsForTesting("http://127.0.0.1:8000/privateClickMeasurement/resources/conversionReport.py?recipient=ClickSource&nonce=" + nonce, "http://localhost:8000/privateClickMeasurement/resources/conversionReport.py?recipient=ClickDestination&nonce=" + nonce);
let imageElement = document.createElement("img");
imageElement.src = "" + nonce;
imageElement.id = "pixel";
Modified: trunk/Source/WebCore/ChangeLog (275136 => 275137)
--- trunk/Source/WebCore/ChangeLog 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebCore/ChangeLog 2021-03-27 18:52:51 UTC (rev 275137)
@@ -1,3 +1,36 @@
+2021-03-27 Kate Cheney <[email protected]>
+
+ PCM: Send report to both click source and attribution destination website
+ https://bugs.webkit.org/show_bug.cgi?id=223615
+ <rdar://problem/75849443>
+
+ Reviewed by Brent Fulgham.
+
+ Introduce 2 new structs for storing the earliest time to send and
+ seconds until send for the source and destination sites.
+
+ * loader/PrivateClickMeasurement.cpp:
+ (WebCore::PrivateClickMeasurement::isValid const):
+ (WebCore::PrivateClickMeasurement::hasPreviouslyBeenReported):
+ (WebCore::randomlyBetweenTwentyFourAndFortyEightHours):
+ (WebCore::PrivateClickMeasurement::attributeAndGetEarliestTimeToSend):
+ * loader/PrivateClickMeasurement.h:
+ (WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData::hasValidSecondsUntilSendValues):
+ (WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData::minSecondsUntilSend):
+ (WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData::encode const):
+ (WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData::decode):
+ (WebCore::PrivateClickMeasurement::AttributionTimeToSendData::earliestTimeToSend):
+ (WebCore::PrivateClickMeasurement::AttributionTimeToSendData::latestTimeToSend):
+ (WebCore::PrivateClickMeasurement::AttributionTimeToSendData::attributionReportEndpoint):
+ (WebCore::PrivateClickMeasurement::AttributionTimeToSendData::encode const):
+ (WebCore::PrivateClickMeasurement::AttributionTimeToSendData::decode):
+ (WebCore::PrivateClickMeasurement::timesToSend const):
+ (WebCore::PrivateClickMeasurement::setTimesToSend):
+ (WebCore::PrivateClickMeasurement::encode const):
+ (WebCore::PrivateClickMeasurement::decode):
+ (WebCore::PrivateClickMeasurement::earliestTimeToSend const): Deleted.
+ (WebCore::PrivateClickMeasurement::setEarliestTimeToSend): Deleted.
+
2021-03-27 Simon Fraser <[email protected]>
Remove DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread()
Modified: trunk/Source/WebCore/loader/PrivateClickMeasurement.cpp (275136 => 275137)
--- trunk/Source/WebCore/loader/PrivateClickMeasurement.cpp 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebCore/loader/PrivateClickMeasurement.cpp 2021-03-27 18:52:51 UTC (rev 275137)
@@ -55,7 +55,7 @@
&& m_sourceID.isValid()
&& !m_sourceSite.registrableDomain.isEmpty()
&& !m_destinationSite.registrableDomain.isEmpty()
- && m_earliestTimeToSend;
+ && (m_timesToSend.sourceEarliestTimeToSend || m_timesToSend.destinationEarliestTimeToSend);
}
Expected<PrivateClickMeasurement::AttributionTriggerData, String> PrivateClickMeasurement::parseAttributionRequest(const URL& redirectURL)
@@ -92,8 +92,18 @@
return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL path contained unrecognized parts."_s);
}
-Optional<Seconds> PrivateClickMeasurement::attributeAndGetEarliestTimeToSend(AttributionTriggerData&& attributionTriggerData)
+bool PrivateClickMeasurement::hasPreviouslyBeenReported()
{
+ return !m_timesToSend.sourceEarliestTimeToSend || !m_timesToSend.destinationEarliestTimeToSend;
+}
+
+static Seconds randomlyBetweenTwentyFourAndFortyEightHours()
+{
+ return 24_h + Seconds(randomNumber() * (24_h).value());
+}
+
+PrivateClickMeasurement::AttributionSecondsUntilSendData PrivateClickMeasurement::attributeAndGetEarliestTimeToSend(AttributionTriggerData&& attributionTriggerData)
+{
if (!attributionTriggerData.isValid() || (m_attributionTriggerData && m_attributionTriggerData->priority >= attributionTriggerData.priority))
return { };
@@ -100,9 +110,11 @@
m_attributionTriggerData = WTFMove(attributionTriggerData);
// 24-48 hour delay before sending. This helps privacy since the conversion and the attribution
// requests are detached and the time of the attribution does not reveal the time of the conversion.
- auto seconds = 24_h + Seconds(randomNumber() * (24_h).value());
- m_earliestTimeToSend = WallTime::now() + seconds;
- return seconds;
+ auto sourceSecondsUntilSend = randomlyBetweenTwentyFourAndFortyEightHours();
+ auto destinationSecondsUntilSend = randomlyBetweenTwentyFourAndFortyEightHours();
+ m_timesToSend = { WallTime::now() + sourceSecondsUntilSend, WallTime::now() + destinationSecondsUntilSend };
+
+ return AttributionSecondsUntilSendData { sourceSecondsUntilSend, destinationSecondsUntilSend };
}
bool PrivateClickMeasurement::hasHigherPriorityThan(const PrivateClickMeasurement& other) const
Modified: trunk/Source/WebCore/loader/PrivateClickMeasurement.h (275136 => 275137)
--- trunk/Source/WebCore/loader/PrivateClickMeasurement.h 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebCore/loader/PrivateClickMeasurement.h 2021-03-27 18:52:51 UTC (rev 275137)
@@ -53,6 +53,7 @@
using PriorityValue = uint32_t;
enum class PcmDataCarried : bool { NonPersonallyIdentifiable, PersonallyIdentifiable };
+ enum class AttributionReportEndpoint : bool { Source, Destination };
struct SourceID {
static constexpr uint32_t MaxEntropy = 255;
@@ -247,6 +248,116 @@
template<class Decoder> static Optional<AttributionTriggerData> decode(Decoder&);
};
+ struct AttributionSecondsUntilSendData {
+ Optional<Seconds> sourceSeconds;
+ Optional<Seconds> destinationSeconds;
+
+ bool hasValidSecondsUntilSendValues()
+ {
+ return sourceSeconds && destinationSeconds;
+ }
+
+ Optional<Seconds> minSecondsUntilSend()
+ {
+ if (!sourceSeconds && !destinationSeconds)
+ return WTF::nullopt;
+
+ if (sourceSeconds && destinationSeconds)
+ return std::min(sourceSeconds, destinationSeconds);
+
+ return sourceSeconds ? sourceSeconds : destinationSeconds;
+ }
+
+ template<class Encoder>
+ void encode(Encoder& encoder) const
+ {
+ encoder << sourceSeconds << destinationSeconds;
+ }
+
+ template<class Decoder>
+ static Optional<AttributionSecondsUntilSendData> decode(Decoder& decoder)
+ {
+ Optional<Optional<Seconds>> sourceSeconds;
+ decoder >> sourceSeconds;
+ if (!sourceSeconds)
+ return WTF::nullopt;
+
+ Optional<Optional<Seconds>> destinationSeconds;
+ decoder >> destinationSeconds;
+ if (!destinationSeconds)
+ return WTF::nullopt;
+
+ return AttributionSecondsUntilSendData { WTFMove(*sourceSeconds), WTFMove(*destinationSeconds) };
+ }
+ };
+
+ struct AttributionTimeToSendData {
+ Optional<WallTime> sourceEarliestTimeToSend;
+ Optional<WallTime> destinationEarliestTimeToSend;
+
+ Optional<WallTime> earliestTimeToSend()
+ {
+ if (!sourceEarliestTimeToSend && !destinationEarliestTimeToSend)
+ return WTF::nullopt;
+
+ if (sourceEarliestTimeToSend && destinationEarliestTimeToSend)
+ return std::min(sourceEarliestTimeToSend, destinationEarliestTimeToSend);
+
+ return sourceEarliestTimeToSend ? sourceEarliestTimeToSend : destinationEarliestTimeToSend;
+ }
+
+ Optional<WallTime> latestTimeToSend()
+ {
+ if (!sourceEarliestTimeToSend && !destinationEarliestTimeToSend)
+ return WTF::nullopt;
+
+ if (sourceEarliestTimeToSend && destinationEarliestTimeToSend)
+ return std::max(sourceEarliestTimeToSend, destinationEarliestTimeToSend);
+
+ return sourceEarliestTimeToSend ? sourceEarliestTimeToSend : destinationEarliestTimeToSend;
+ }
+
+ Optional<AttributionReportEndpoint> attributionReportEndpoint()
+ {
+ if (sourceEarliestTimeToSend && destinationEarliestTimeToSend) {
+ if (*sourceEarliestTimeToSend < *destinationEarliestTimeToSend)
+ return AttributionReportEndpoint::Source;
+
+ return AttributionReportEndpoint::Destination;
+ }
+
+ if (sourceEarliestTimeToSend)
+ return AttributionReportEndpoint::Source;
+
+ if (destinationEarliestTimeToSend)
+ return AttributionReportEndpoint::Destination;
+
+ return WTF::nullopt;
+ }
+
+ template<class Encoder>
+ void encode(Encoder& encoder) const
+ {
+ encoder << sourceEarliestTimeToSend << destinationEarliestTimeToSend;
+ }
+
+ template<class Decoder>
+ static Optional<AttributionTimeToSendData> decode(Decoder& decoder)
+ {
+ Optional<Optional<WallTime>> sourceEarliestTimeToSend;
+ decoder >> sourceEarliestTimeToSend;
+ if (!sourceEarliestTimeToSend)
+ return WTF::nullopt;
+
+ Optional<Optional<WallTime>> destinationEarliestTimeToSend;
+ decoder >> destinationEarliestTimeToSend;
+ if (!destinationEarliestTimeToSend)
+ return WTF::nullopt;
+
+ return AttributionTimeToSendData { WTFMove(*sourceEarliestTimeToSend), WTFMove(*destinationEarliestTimeToSend) };
+ }
+ };
+
PrivateClickMeasurement() = default;
PrivateClickMeasurement(SourceID sourceID, const SourceSite& sourceSite, const AttributionDestinationSite& destinationSite, String&& sourceDescription = { }, String&& purchaser = { }, WallTime timeOfAdClick = WallTime::now())
: m_sourceID { sourceID }
@@ -260,7 +371,7 @@
WEBCORE_EXPORT static const Seconds maxAge();
WEBCORE_EXPORT static Expected<AttributionTriggerData, String> parseAttributionRequest(const URL& redirectURL);
- WEBCORE_EXPORT Optional<Seconds> attributeAndGetEarliestTimeToSend(AttributionTriggerData&&);
+ WEBCORE_EXPORT AttributionSecondsUntilSendData attributeAndGetEarliestTimeToSend(AttributionTriggerData&&);
WEBCORE_EXPORT bool hasHigherPriorityThan(const PrivateClickMeasurement&) const;
WEBCORE_EXPORT URL attributionReportSourceURL() const;
WEBCORE_EXPORT URL attributionReportAttributeOnURL() const;
@@ -268,8 +379,9 @@
const SourceSite& sourceSite() const { return m_sourceSite; };
const AttributionDestinationSite& destinationSite() const { return m_destinationSite; };
WallTime timeOfAdClick() const { return m_timeOfAdClick; }
- Optional<WallTime> earliestTimeToSend() const { return m_earliestTimeToSend; };
- void setEarliestTimeToSend(WallTime time) { m_earliestTimeToSend = time; }
+ WEBCORE_EXPORT bool hasPreviouslyBeenReported();
+ AttributionTimeToSendData timesToSend() const { return m_timesToSend; };
+ void setTimesToSend(AttributionTimeToSendData data) { m_timesToSend = data; }
const SourceID& sourceID() const { return m_sourceID; }
Optional<AttributionTriggerData> attributionTriggerData() { return m_attributionTriggerData; }
void setAttribution(AttributionTriggerData&& attributionTriggerData) { m_attributionTriggerData = WTFMove(attributionTriggerData); }
@@ -327,7 +439,7 @@
WallTime m_timeOfAdClick;
Optional<AttributionTriggerData> m_attributionTriggerData;
- Optional<WallTime> m_earliestTimeToSend;
+ AttributionTimeToSendData m_timesToSend;
struct SourceUnlinkableToken {
#if PLATFORM(COCOA)
@@ -354,7 +466,7 @@
<< m_timeOfAdClick
<< m_ephemeralSourceNonce
<< m_attributionTriggerData
- << m_earliestTimeToSend;
+ << m_timesToSend;
}
template<class Decoder>
@@ -400,9 +512,9 @@
if (!attributionTriggerData)
return WTF::nullopt;
- Optional<Optional<WallTime>> earliestTimeToSend;
- decoder >> earliestTimeToSend;
- if (!earliestTimeToSend)
+ Optional<AttributionTimeToSendData> timesToSend;
+ decoder >> timesToSend;
+ if (!timesToSend)
return WTF::nullopt;
PrivateClickMeasurement attribution {
@@ -415,7 +527,7 @@
};
attribution.m_ephemeralSourceNonce = WTFMove(*ephemeralSourceNonce);
attribution.m_attributionTriggerData = WTFMove(*attributionTriggerData);
- attribution.m_earliestTimeToSend = WTFMove(*earliestTimeToSend);
+ attribution.m_timesToSend = WTFMove(*timesToSend);
return attribution;
}
Modified: trunk/Source/WebKit/ChangeLog (275136 => 275137)
--- trunk/Source/WebKit/ChangeLog 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/ChangeLog 2021-03-27 18:52:51 UTC (rev 275137)
@@ -1,3 +1,66 @@
+2021-03-27 Kate Cheney <[email protected]>
+
+ PCM: Send report to both click source and attribution destination website
+ https://bugs.webkit.org/show_bug.cgi?id=223615
+ <rdar://problem/75849443>
+
+ Reviewed by Brent Fulgham.
+
+ * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
+ Move these queries to the correct INSERT OR REPLACE category. Stop
+ inserting null for earliestTimeToSendToDestination and starting
+ binding a parameter to it now that we are supporting reports to both
+ sites.
+
+ Now that earliestTimeToSend* can be null if a report has been sent
+ to a site, we need queries to set the value to null, and also need
+ to sort attributions by the minimum of either the two reporting times,
+ or the non-null time if one is null.
+
+ (WebKit::ResourceLoadStatisticsDatabaseStore::destroyStatements):
+ (WebKit::ResourceLoadStatisticsDatabaseStore::buildPrivateClickMeasurementFromDatabase):
+ (WebKit::ResourceLoadStatisticsDatabaseStore::insertPrivateClickMeasurement):
+ (WebKit::ResourceLoadStatisticsDatabaseStore::attributePrivateClickMeasurement):
+ We should not attribute a PCM value if it has already been reported to
+ either the source or destination. This is covered by checking
+ secondsUntilSend.hasValidSecondsUntilSendValues() and
+ previouslyAttributed.value().hasPreviouslyBeenReported() before
+ inserting anything into the attributed PCM table.
+
+ (WebKit::ResourceLoadStatisticsDatabaseStore::earliestTimesToSend):
+ (WebKit::ResourceLoadStatisticsDatabaseStore::markReportAsSentToSource):
+ (WebKit::ResourceLoadStatisticsDatabaseStore::markReportAsSentToDestination):
+ (WebKit::ResourceLoadStatisticsDatabaseStore::clearSentAttribution):
+ Clear a value from the attributed table only if it has been sent to
+ both source and destination site. Otherwise, set the corresponding
+ attribution endpoint to null so we don't send it here again.
+
+ (WebKit::ResourceLoadStatisticsDatabaseStore::markAttributedPrivateClickMeasurementsAsExpiredForTesting):
+ For the sake of testing we can set the destination earliest time to
+ send to null. We are only confirming here that the expired attribution
+ gets sent.
+
+ * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h:
+ * NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.h:
+ * NetworkProcess/Classifier/ResourceLoadStatisticsStore.h:
+ * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
+ (WebKit::WebResourceLoadStatisticsStore::attributePrivateClickMeasurement):
+ (WebKit::WebResourceLoadStatisticsStore::clearSentAttribution):
+ * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
+ * NetworkProcess/PrivateClickMeasurementManager.cpp:
+ (WebKit::PrivateClickMeasurementManager::storeUnattributed):
+ (WebKit::PrivateClickMeasurementManager::getTokenPublicKey):
+ We currently have no way of setting the destination token URL site for
+ testing. To avoid flakiness, we should not make a ping load for the
+ token URL if we are reporting to the destination in test mode.
+
+ (WebKit::PrivateClickMeasurementManager::attribute):
+ (WebKit::PrivateClickMeasurementManager::fireConversionRequest):
+ (WebKit::PrivateClickMeasurementManager::fireConversionRequestImpl):
+ (WebKit::PrivateClickMeasurementManager::clearSentAttribution):
+ (WebKit::PrivateClickMeasurementManager::firePendingAttributionRequests):
+ * NetworkProcess/PrivateClickMeasurementManager.h:
+
2021-03-27 Tyler Wilcock <[email protected]>
Remove DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread()
Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp (275136 => 275137)
--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp 2021-03-27 18:52:51 UTC (rev 275137)
@@ -81,10 +81,6 @@
constexpr auto topFrameUniqueRedirectsFromQuery = "INSERT OR IGNORE INTO TopFrameUniqueRedirectsFrom (targetDomainID, fromDomainID) SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
constexpr auto topFrameLoadedThirdPartyScriptsQuery = "INSERT OR IGNORE into TopFrameLoadedThirdPartyScripts (topFrameDomainID, subresourceDomainID) SELECT ?, domainID FROM ObservedDomains where registrableDomain in ( "_s;
constexpr auto subresourceUniqueRedirectsFromQuery = "INSERT OR IGNORE INTO SubresourceUniqueRedirectsFrom (subresourceDomainID, fromDomainID) SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
-constexpr auto insertUnattributedPrivateClickMeasurementQuery = "INSERT OR REPLACE INTO UnattributedPrivateClickMeasurement (sourceSiteDomainID, destinationSiteDomainID, "
- "sourceID, timeOfAdClick, token, signature, keyID) VALUES (?, ?, ?, ?, ?, ?, ?)"_s;
-constexpr auto insertAttributedPrivateClickMeasurementQuery = "INSERT OR REPLACE INTO AttributedPrivateClickMeasurement (sourceSiteDomainID, destinationSiteDomainID, "
- "sourceID, attributionTriggerData, priority, timeOfAdClick, earliestTimeToSendToSource, token, signature, keyID, earliestTimeToSendToDestination) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, null)"_s;
// INSERT OR REPLACE Queries
constexpr auto subframeUnderTopFrameDomainsQuery = "INSERT OR REPLACE into SubframeUnderTopFrameDomains (subFrameDomainID, lastUpdated, topFrameDomainID) SELECT ?, ?, domainID FROM ObservedDomains where registrableDomain in ( "_s;
@@ -91,6 +87,10 @@
constexpr auto topFrameLinkDecorationsFromQuery = "INSERT OR REPLACE INTO TopFrameLinkDecorationsFrom (toDomainID, lastUpdated, fromDomainID) SELECT ?, ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
constexpr auto subresourceUnderTopFrameDomainsQuery = "INSERT OR REPLACE INTO SubresourceUnderTopFrameDomains (subresourceDomainID, lastUpdated, topFrameDomainID) SELECT ?, ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
constexpr auto subresourceUniqueRedirectsToQuery = "INSERT OR REPLACE INTO SubresourceUniqueRedirectsTo (subresourceDomainID, lastUpdated, toDomainID) SELECT ?, ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
+constexpr auto insertUnattributedPrivateClickMeasurementQuery = "INSERT OR REPLACE INTO UnattributedPrivateClickMeasurement (sourceSiteDomainID, destinationSiteDomainID, "
+ "sourceID, timeOfAdClick, token, signature, keyID) VALUES (?, ?, ?, ?, ?, ?, ?)"_s;
+constexpr auto insertAttributedPrivateClickMeasurementQuery = "INSERT OR REPLACE INTO AttributedPrivateClickMeasurement (sourceSiteDomainID, destinationSiteDomainID, "
+ "sourceID, attributionTriggerData, priority, timeOfAdClick, earliestTimeToSendToSource, token, signature, keyID, earliestTimeToSendToDestination) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"_s;
// EXISTS Queries
constexpr auto subframeUnderTopFrameDomainExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubframeUnderTopFrameDomains WHERE subFrameDomainID = ? "
@@ -116,6 +116,8 @@
constexpr auto updateGrandfatheredQuery = "UPDATE ObservedDomains SET grandfathered = ? WHERE registrableDomain = ?"_s;
constexpr auto updateIsScheduledForAllButCookieDataRemovalQuery = "UPDATE ObservedDomains SET isScheduledForAllButCookieDataRemoval = ? WHERE registrableDomain = ?"_s;
constexpr auto setUnattributedPrivateClickMeasurementAsExpiredQuery = "UPDATE UnattributedPrivateClickMeasurement SET timeOfAdClick = -1.0"_s;
+constexpr auto markReportAsSentToSourceQuery = "UPDATE AttributedPrivateClickMeasurement SET earliestTimeToSendToSource = null WHERE sourceSiteDomainID = ? AND destinationSiteDomainID = ?"_s;
+constexpr auto markReportAsSentToDestinationQuery = "UPDATE AttributedPrivateClickMeasurement SET earliestTimeToSendToDestination = null WHERE sourceSiteDomainID = ? AND destinationSiteDomainID = ?"_s;
// SELECT Queries
constexpr auto domainIDFromStringQuery = "SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
@@ -135,9 +137,13 @@
"UNION ALL SELECT topFrameDomainID FROM SubresourceUnderTopFrameDomains WHERE subresourceDomainID = ?"
"UNION ALL SELECT toDomainID FROM SubresourceUniqueRedirectsTo WHERE subresourceDomainID = ?"_s;
constexpr auto allUnattributedPrivateClickMeasurementAttributionsQuery = "SELECT * FROM UnattributedPrivateClickMeasurement"_s;
-constexpr auto allAttributedPrivateClickMeasurementQuery = "SELECT * FROM AttributedPrivateClickMeasurement ORDER BY earliestTimeToSendToSource"_s;
+constexpr auto allAttributedPrivateClickMeasurementQuery = "SELECT *, MIN(earliestTimeToSendToSource, earliestTimeToSendToDestination) as minVal "
+ "FROM AttributedPrivateClickMeasurement WHERE earliestTimeToSendToSource IS NOT NULL AND earliestTimeToSendToDestination IS NOT NULL "
+ "UNION ALL SELECT *, earliestTimeToSendToSource as minVal FROM AttributedPrivateClickMeasurement WHERE earliestTimeToSendToDestination IS NULL "
+ "UNION ALL SELECT *, earliestTimeToSendToDestination as minVal FROM AttributedPrivateClickMeasurement WHERE earliestTimeToSendToSource IS NULL ORDER BY minVal"_s;
constexpr auto findUnattributedQuery = "SELECT * FROM UnattributedPrivateClickMeasurement WHERE sourceSiteDomainID = ? AND destinationSiteDomainID = ?"_s;
constexpr auto findAttributedQuery = "SELECT * FROM AttributedPrivateClickMeasurement WHERE sourceSiteDomainID = ? AND destinationSiteDomainID = ?"_s;
+constexpr auto earliestTimesToSendQuery = "SELECT earliestTimeToSendToSource, earliestTimeToSendToDestination FROM AttributedPrivateClickMeasurement WHERE sourceSiteDomainID = ? AND destinationSiteDomainID = ?"_s;
// EXISTS for testing queries
constexpr auto linkDecorationExistsQuery = "SELECT EXISTS (SELECT * FROM TopFrameLinkDecorationsFrom WHERE toDomainID = ? OR fromDomainID = ?)"_s;
@@ -866,6 +872,9 @@
m_findAttributedStatement = nullptr;
m_updateAttributionsEarliestTimeToSendStatement = nullptr;
m_removeUnattributedStatement = nullptr;
+ m_markReportAsSentToSourceStatement = nullptr;
+ m_markReportAsSentToDestinationStatement = nullptr;
+ m_earliestTimesToSendStatement = nullptr;
}
bool ResourceLoadStatisticsDatabaseStore::insertObservedDomain(const ResourceLoadStatistics& loadStatistics)
@@ -3047,12 +3056,23 @@
if (attributionType == PrivateClickMeasurementAttributionType::Attributed) {
auto attributionTriggerData = statement->getColumnInt(3);
auto priority = statement->getColumnInt(4);
- auto earliestTimeToSend = statement->getColumnDouble(6);
-
+ auto sourceEarliestTimeToSendValue = statement->getColumnDouble(6);
+ auto destinationEarliestTimeToSendValue = statement->getColumnDouble(10);
+
if (attributionTriggerData != -1)
attribution.setAttribution(WebCore::PrivateClickMeasurement::AttributionTriggerData { static_cast<uint32_t>(attributionTriggerData), WebCore::PrivateClickMeasurement::Priority(priority) });
- attribution.setEarliestTimeToSend(WallTime::fromRawSeconds(earliestTimeToSend));
+ Optional<WallTime> sourceEarliestTimeToSend;
+ Optional<WallTime> destinationEarliestTimeToSend;
+
+ // A value of 0.0 indicates that the report has been sent to the respective site.
+ if (sourceEarliestTimeToSendValue > 0.0)
+ sourceEarliestTimeToSend = WallTime::fromRawSeconds(sourceEarliestTimeToSendValue);
+
+ if (destinationEarliestTimeToSendValue > 0.0)
+ destinationEarliestTimeToSend = WallTime::fromRawSeconds(destinationEarliestTimeToSendValue);
+
+ attribution.setTimesToSend({ sourceEarliestTimeToSend, destinationEarliestTimeToSend });
}
attribution.setSourceSecretToken({ token, signature, keyID });
@@ -3107,8 +3127,12 @@
if (attributionType == PrivateClickMeasurementAttributionType::Attributed) {
auto attributionTriggerData = attribution.attributionTriggerData() ? attribution.attributionTriggerData().value().data : -1;
auto priority = attribution.attributionTriggerData() ? attribution.attributionTriggerData().value().priority : -1;
- auto earliestTimeToSend = attribution.earliestTimeToSend() ? attribution.earliestTimeToSend().value().secondsSinceEpoch().value() : -1;
+ auto sourceEarliestTimeToSend = attribution.timesToSend().sourceEarliestTimeToSend ? attribution.timesToSend().sourceEarliestTimeToSend.value().secondsSinceEpoch().value() : -1;
+ auto destinationEarliestTimeToSend = attribution.timesToSend().destinationEarliestTimeToSend ? attribution.timesToSend().destinationEarliestTimeToSend.value().secondsSinceEpoch().value() : -1;
+ // We should never be inserting an attributed private click measurement value into the database without valid report times.
+ ASSERT(sourceEarliestTimeToSend != -1 && destinationEarliestTimeToSend != -1);
+
auto statement = SQLiteStatement(m_database, insertAttributedPrivateClickMeasurementQuery);
if (statement.prepare() != SQLITE_OK
|| statement.bindInt(1, *sourceData.second) != SQLITE_OK
@@ -3117,10 +3141,11 @@
|| statement.bindInt(4, attributionTriggerData) != SQLITE_OK
|| statement.bindInt(5, priority) != SQLITE_OK
|| statement.bindDouble(6, attribution.timeOfAdClick().secondsSinceEpoch().value()) != SQLITE_OK
- || statement.bindDouble(7, earliestTimeToSend) != SQLITE_OK
+ || statement.bindDouble(7, sourceEarliestTimeToSend) != SQLITE_OK
|| statement.bindText(8, sourceUnlinkableToken ? sourceUnlinkableToken->tokenBase64URL : emptyString()) != SQLITE_OK
|| statement.bindText(9, sourceUnlinkableToken ? sourceUnlinkableToken->signatureBase64URL : emptyString()) != SQLITE_OK
|| statement.bindText(10, sourceUnlinkableToken ? sourceUnlinkableToken->keyIDBase64URL : emptyString()) != SQLITE_OK
+ || statement.bindDouble(11, destinationEarliestTimeToSend) != SQLITE_OK
|| statement.step() != SQLITE_DONE) {
RELEASE_LOG_ERROR_IF_ALLOWED(m_sessionID, "%p - ResourceLoadStatisticsDatabaseStore::insertPrivateClickMeasurement insertAttributedPrivateClickMeasurementQuery, error message: %{private}s", this, m_database.lastErrorMsg());
ASSERT_NOT_REACHED();
@@ -3172,7 +3197,7 @@
}
}
-Optional<Seconds> ResourceLoadStatisticsDatabaseStore::attributePrivateClickMeasurement(const SourceSite& sourceSite, const AttributionDestinationSite& destinationSite, AttributionTriggerData&& attributionTriggerData)
+Optional<PrivateClickMeasurement::AttributionSecondsUntilSendData> ResourceLoadStatisticsDatabaseStore::attributePrivateClickMeasurement(const SourceSite& sourceSite, const AttributionDestinationSite& destinationSite, AttributionTriggerData&& attributionTriggerData)
{
// We should always clear expired clicks from the database before scheduling an attribution.
clearExpiredPrivateClickMeasurement();
@@ -3193,7 +3218,7 @@
debugBroadcastConsoleMessage(MessageSource::PrivateClickMeasurement, MessageLevel::Info, makeString("[Private Click Measurement] Got an attribution with attribution trigger data: '"_s, data, "' and priority: '"_s, priority, "'."_s));
}
- auto secondsUntilSend = Seconds::infinity();
+ PrivateClickMeasurement::AttributionSecondsUntilSendData secondsUntilSend { WTF::nullopt, WTF::nullopt };
auto attribution = findPrivateClickMeasurement(sourceSite, destinationSite);
auto& previouslyUnattributed = attribution.first;
@@ -3202,15 +3227,19 @@
if (previouslyUnattributed) {
// Always convert the pending attribution and remove it from the unattributed map.
removeUnattributed(*previouslyUnattributed);
- if (auto optionalSecondsUntilSend = previouslyUnattributed.value().attributeAndGetEarliestTimeToSend(WTFMove(attributionTriggerData))) {
- secondsUntilSend = *optionalSecondsUntilSend;
- ASSERT(secondsUntilSend != Seconds::infinity());
- if (UNLIKELY(debugModeEnabled())) {
- RELEASE_LOG_INFO(PrivateClickMeasurement, "Converted a stored ad click with attribution trigger data: %{public}u and priority: %{public}u.", data, priority);
- debugBroadcastConsoleMessage(MessageSource::PrivateClickMeasurement, MessageLevel::Info, makeString("[Private Click Measurement] Converted a stored ad click with attribution trigger data: '"_s, data, "' and priority: '"_s, priority, "'."_s));
- }
+ secondsUntilSend = previouslyUnattributed.value().attributeAndGetEarliestTimeToSend(WTFMove(attributionTriggerData));
+
+ // We should always have a valid secondsUntilSend value for a previouslyUnattributed value because there can be no previous attribution with a higher priority.
+ if (!secondsUntilSend.hasValidSecondsUntilSendValues()) {
+ ASSERT_NOT_REACHED();
+ return WTF::nullopt;
}
+ if (UNLIKELY(debugModeEnabled())) {
+ RELEASE_LOG_INFO(PrivateClickMeasurement, "Converted a stored ad click with attribution trigger data: %{public}u and priority: %{public}u.", data, priority);
+ debugBroadcastConsoleMessage(MessageSource::PrivateClickMeasurement, MessageLevel::Info, makeString("[Private Click Measurement] Converted a stored ad click with attribution trigger data: '"_s, data, "' and priority: '"_s, priority, "'."_s));
+ }
+
// If there is no previous attribution, or the new attribution has higher priority, insert/update the database.
if (!previouslyAttributed || previouslyUnattributed.value().hasHigherPriorityThan(*previouslyAttributed)) {
insertPrivateClickMeasurement(WTFMove(*previouslyUnattributed), PrivateClickMeasurementAttributionType::Attributed);
@@ -3221,13 +3250,15 @@
}
}
} else if (previouslyAttributed) {
- // If we have no new attribution, re-attribute the old one to respect the new priority.
- if (auto optionalSecondsUntilSend = previouslyAttributed.value().attributeAndGetEarliestTimeToSend(WTFMove(attributionTriggerData))) {
+ // If we have no new attribution, re-attribute the old one to respect the new priority, but only if this report has
+ // not been sent to the source or destination site yet.
+ if (!previouslyAttributed.value().hasPreviouslyBeenReported()) {
+ auto secondsUntilSend = previouslyAttributed.value().attributeAndGetEarliestTimeToSend(WTFMove(attributionTriggerData));
+ if (!secondsUntilSend.hasValidSecondsUntilSendValues())
+ return WTF::nullopt;
+
insertPrivateClickMeasurement(WTFMove(*previouslyAttributed), PrivateClickMeasurementAttributionType::Attributed);
- secondsUntilSend = *optionalSecondsUntilSend;
- ASSERT(secondsUntilSend != Seconds::infinity());
-
if (UNLIKELY(debugModeEnabled())) {
RELEASE_LOG_INFO(PrivateClickMeasurement, "Re-converted an ad click with a new one with attribution trigger data: %{public}u and priority: %{public}u because it had higher priority.", data, priority);
debugBroadcastConsoleMessage(MessageSource::PrivateClickMeasurement, MessageLevel::Info, makeString("[Private Click Measurement] Re-converted an ad click with a new one with attribution trigger data: '"_s, data, "' and priority: '"_s, priority, "'' because it had higher priority."_s));
@@ -3235,7 +3266,7 @@
}
}
- if (secondsUntilSend == Seconds::infinity())
+ if (!secondsUntilSend.hasValidSecondsUntilSendValues())
return WTF::nullopt;
return secondsUntilSend;
@@ -3398,14 +3429,97 @@
return builder.toString();
}
-void ResourceLoadStatisticsDatabaseStore::clearSentAttribution(WebCore::PrivateClickMeasurement&& attribution)
+std::pair<Optional<SourceEarliestTimeToSend>, Optional<DestinationEarliestTimeToSend>> ResourceLoadStatisticsDatabaseStore::earliestTimesToSend(const WebCore::PrivateClickMeasurement& attribution)
{
auto sourceSiteDomainID = domainID(attribution.sourceSite().registrableDomain);
auto destinationSiteDomainID = domainID(attribution.destinationSite().registrableDomain);
if (!sourceSiteDomainID || !destinationSiteDomainID)
+ return std::make_pair(WTF::nullopt, WTF::nullopt);
+
+ auto scopedStatement = this->scopedStatement(m_earliestTimesToSendStatement, earliestTimesToSendQuery, "earliestTimesToSend"_s);
+
+ if (!scopedStatement
+ || scopedStatement->bindInt(1, *sourceSiteDomainID) != SQLITE_OK
+ || scopedStatement->bindInt(2, *destinationSiteDomainID) != SQLITE_OK
+ || scopedStatement->step() != SQLITE_ROW) {
+ RELEASE_LOG_ERROR(Network, "ResourceLoadStatisticsDatabaseStore::earliestTimesToSend, error message: %" PUBLIC_LOG_STRING, m_database.lastErrorMsg());
+ ASSERT_NOT_REACHED();
+ }
+
+ Optional<SourceEarliestTimeToSend> earliestTimeToSendToSource;
+ Optional<DestinationEarliestTimeToSend> earliestTimeToSendToDestination;
+
+ // A value of 0.0 indicates that the report has been sent to the respective site.
+ if (scopedStatement->getColumnDouble(0) > 0.0)
+ earliestTimeToSendToSource = scopedStatement->getColumnDouble(0);
+
+ if (scopedStatement->getColumnDouble(1) > 0.0)
+ earliestTimeToSendToDestination = scopedStatement->getColumnDouble(1);
+
+ return std::make_pair(earliestTimeToSendToSource, earliestTimeToSendToDestination);
+}
+
+void ResourceLoadStatisticsDatabaseStore::markReportAsSentToSource(SourceDomainID sourceSiteDomainID, DestinationDomainID destinationSiteDomainID)
+{
+ auto scopedStatement = this->scopedStatement(m_markReportAsSentToSourceStatement, markReportAsSentToSourceQuery, "markReportAsSentToSource"_s);
+
+ if (!scopedStatement
+ || scopedStatement->bindInt(1, sourceSiteDomainID) != SQLITE_OK
+ || scopedStatement->bindInt(2, destinationSiteDomainID) != SQLITE_OK
+ || scopedStatement->step() != SQLITE_DONE) {
+ RELEASE_LOG_ERROR(Network, "ResourceLoadStatisticsDatabaseStore::markReportAsSentToSource, error message: %" PUBLIC_LOG_STRING, m_database.lastErrorMsg());
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void ResourceLoadStatisticsDatabaseStore::markReportAsSentToDestination(SourceDomainID sourceSiteDomainID, DestinationDomainID destinationSiteDomainID)
+{
+ auto scopedStatement = this->scopedStatement(m_markReportAsSentToDestinationStatement, markReportAsSentToDestinationQuery, "markReportAsSentToDestination"_s);
+
+ if (!scopedStatement
+ || scopedStatement->bindInt(1, sourceSiteDomainID) != SQLITE_OK
+ || scopedStatement->bindInt(2, destinationSiteDomainID) != SQLITE_OK
+ || scopedStatement->step() != SQLITE_DONE) {
+ RELEASE_LOG_ERROR(Network, "ResourceLoadStatisticsDatabaseStore::markReportAsSentToDestination, error message: %" PUBLIC_LOG_STRING, m_database.lastErrorMsg());
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void ResourceLoadStatisticsDatabaseStore::clearSentAttribution(WebCore::PrivateClickMeasurement&& attribution, PrivateClickMeasurement::AttributionReportEndpoint attributionReportEndpoint)
+{
+ auto timesToSend = earliestTimesToSend(attribution);
+ auto sourceEarliestTimeToSend = timesToSend.first;
+ auto destinationEarliestTimeToSend = timesToSend.second;
+
+ auto sourceSiteDomainID = domainID(attribution.sourceSite().registrableDomain);
+ auto destinationSiteDomainID = domainID(attribution.destinationSite().registrableDomain);
+
+ if (!sourceSiteDomainID || !destinationSiteDomainID)
return;
+ switch (attributionReportEndpoint) {
+ case PrivateClickMeasurement::AttributionReportEndpoint::Source:
+ if (!sourceEarliestTimeToSend) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+ markReportAsSentToSource(*sourceSiteDomainID, *destinationSiteDomainID);
+ sourceEarliestTimeToSend = WTF::nullopt;
+ break;
+ case PrivateClickMeasurement::AttributionReportEndpoint::Destination:
+ if (!destinationEarliestTimeToSend) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+ markReportAsSentToDestination(*sourceSiteDomainID, *destinationSiteDomainID);
+ destinationEarliestTimeToSend = WTF::nullopt;
+ }
+
+ // Don't clear the attribute from the database unless it has been reported both to the source and destination site.
+ if (destinationEarliestTimeToSend || sourceEarliestTimeToSend)
+ return;
+
SQLiteStatement clearAttributedStatement(m_database, "DELETE FROM AttributedPrivateClickMeasurement WHERE sourceSiteDomainID = ? AND destinationSiteDomainID = ?"_s);
if (clearAttributedStatement.prepare() != SQLITE_OK
|| clearAttributedStatement.bindInt(1, *sourceSiteDomainID) != SQLITE_OK
@@ -3419,14 +3533,23 @@
void ResourceLoadStatisticsDatabaseStore::markAttributedPrivateClickMeasurementsAsExpiredForTesting()
{
auto expiredTimeToSend = WallTime::now() - 1_h;
-
- auto statement = SQLiteStatement(m_database, "UPDATE AttributedPrivateClickMeasurement SET earliestTimeToSendToSource = ?");
- if (statement.prepare() != SQLITE_OK
- || statement.bindInt(1, expiredTimeToSend.secondsSinceEpoch().value()) != SQLITE_OK
- || statement.step() != SQLITE_DONE) {
- RELEASE_LOG_ERROR_IF_ALLOWED(m_sessionID, "%p - ResourceLoadStatisticsDatabaseStore::insertPrivateClickMeasurement, error message: %{private}s", this, m_database.lastErrorMsg());
+
+ auto earliestTimeToSendToSourceStatement = SQLiteStatement(m_database, "UPDATE AttributedPrivateClickMeasurement SET earliestTimeToSendToSource = ?");
+ auto earliestTimeToSendToDestinationStatement = SQLiteStatement(m_database, "UPDATE AttributedPrivateClickMeasurement SET earliestTimeToSendToDestination = null");
+
+ if (earliestTimeToSendToSourceStatement.prepare() != SQLITE_OK
+ || earliestTimeToSendToSourceStatement.bindInt(1, expiredTimeToSend.secondsSinceEpoch().value()) != SQLITE_OK
+ || earliestTimeToSendToSourceStatement.step() != SQLITE_DONE) {
+ RELEASE_LOG_ERROR_IF_ALLOWED(m_sessionID, "%p - ResourceLoadStatisticsDatabaseStore::markAttributedPrivateClickMeasurementsAsExpiredForTesting, error message: %{private}s", this, m_database.lastErrorMsg());
ASSERT_NOT_REACHED();
}
+
+ if (earliestTimeToSendToDestinationStatement.prepare() != SQLITE_OK
+ || earliestTimeToSendToDestinationStatement.step() != SQLITE_DONE) {
+ RELEASE_LOG_ERROR_IF_ALLOWED(m_sessionID, "%p - ResourceLoadStatisticsDatabaseStore::markAttributedPrivateClickMeasurementsAsExpiredForTesting, error message: %{private}s", this, m_database.lastErrorMsg());
+ ASSERT_NOT_REACHED();
+ }
+
return;
}
Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h (275136 => 275137)
--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h 2021-03-27 18:52:51 UTC (rev 275137)
@@ -64,6 +64,10 @@
using ExpectedColumns = Vector<String>;
using ExistingColumnName = String;
using ExpectedColumnName = String;
+using SourceEarliestTimeToSend = double;
+using DestinationEarliestTimeToSend = double;
+using SourceDomainID = unsigned;
+using DestinationDomainID = unsigned;
// This is always constructed / used / destroyed on the WebResourceLoadStatisticsStore's statistics queue.
class ResourceLoadStatisticsDatabaseStore final : public ResourceLoadStatisticsStore {
@@ -138,12 +142,12 @@
// Private Click Measurement.
void insertPrivateClickMeasurement(WebCore::PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType) override;
void markAllUnattributedPrivateClickMeasurementAsExpiredForTesting() override;
- Optional<Seconds> attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&) override;
+ Optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData> attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&) override;
Vector<WebCore::PrivateClickMeasurement> allAttributedPrivateClickMeasurement() override;
void clearPrivateClickMeasurement(Optional<RegistrableDomain>) override;
void clearExpiredPrivateClickMeasurement() override;
String privateClickMeasurementToString() override;
- void clearSentAttribution(WebCore::PrivateClickMeasurement&&) override;
+ void clearSentAttribution(WebCore::PrivateClickMeasurement&&, WebCore::PrivateClickMeasurement::AttributionReportEndpoint) override;
void markAttributedPrivateClickMeasurementsAsExpiredForTesting() override;
Vector<String> columnsForTable(const String&);
@@ -193,6 +197,10 @@
Vector<RegistrableDomain> domainsWithUserInteractionAsFirstParty() const;
HashMap<TopFrameDomain, SubResourceDomain> domainsWithStorageAccess() const;
+ void markReportAsSentToDestination(SourceDomainID, DestinationDomainID);
+ void markReportAsSentToSource(SourceDomainID, DestinationDomainID);
+ std::pair<Optional<SourceEarliestTimeToSend>, Optional<DestinationEarliestTimeToSend>> earliestTimesToSend(const WebCore::PrivateClickMeasurement&);
+
struct DomainData {
unsigned domainID;
RegistrableDomain registrableDomain;
@@ -289,6 +297,7 @@
mutable std::unique_ptr<WebCore::SQLiteStatement> m_uniqueRedirectExistsStatement;
mutable std::unique_ptr<WebCore::SQLiteStatement> m_observedDomainsExistsStatement;
mutable std::unique_ptr<WebCore::SQLiteStatement> m_removeAllDataStatement;
+ mutable std::unique_ptr<WebCore::SQLiteStatement> m_earliestTimesToSendStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_insertUnattributedPrivateClickMeasurementStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_insertAttributedPrivateClickMeasurementStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_setUnattributedPrivateClickMeasurementAsExpiredStatement;
@@ -301,7 +310,9 @@
std::unique_ptr<WebCore::SQLiteStatement> m_findAttributedStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_updateAttributionsEarliestTimeToSendStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_removeUnattributedStatement;
-
+ std::unique_ptr<WebCore::SQLiteStatement> m_markReportAsSentToSourceStatement;
+ std::unique_ptr<WebCore::SQLiteStatement> m_markReportAsSentToDestinationStatement;
+
PAL::SessionID m_sessionID;
bool m_isNewResourceLoadStatisticsDatabaseFile { false };
unsigned m_operatingDatesSize { 0 };
Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.h (275136 => 275137)
--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.h 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.h 2021-03-27 18:52:51 UTC (rev 275137)
@@ -111,12 +111,12 @@
// Private Click Measurement is not implemented in the ITP memory store.
void insertPrivateClickMeasurement(WebCore::PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType) override { };
void markAllUnattributedPrivateClickMeasurementAsExpiredForTesting() override { };
- Optional<Seconds> attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&) override { return { }; };
+ Optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&) override { return { }; };
Vector<WebCore::PrivateClickMeasurement> allAttributedPrivateClickMeasurement() override { return { }; };
void clearPrivateClickMeasurement(Optional<RegistrableDomain>) override { };
void clearExpiredPrivateClickMeasurement() override { };
String privateClickMeasurementToString() override { return String(); };
- void clearSentAttribution(WebCore::PrivateClickMeasurement&&) override { };
+ void clearSentAttribution(WebCore::PrivateClickMeasurement&&, WebCore::PrivateClickMeasurement::AttributionReportEndpoint) override { };
void markAttributedPrivateClickMeasurementsAsExpiredForTesting() override { };
private:
Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h (275136 => 275137)
--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h 2021-03-27 18:52:51 UTC (rev 275137)
@@ -206,12 +206,12 @@
// Private Click Measurement.
virtual void insertPrivateClickMeasurement(WebCore::PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType) = 0;
virtual void markAllUnattributedPrivateClickMeasurementAsExpiredForTesting() = 0;
- virtual Optional<Seconds> attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&) = 0;
+ virtual Optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData> attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&) = 0;
virtual Vector<WebCore::PrivateClickMeasurement> allAttributedPrivateClickMeasurement() = 0;
virtual void clearPrivateClickMeasurement(Optional<RegistrableDomain>) = 0;
virtual void clearExpiredPrivateClickMeasurement() = 0;
virtual String privateClickMeasurementToString() = 0;
- virtual void clearSentAttribution(WebCore::PrivateClickMeasurement&&) = 0;
+ virtual void clearSentAttribution(WebCore::PrivateClickMeasurement&&, WebCore::PrivateClickMeasurement::AttributionReportEndpoint) = 0;
virtual void markAttributedPrivateClickMeasurementsAsExpiredForTesting() = 0;
protected:
Modified: trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp (275136 => 275137)
--- trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp 2021-03-27 18:52:51 UTC (rev 275137)
@@ -1522,7 +1522,7 @@
});
}
-void WebResourceLoadStatisticsStore::attributePrivateClickMeasurement(const PrivateClickMeasurement::SourceSite& sourceSite, const PrivateClickMeasurement::AttributionDestinationSite& destinationSite, PrivateClickMeasurement::AttributionTriggerData&& attributionTriggerData, CompletionHandler<void(Optional<Seconds>)>&& completionHandler)
+void WebResourceLoadStatisticsStore::attributePrivateClickMeasurement(const PrivateClickMeasurement::SourceSite& sourceSite, const PrivateClickMeasurement::AttributionDestinationSite& destinationSite, PrivateClickMeasurement::AttributionTriggerData&& attributionTriggerData, CompletionHandler<void(Optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
@@ -1639,7 +1639,7 @@
});
}
-void WebResourceLoadStatisticsStore::clearSentAttribution(WebCore::PrivateClickMeasurement&& attributionToClear)
+void WebResourceLoadStatisticsStore::clearSentAttribution(WebCore::PrivateClickMeasurement&& attributionToClear, PrivateClickMeasurement::AttributionReportEndpoint attributionReportEndpoint)
{
ASSERT(RunLoop::isMain());
@@ -1646,11 +1646,11 @@
if (isEphemeral())
return;
- postTask([this, attributionToClear = WTFMove(attributionToClear)]() mutable {
+ postTask([this, attributionToClear = WTFMove(attributionToClear), attributionReportEndpoint]() mutable {
if (!m_statisticsStore)
return;
- m_statisticsStore->clearSentAttribution(WTFMove(attributionToClear));
+ m_statisticsStore->clearSentAttribution(WTFMove(attributionToClear), attributionReportEndpoint);
});
}
Modified: trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h (275136 => 275137)
--- trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h 2021-03-27 18:52:51 UTC (rev 275137)
@@ -310,13 +310,13 @@
// Private Click Measurement.
void insertPrivateClickMeasurement(WebCore::PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType);
void markAllUnattributedPrivateClickMeasurementAsExpiredForTesting();
- void attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&, CompletionHandler<void(Optional<Seconds>)>&&);
+ void attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&, CompletionHandler<void(Optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>)>&&);
void allAttributedPrivateClickMeasurement(CompletionHandler<void(Vector<WebCore::PrivateClickMeasurement>&&)>&&);
void clearPrivateClickMeasurement();
void clearPrivateClickMeasurementForRegistrableDomain(const WebCore::RegistrableDomain&);
void clearExpiredPrivateClickMeasurement();
void privateClickMeasurementToString(CompletionHandler<void(String)>&&);
- void clearSentAttribution(WebCore::PrivateClickMeasurement&&);
+ void clearSentAttribution(WebCore::PrivateClickMeasurement&&, WebCore::PrivateClickMeasurement::AttributionReportEndpoint);
void markAttributedPrivateClickMeasurementsAsExpiredForTesting(CompletionHandler<void()>&&);
private:
Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurementManager.cpp (275136 => 275137)
--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurementManager.cpp 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurementManager.cpp 2021-03-27 18:52:51 UTC (rev 275137)
@@ -77,7 +77,7 @@
if (attribution.ephemeralSourceNonce()) {
auto attributionCopy = attribution;
- getTokenPublicKey(WTFMove(attributionCopy), [weakThis = makeWeakPtr(*this), this] (PrivateClickMeasurement&& attribution, const String& publicKeyBase64URL) {
+ getTokenPublicKey(WTFMove(attributionCopy), PrivateClickMeasurement::AttributionReportEndpoint::Source, [weakThis = makeWeakPtr(*this), this] (PrivateClickMeasurement&& attribution, const String& publicKeyBase64URL) {
if (!weakThis)
return;
@@ -144,7 +144,7 @@
return generateNetworkResourceLoadParameters(WTFMove(url), "GET"_s, nullptr, dataTypeCarried);
}
-void PrivateClickMeasurementManager::getTokenPublicKey(PrivateClickMeasurement&& attribution, Function<void(PrivateClickMeasurement&& attribution, const String& publicKeyBase64URL)>&& callback)
+void PrivateClickMeasurementManager::getTokenPublicKey(PrivateClickMeasurement&& attribution, PrivateClickMeasurement::AttributionReportEndpoint attributionReportEndpoint, Function<void(PrivateClickMeasurement&& attribution, const String& publicKeyBase64URL)>&& callback)
{
if (!featureEnabled())
return;
@@ -153,6 +153,8 @@
auto pcmDataCarried = PrivateClickMeasurement::PcmDataCarried::PersonallyIdentifiable;
auto tokenPublicKeyURL = attribution.tokenPublicKeyURL();
if (m_tokenPublicKeyURLForTesting) {
+ if (attributionReportEndpoint == PrivateClickMeasurement::AttributionReportEndpoint::Destination)
+ return;
tokenPublicKeyURL = *m_tokenPublicKeyURLForTesting;
pcmDataCarried = PrivateClickMeasurement::PcmDataCarried::NonPersonallyIdentifiable;
}
@@ -270,21 +272,30 @@
return;
if (auto* resourceLoadStatistics = m_networkSession->resourceLoadStatistics()) {
- resourceLoadStatistics->attributePrivateClickMeasurement(sourceSite, destinationSite, WTFMove(attributionTriggerData), [this, weakThis = makeWeakPtr(*this)] (auto optionalSecondsUntilSend) {
+ resourceLoadStatistics->attributePrivateClickMeasurement(sourceSite, destinationSite, WTFMove(attributionTriggerData), [this, weakThis = makeWeakPtr(*this)] (auto attributionSecondsUntilSendData) {
if (!weakThis)
return;
- if (optionalSecondsUntilSend) {
- auto secondsUntilSend = *optionalSecondsUntilSend;
- if (m_firePendingAttributionRequestsTimer.isActive() && m_firePendingAttributionRequestsTimer.nextFireInterval() < secondsUntilSend)
+
+ if (!attributionSecondsUntilSendData)
+ return;
+
+ if (attributionSecondsUntilSendData.value().hasValidSecondsUntilSendValues()) {
+ auto minSecondsUntilSend = attributionSecondsUntilSendData.value().minSecondsUntilSend();
+
+ ASSERT(minSecondsUntilSend);
+ if (!minSecondsUntilSend)
return;
+ if (m_firePendingAttributionRequestsTimer.isActive() && m_firePendingAttributionRequestsTimer.nextFireInterval() < *minSecondsUntilSend)
+ return;
+
if (UNLIKELY(debugModeEnabled())) {
- m_networkProcess->broadcastConsoleMessage(m_sessionID, MessageSource::PrivateClickMeasurement, MessageLevel::Log, makeString("[Private Click Measurement] Setting timer for firing attribution request to the debug mode timeout of "_s, debugModeSecondsUntilSend.seconds(), " seconds where the regular timeout would have been "_s, secondsUntilSend.seconds(), " seconds."_s));
- secondsUntilSend = debugModeSecondsUntilSend;
+ m_networkProcess->broadcastConsoleMessage(m_sessionID, MessageSource::PrivateClickMeasurement, MessageLevel::Log, makeString("[Private Click Measurement] Setting timer for firing attribution request to the debug mode timeout of "_s, debugModeSecondsUntilSend.seconds(), " seconds where the regular timeout would have been "_s, minSecondsUntilSend.value().seconds(), " seconds."_s));
+ minSecondsUntilSend = debugModeSecondsUntilSend;
} else
- m_networkProcess->broadcastConsoleMessage(m_sessionID, MessageSource::PrivateClickMeasurement, MessageLevel::Log, makeString("[Private Click Measurement] Setting timer for firing attribution request to the timeout of "_s, secondsUntilSend.seconds(), " seconds."_s));
+ m_networkProcess->broadcastConsoleMessage(m_sessionID, MessageSource::PrivateClickMeasurement, MessageLevel::Log, makeString("[Private Click Measurement] Setting timer for firing attribution request to the timeout of "_s, minSecondsUntilSend.value().seconds(), " seconds."_s));
- startTimer(secondsUntilSend);
+ startTimer(*minSecondsUntilSend);
}
});
}
@@ -291,18 +302,18 @@
#endif
}
-void PrivateClickMeasurementManager::fireConversionRequest(const PrivateClickMeasurement& attribution)
+void PrivateClickMeasurementManager::fireConversionRequest(const PrivateClickMeasurement& attribution, PrivateClickMeasurement::AttributionReportEndpoint attributionReportEndpoint)
{
if (!featureEnabled())
return;
if (!attribution.sourceUnlinkableToken()) {
- fireConversionRequestImpl(attribution);
+ fireConversionRequestImpl(attribution, attributionReportEndpoint);
return;
}
auto attributionCopy = attribution;
- getTokenPublicKey(WTFMove(attributionCopy), [weakThis = makeWeakPtr(*this), this] (PrivateClickMeasurement&& attribution, const String& publicKeyBase64URL) {
+ getTokenPublicKey(WTFMove(attributionCopy), attributionReportEndpoint, [weakThis = makeWeakPtr(*this), this, attributionReportEndpoint] (PrivateClickMeasurement&& attribution, const String& publicKeyBase64URL) {
if (!weakThis)
return;
@@ -318,13 +329,21 @@
if (keyID != attribution.sourceUnlinkableToken()->keyIDBase64URL)
return;
- fireConversionRequestImpl(attribution);
+ fireConversionRequestImpl(attribution, attributionReportEndpoint);
});
}
-void PrivateClickMeasurementManager::fireConversionRequestImpl(const PrivateClickMeasurement& attribution)
+void PrivateClickMeasurementManager::fireConversionRequestImpl(const PrivateClickMeasurement& attribution, PrivateClickMeasurement::AttributionReportEndpoint attributionReportEndpoint)
{
- auto attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportSourceURL : attribution.attributionReportSourceURL();
+ URL attributionURL;
+ switch (attributionReportEndpoint) {
+ case PrivateClickMeasurement::AttributionReportEndpoint::Source:
+ attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportSourceURL : attribution.attributionReportSourceURL();
+ break;
+ case PrivateClickMeasurement::AttributionReportEndpoint::Destination:
+ attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportAttributeOnURL : attribution.attributionReportAttributeOnURL();
+ }
+
if (attributionURL.isEmpty() || !attributionURL.isValid())
return;
@@ -343,7 +362,7 @@
});
}
-void PrivateClickMeasurementManager::clearSentAttribution(PrivateClickMeasurement&& sentConversion)
+void PrivateClickMeasurementManager::clearSentAttribution(PrivateClickMeasurement&& sentConversion, PrivateClickMeasurement::AttributionReportEndpoint attributionReportEndpoint)
{
#if ENABLE(RESOURCE_LOAD_STATISTICS)
if (!featureEnabled())
@@ -350,7 +369,7 @@
return;
if (auto* resourceLoadStatistics = m_networkSession->resourceLoadStatistics())
- resourceLoadStatistics->clearSentAttribution(WTFMove(sentConversion));
+ resourceLoadStatistics->clearSentAttribution(WTFMove(sentConversion), attributionReportEndpoint);
#endif
}
@@ -371,8 +390,10 @@
bool hasSentAttribution = false;
for (auto& attribution : attributions) {
- auto earliestTimeToSend = attribution.earliestTimeToSend();
- if (!earliestTimeToSend) {
+ Optional<WallTime> earliestTimeToSend = attribution.timesToSend().earliestTimeToSend();
+ Optional<WebCore::PrivateClickMeasurement::AttributionReportEndpoint> attributionReportEndpoint = attribution.timesToSend().attributionReportEndpoint();
+
+ if (!earliestTimeToSend || !attributionReportEndpoint) {
ASSERT_NOT_REACHED();
continue;
}
@@ -388,9 +409,15 @@
return;
}
- fireConversionRequest(attribution);
- clearSentAttribution(WTFMove(attribution));
+ auto laterTimeToSend = attribution.timesToSend().latestTimeToSend();
+ fireConversionRequest(attribution, *attributionReportEndpoint);
+ clearSentAttribution(WTFMove(attribution), *attributionReportEndpoint);
hasSentAttribution = true;
+
+ // Update nextTimeToFire in case the later report time for this attribution is sooner than the scheduled next time to fire.
+ if (laterTimeToSend)
+ nextTimeToFire = std::min(nextTimeToFire, laterTimeToSend.value().secondsSinceEpoch());
+
continue;
}
Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurementManager.h (275136 => 275137)
--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurementManager.h 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurementManager.h 2021-03-27 18:52:51 UTC (rev 275137)
@@ -70,12 +70,12 @@
void startTimer(Seconds);
private:
- void getTokenPublicKey(PrivateClickMeasurement&&, Function<void(PrivateClickMeasurement&& attribution, const String& publicKeyBase64URL)>&&);
+ void getTokenPublicKey(PrivateClickMeasurement&&, PrivateClickMeasurement::AttributionReportEndpoint, Function<void(PrivateClickMeasurement&& attribution, const String& publicKeyBase64URL)>&&);
void getSignedUnlinkableToken(PrivateClickMeasurement&&);
- void clearSentAttribution(PrivateClickMeasurement&&);
+ void clearSentAttribution(PrivateClickMeasurement&&, PrivateClickMeasurement::AttributionReportEndpoint);
void attribute(const SourceSite&, const AttributionDestinationSite&, AttributionTriggerData&&);
- void fireConversionRequest(const PrivateClickMeasurement&);
- void fireConversionRequestImpl(const PrivateClickMeasurement&);
+ void fireConversionRequest(const PrivateClickMeasurement&, PrivateClickMeasurement::AttributionReportEndpoint);
+ void fireConversionRequestImpl(const PrivateClickMeasurement&, PrivateClickMeasurement::AttributionReportEndpoint);
void firePendingAttributionRequests();
void clearExpired();
bool featureEnabled() const;
Modified: trunk/Tools/ChangeLog (275136 => 275137)
--- trunk/Tools/ChangeLog 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Tools/ChangeLog 2021-03-27 18:52:51 UTC (rev 275137)
@@ -1,3 +1,17 @@
+2021-03-27 Kate Cheney <[email protected]>
+
+ PCM: Send report to both click source and attribution destination website
+ https://bugs.webkit.org/show_bug.cgi?id=223615
+ <rdar://problem/75849443>
+
+ Reviewed by Brent Fulgham.
+
+ Update API tests to check for a valid time to send for both the source
+ and destination site.
+
+ * TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp:
+ (TestWebKitAPI::TEST):
+
2021-03-26 Lauro Moura <[email protected]>
REGRESSION(r275111) [GLIB] Fix build with new derived sources and forwarding headers scheme
Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp (275136 => 275137)
--- trunk/Tools/TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp 2021-03-27 18:31:30 UTC (rev 275136)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp 2021-03-27 18:52:51 UTC (rev 275137)
@@ -82,9 +82,10 @@
PrivateClickMeasurement attribution { PrivateClickMeasurement::SourceID(PrivateClickMeasurement::SourceID::MaxEntropy), PrivateClickMeasurement::SourceSite { webKitURL }, PrivateClickMeasurement::AttributionDestinationSite { exampleURL } };
auto now = WallTime::now();
attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(PrivateClickMeasurement::AttributionTriggerData::MaxEntropy, PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy)));
- auto earliestTimeToSend = attribution.earliestTimeToSend();
- ASSERT_TRUE(earliestTimeToSend);
- ASSERT_TRUE(earliestTimeToSend.value().secondsSinceEpoch() - 24_h >= now.secondsSinceEpoch());
+ auto earliestTimeToSend = attribution.timesToSend();
+ ASSERT_TRUE(earliestTimeToSend.sourceEarliestTimeToSend && earliestTimeToSend.destinationEarliestTimeToSend);
+ ASSERT_TRUE(earliestTimeToSend.sourceEarliestTimeToSend.value().secondsSinceEpoch() - 24_h >= now.secondsSinceEpoch());
+ ASSERT_TRUE(earliestTimeToSend.destinationEarliestTimeToSend.value().secondsSinceEpoch() - 24_h >= now.secondsSinceEpoch());
}
TEST(PrivateClickMeasurement, ValidConversionURLs)
@@ -182,7 +183,7 @@
ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
- ASSERT_FALSE(attribution.earliestTimeToSend());
+ ASSERT_FALSE(attribution.timesToSend().sourceEarliestTimeToSend && attribution.timesToSend().destinationEarliestTimeToSend);
}
TEST(PrivateClickMeasurement, InvalidConversionURLs)