Title: [250804] trunk
Revision
250804
Author
commit-qu...@webkit.org
Date
2019-10-07 16:17:28 -0700 (Mon, 07 Oct 2019)

Log Message

Domain relationships in the ITP Database should be inserted in a single query and ignore repeat insert attempts. (202604)
https://bugs.webkit.org/show_bug.cgi?id=202604
<rdar://problem/55995831>

Patch by Kate Cheney <katherine_che...@apple.com> on 2019-10-07
Source/WebKit:

Reviewed by Chris Dumez.

This patch addresses two clean-ups for the ITP SQLite Database Store.
First, by using INSERT OR IGNORE as opposed to INSERT, it eliminates
the need to check if a relationship already exists in the database
before inserting it. Second, instead of looping through domain lists
and inserting each relationship as a separate query, this patch now
converts lists to a string which SQLite can use to insert multiple
rows into a database using a single query.

Some Exists queries could not be deleted because they were being
used for testing.

* NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
(WebKit::ResourceLoadStatisticsDatabaseStore::ResourceLoadStatisticsDatabaseStore):
(WebKit::ResourceLoadStatisticsDatabaseStore::prepareStatements):
Removed old insert queries and replaced them with queries able to
insert string-lists of domains in a single query. Also reorganized
the queries by functionality to make them easier to find and edit.

(WebKit::ResourceLoadStatisticsDatabaseStore::createUniqueIndices):
(WebKit::ResourceLoadStatisticsDatabaseStore::createSchema):
In order to properly take advantage of the INSERT OR IGNORE
functionality, unique indices must be established so the SQLite table
is aware of what patterns to look for when ignoring a new insert.

(WebKit::ResourceLoadStatisticsDatabaseStore::ensureAndmakeDomainList):
(WebKit::ResourceLoadStatisticsDatabaseStore::insertDomainRelationshipList):
(WebKit::ResourceLoadStatisticsDatabaseStore::insertDomainRelationships):
Looping through the domain list is no longer needed with the new query
changes. Additionally, ensuring a domain is in the Observed Domains
table of the database must be done before utilizing any inserting of
relationships because the relationship queries rely on fetching the
topFrame domainID from the ObservedDomains table.

(WebKit::ResourceLoadStatisticsDatabaseStore::grantStorageAccess):
(WebKit::ResourceLoadStatisticsDatabaseStore::logFrameNavigation):
(WebKit::ResourceLoadStatisticsDatabaseStore::logCrossSiteLoadWithLinkDecoration):
(WebKit::ResourceLoadStatisticsDatabaseStore::setSubframeUnderTopFrameDomain):
(WebKit::ResourceLoadStatisticsDatabaseStore::setSubresourceUnderTopFrameDomain):
(WebKit::ResourceLoadStatisticsDatabaseStore::setSubresourceUniqueRedirectTo):
(WebKit::ResourceLoadStatisticsDatabaseStore::setSubresourceUniqueRedirectFrom):
(WebKit::ResourceLoadStatisticsDatabaseStore::setTopFrameUniqueRedirectTo):
(WebKit::ResourceLoadStatisticsDatabaseStore::setTopFrameUniqueRedirectFrom):
With the ensure check being done once in the ensureAndmakeDomainList
function, these functions no longer have to make that check before
inserting.
* NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h:

* NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
(WebKit::WebResourceLoadStatisticsStore::mergeStatisticForTesting):
(WebKit::WebResourceLoadStatisticsStore::isRelationshipOnlyInDatabaseOnce):
* NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
Changes in WeResourceLoadStatisticsStore were for testing only. This
patch updated the merge statistic testing to also test this change by
having a topFrameDomain list with more than one domain.

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::mergeStatisticForTesting):
(WebKit::NetworkProcess::isRelationshipOnlyInDatabaseOnce):
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcess.messages.in:
* UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
(WKWebsiteDataStoreSetStatisticsMergeStatistic):
(WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnce):
* UIProcess/API/C/WKWebsiteDataStoreRef.h:
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::mergeStatisticForTesting):
(WebKit::NetworkProcessProxy::isRelationshipOnlyInDatabaseOnce):
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::mergeStatisticForTesting):
(WebKit::WebsiteDataStore::isRelationshipOnlyInDatabaseOnce):
* UIProcess/WebsiteData/WebsiteDataStore.h:
Added a new function for testing that there are no repeat inserts
into the database. Updated mergeStatistics to test the list-insert
functionality.

Tools:

Reviewed by Chris Dumez.

Updates to testing infrastructure to test successful list-merging of
top domains into the ITP SQLite database and test against repeat
inserts.
* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::setStatisticsMergeStatistic):
(WTR::TestRunner::isStatisticsHasHadUserInteraction):
(WTR::TestRunner::isStatisticsOnlyInDatabaseOnce):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::setStatisticsMergeStatistic):
(WTR::TestController::isStatisticsOnlyInDatabaseOnce):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):

LayoutTests:

Reviewed by Chris Dumez.

Edited the merge-statistics tests to also test the list-insertion
change made in this patch. Also added a new test to ensure repeat
inserts only result in one entry in the database.

* http/tests/resourceLoadStatistics/many-inserts-only-insert-once-expected.txt: Added.
* http/tests/resourceLoadStatistics/many-inserts-only-insert-once.html: Added.
* http/tests/resourceLoadStatistics/merge-statistic-does-not-overwrite-database.html:
* http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database-expected.txt:
* http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database.html:
* http/tests/resourceLoadStatistics/merge-statistic-does-partially-overwrite-database.html:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (250803 => 250804)


--- trunk/LayoutTests/ChangeLog	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/LayoutTests/ChangeLog	2019-10-07 23:17:28 UTC (rev 250804)
@@ -1,3 +1,22 @@
+2019-10-07  Kate Cheney  <katherine_che...@apple.com>
+
+        Domain relationships in the ITP Database should be inserted in a single query and ignore repeat insert attempts. (202604)
+        https://bugs.webkit.org/show_bug.cgi?id=202604
+        <rdar://problem/55995831>
+
+        Reviewed by Chris Dumez.
+
+        Edited the merge-statistics tests to also test the list-insertion
+        change made in this patch. Also added a new test to ensure repeat
+        inserts only result in one entry in the database.
+
+        * http/tests/resourceLoadStatistics/many-inserts-only-insert-once-expected.txt: Added.
+        * http/tests/resourceLoadStatistics/many-inserts-only-insert-once.html: Added.
+        * http/tests/resourceLoadStatistics/merge-statistic-does-not-overwrite-database.html:
+        * http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database-expected.txt:
+        * http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database.html:
+        * http/tests/resourceLoadStatistics/merge-statistic-does-partially-overwrite-database.html:
+
 2019-10-07  Sihui Liu  <sihui_...@apple.com>
 
         ASSERTION FAILED: m_transactionOperationsInProgressQueue.first() == &operation in IDBTransaction::operationCompletedOnClient

Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/many-inserts-only-insert-once-expected.txt (0 => 250804)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/many-inserts-only-insert-once-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/many-inserts-only-insert-once-expected.txt	2019-10-07 23:17:28 UTC (rev 250804)
@@ -0,0 +1,11 @@
+Tests that many inserts into the database only result in one row.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS url set as subframe
+PASS Relationships are only in the database once.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/many-inserts-only-insert-once.html (0 => 250804)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/many-inserts-only-insert-once.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/many-inserts-only-insert-once.html	2019-10-07 23:17:28 UTC (rev 250804)
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+        <title>Tests that many inserts into the database only result in one row.</title>
+    <script src=""
+    <script src=""
+</head>
+<body>
+<script>
+    testRunner.setUseITPDatabase(true);
+    description("Tests that many inserts into the database only result in one row.");
+    jsTestIsAsync = true;
+
+    const topFrameUrl = "http://127.0.0.1:8000/temp";
+    const subframeUrl = "http://127.0.1.1:8000/temp";
+
+    function checkClassificationAndContinue() {
+        if (!testRunner.isStatisticsRegisteredAsSubFrameUnder(subframeUrl, topFrameUrl)) {
+            testFailed("url did not get set as subframe.");
+            setEnableFeature(false, finishJSTest);
+        } else if (!testRunner.isStatisticsRegisteredAsRedirectingTo(subframeUrl, topFrameUrl)) {
+            testFailed("url did not get set as redirecting to top frame.");
+            setEnableFeature(false, finishJSTest);
+        } else if (!testRunner.isStatisticsRegisteredAsSubresourceUnder(subframeUrl, topFrameUrl)) {
+            testFailed("url did not get set as subresource.");
+            setEnableFeature(false, finishJSTest);
+        } else {
+            testPassed("url set as subframe");
+            checkCountStatistics();
+        }
+    }
+
+    function insertManyTimes(url) {
+        testRunner.setStatisticsSubframeUnderTopFrameOrigin(url, topFrameUrl);
+        testRunner.setStatisticsSubframeUnderTopFrameOrigin(url, topFrameUrl);
+        testRunner.setStatisticsSubframeUnderTopFrameOrigin(url, topFrameUrl);
+        testRunner.setStatisticsSubframeUnderTopFrameOrigin(url, topFrameUrl);
+        
+        testRunner.setStatisticsSubresourceUnderTopFrameOrigin(url, topFrameUrl);
+        testRunner.setStatisticsSubresourceUnderTopFrameOrigin(url, topFrameUrl);
+        testRunner.setStatisticsSubresourceUnderTopFrameOrigin(url, topFrameUrl);
+        testRunner.setStatisticsSubresourceUnderTopFrameOrigin(url, topFrameUrl);
+        
+        
+        testRunner.setStatisticsSubresourceUniqueRedirectTo(url, topFrameUrl);
+        testRunner.setStatisticsSubresourceUniqueRedirectTo(url, topFrameUrl);
+        testRunner.setStatisticsSubresourceUniqueRedirectTo(url, topFrameUrl);
+        testRunner.setStatisticsSubresourceUniqueRedirectTo(url, topFrameUrl);
+    }
+
+    function setUpStatisticsAndContinue() {
+        insertManyTimes(subframeUrl);
+        testRunner.installStatisticsDidScanDataRecordsCallback(checkClassificationAndContinue);
+        testRunner.statisticsProcessStatisticsAndDataRecords();
+    }
+
+    function checkCountStatistics(result) {
+        if (testRunner.isStatisticsOnlyInDatabaseOnce(subframeUrl, topFrameUrl))
+            testPassed("Relationships are only in the database once.");
+        else
+            testFailed("Relationships are in the database more than once.");
+
+        setEnableFeature(false, finishJSTest);
+    }
+
+    if (window.testRunner) {
+        setEnableFeature(true, function() {
+            testRunner.setStatisticsNotifyPagesWhenDataRecordsWereScanned(true);
+            setUpStatisticsAndContinue();
+        });
+    }
+</script>
+</body>
+</html>

Modified: trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-not-overwrite-database.html (250803 => 250804)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-not-overwrite-database.html	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-not-overwrite-database.html	2019-10-07 23:17:28 UTC (rev 250804)
@@ -16,7 +16,7 @@
     const newerTimestamp = olderTimestamp + 10;
                                       
     function insertSecondStatistic() {
-      testRunner.setStatisticsMergeStatistic(url, "", olderTimestamp, false, 99, false, false, false, 0, function() {
+      testRunner.setStatisticsMergeStatistic(url, "", "", olderTimestamp, false, 99, false, false, false, 0, function() {
           if (testRunner.isStatisticsPrevalentResource(url))
               testPassed("Host did not overwrite old prevalent resource value.");
           else
@@ -44,7 +44,7 @@
     }
     
     function runTestRunnerTest() {
-        testRunner.setStatisticsMergeStatistic(url, "", newerTimestamp, true, 100, true, true, true, 1, function() {
+        testRunner.setStatisticsMergeStatistic(url, "", "", newerTimestamp, true, 100, true, true, true, 1, function() {
             if (!testRunner.isStatisticsPrevalentResource(url))
                 testFailed("Host got set as prevalent resource.");
 

Modified: trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database-expected.txt (250803 => 250804)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database-expected.txt	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database-expected.txt	2019-10-07 23:17:28 UTC (rev 250804)
@@ -7,7 +7,8 @@
 PASS Host overwrote old very prevalent resource value.
 PASS Host overwrote old user interaction value.
 PASS Host overwrote old grandfathered value.
-PASS Host set as subframe under top frame.
+PASS Host set as subframe under top frame1.
+PASS Host set as subframe under top frame2.
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database.html (250803 => 250804)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database.html	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-overwrite-database.html	2019-10-07 23:17:28 UTC (rev 250804)
@@ -11,7 +11,8 @@
     jsTestIsAsync = true;
 
     const url = ""
-    const sampleTopFrameURL = "http://topFrameDomain:8000";
+    const sampleTopFrameURL1 = "http://topFrameDomain1:8000";
+    const sampleTopFrameURL2 = "http://topFrameDomain2:8000";
     const olderTimestamp = Math.round((new Date()).getTime() / 1000);
     const newerTimestamp = olderTimestamp + 10;
     const mostRecentUIToTriggerFirstPartyInteractionCount = newerTimestamp + 90000;
@@ -18,44 +19,50 @@
                                       
     function insertSecondStatistic() {
     
-        // set this resource as prevalent so it is in the statistics store
-        testRunner.setStatisticsPrevalentResource(sampleTopFrameURL, true, function() {
-        
-              testRunner.setStatisticsMergeStatistic(url, sampleTopFrameURL, newerTimestamp, true, mostRecentUIToTriggerFirstPartyInteractionCount, true, true, true, 1, function() {
-                  if (testRunner.isStatisticsPrevalentResource(url))
-                      testPassed("Host overwrote old prevalent resource value.");
-                  else
-                      testFailed("Host not set as prevalent resource.");
+        // set url1 as prevalent so it is in the statistics store
+        // set url2 having UI so it is in the statistics store
+        testRunner.setStatisticsPrevalentResource(sampleTopFrameURL1, true, function() {
+            testRunner.setStatisticsHasHadUserInteraction(sampleTopFrameURL2, true, function () {
+                  testRunner.setStatisticsMergeStatistic(url, sampleTopFrameURL1, sampleTopFrameURL2, newerTimestamp, true, mostRecentUIToTriggerFirstPartyInteractionCount, true, true, true, 1, function() {
+                      if (testRunner.isStatisticsPrevalentResource(url))
+                          testPassed("Host overwrote old prevalent resource value.");
+                      else
+                          testFailed("Host not set as prevalent resource.");
 
-                  if (testRunner.isStatisticsVeryPrevalentResource(url))
-                      testPassed("Host overwrote old very prevalent resource value.");
-                  else
-                      testFailed("Host not set as very prevalent resource.");
-                                                     
-                  if (testRunner.isStatisticsHasHadUserInteraction(url))
-                      testPassed("Host overwrote old user interaction value.");
-                  else
-                      testFailed("Host not logged for user interaction.");
-                   
-                  if (testRunner.isStatisticsGrandfathered(url))
-                      testPassed("Host overwrote old grandfathered value.");
-                  else
-                      testFailed("Host not set as grandfathered.");
+                      if (testRunner.isStatisticsVeryPrevalentResource(url))
+                          testPassed("Host overwrote old very prevalent resource value.");
+                      else
+                          testFailed("Host not set as very prevalent resource.");
+                                                         
+                      if (testRunner.isStatisticsHasHadUserInteraction(url))
+                          testPassed("Host overwrote old user interaction value.");
+                      else
+                          testFailed("Host not logged for user interaction.");
+                       
+                      if (testRunner.isStatisticsGrandfathered(url))
+                          testPassed("Host overwrote old grandfathered value.");
+                      else
+                          testFailed("Host not set as grandfathered.");
 
-                  if (testRunner.isStatisticsRegisteredAsSubFrameUnder(url, sampleTopFrameURL))
-                      testPassed("Host set as subframe under top frame.");
-                  else
-                      testFailed("Host not set as subframe under top frame.");
+                      if (testRunner.isStatisticsRegisteredAsSubFrameUnder(url, sampleTopFrameURL1))
+                          testPassed("Host set as subframe under top frame1.");
+                      else
+                          testFailed("Host not set as subframe under top frame1.");
+                      if (testRunner.isStatisticsRegisteredAsSubFrameUnder(url, sampleTopFrameURL2))
+                          testPassed("Host set as subframe under top frame2.");
+                      else
+                          testFailed("Host not set as subframe under top frame2.");
 
-                  testRunner.statisticsResetToConsistentState(function() {
-                      finishJSTest();
+                      testRunner.statisticsResetToConsistentState(function() {
+                          finishJSTest();
+                      });
                   });
-              });
-         });
+             });
+        });
     }
     
     function runTestRunnerTest() {
-        testRunner.setStatisticsMergeStatistic(url, "", olderTimestamp, false, 0, false, false, false, 0, function() {
+        testRunner.setStatisticsMergeStatistic(url, "", "", olderTimestamp, false, 0, false, false, false, 0, function() {
             if (testRunner.isStatisticsPrevalentResource(url))
                 testFailed("Host got set as prevalent resource.");
 

Modified: trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-partially-overwrite-database.html (250803 => 250804)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-partially-overwrite-database.html	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/merge-statistic-does-partially-overwrite-database.html	2019-10-07 23:17:28 UTC (rev 250804)
@@ -21,7 +21,7 @@
         // set this resource as prevalent so it is in the statistics store
         testRunner.setStatisticsPrevalentResource(sampleTopFrameURL, true, function() {
         
-              testRunner.setStatisticsMergeStatistic(url, sampleTopFrameURL, newerTimestamp, false, mostRecentUIToTriggerFirstPartyInteractionCount, true, true, true, 1, function() {
+              testRunner.setStatisticsMergeStatistic(url, sampleTopFrameURL, "", newerTimestamp, false, mostRecentUIToTriggerFirstPartyInteractionCount, true, true, true, 1, function() {
                   if (testRunner.isStatisticsPrevalentResource(url))
                       testPassed("Host overwrote old prevalent resource value.");
                   else
@@ -55,7 +55,7 @@
     }
     
     function runTestRunnerTest() {
-        testRunner.setStatisticsMergeStatistic(url, "", olderTimestamp, false, 0, false, false, false, 0, function() {
+        testRunner.setStatisticsMergeStatistic(url, "", "", olderTimestamp, false, 0, false, false, false, 0, function() {
             if (testRunner.isStatisticsPrevalentResource(url))
                 testFailed("Host got set as prevalent resource.");
 

Modified: trunk/Source/WebKit/ChangeLog (250803 => 250804)


--- trunk/Source/WebKit/ChangeLog	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/ChangeLog	2019-10-07 23:17:28 UTC (rev 250804)
@@ -1,3 +1,87 @@
+2019-10-07  Kate Cheney  <katherine_che...@apple.com>
+
+        Domain relationships in the ITP Database should be inserted in a single query and ignore repeat insert attempts. (202604)
+        https://bugs.webkit.org/show_bug.cgi?id=202604
+        <rdar://problem/55995831>
+
+        Reviewed by Chris Dumez. 
+        
+        This patch addresses two clean-ups for the ITP SQLite Database Store.
+        First, by using INSERT OR IGNORE as opposed to INSERT, it eliminates
+        the need to check if a relationship already exists in the database
+        before inserting it. Second, instead of looping through domain lists
+        and inserting each relationship as a separate query, this patch now
+        converts lists to a string which SQLite can use to insert multiple
+        rows into a database using a single query.
+
+        Some Exists queries could not be deleted because they were being
+        used for testing.
+
+        * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
+        (WebKit::ResourceLoadStatisticsDatabaseStore::ResourceLoadStatisticsDatabaseStore):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::prepareStatements):
+        Removed old insert queries and replaced them with queries able to
+        insert string-lists of domains in a single query. Also reorganized
+        the queries by functionality to make them easier to find and edit.
+
+        (WebKit::ResourceLoadStatisticsDatabaseStore::createUniqueIndices):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::createSchema):
+        In order to properly take advantage of the INSERT OR IGNORE
+        functionality, unique indices must be established so the SQLite table
+        is aware of what patterns to look for when ignoring a new insert.
+
+        (WebKit::ResourceLoadStatisticsDatabaseStore::ensureAndmakeDomainList):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::insertDomainRelationshipList):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::insertDomainRelationships):
+        Looping through the domain list is no longer needed with the new query
+        changes. Additionally, ensuring a domain is in the Observed Domains
+        table of the database must be done before utilizing any inserting of 
+        relationships because the relationship queries rely on fetching the
+        topFrame domainID from the ObservedDomains table.
+
+        (WebKit::ResourceLoadStatisticsDatabaseStore::grantStorageAccess):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::logFrameNavigation):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::logCrossSiteLoadWithLinkDecoration):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::setSubframeUnderTopFrameDomain):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::setSubresourceUnderTopFrameDomain):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::setSubresourceUniqueRedirectTo):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::setSubresourceUniqueRedirectFrom):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::setTopFrameUniqueRedirectTo):
+        (WebKit::ResourceLoadStatisticsDatabaseStore::setTopFrameUniqueRedirectFrom):
+        With the ensure check being done once in the ensureAndmakeDomainList
+        function, these functions no longer have to make that check before
+        inserting.
+        * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h:
+
+        * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::WebResourceLoadStatisticsStore::mergeStatisticForTesting):
+        (WebKit::WebResourceLoadStatisticsStore::isRelationshipOnlyInDatabaseOnce):
+        * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
+        Changes in WeResourceLoadStatisticsStore were for testing only. This
+        patch updated the merge statistic testing to also test this change by
+        having a topFrameDomain list with more than one domain.
+
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::mergeStatisticForTesting):
+        (WebKit::NetworkProcess::isRelationshipOnlyInDatabaseOnce):
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcess.messages.in:
+        * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
+        (WKWebsiteDataStoreSetStatisticsMergeStatistic):
+        (WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnce):
+        * UIProcess/API/C/WKWebsiteDataStoreRef.h:
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::mergeStatisticForTesting):
+        (WebKit::NetworkProcessProxy::isRelationshipOnlyInDatabaseOnce):
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::mergeStatisticForTesting):
+        (WebKit::WebsiteDataStore::isRelationshipOnlyInDatabaseOnce):
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+        Added a new function for testing that there are no repeat inserts
+        into the database. Updated mergeStatistics to test the list-insert
+        functionality.
+
 2019-10-07  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] Change signature of HostFunction to (JSGlobalObject*, CallFrame*)

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp (250803 => 250804)


--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -62,57 +62,56 @@
 #define RELEASE_LOG_ERROR_IF_ALLOWED(sessionID, fmt, ...)  ((void)0)
 #endif
 
+// COUNT Queries
 constexpr auto observedDomainCountQuery = "SELECT COUNT(*) FROM ObservedDomains"_s;
+constexpr auto countSubframeUnderTopFrameQuery = "SELECT COUNT(*) FROM SubframeUnderTopFrameDomains WHERE subFrameDomainID = ? AND topFrameDomainID = ?;"_s;
+constexpr auto countSubresourceUnderTopFrameQuery = "SELECT COUNT(*) FROM SubresourceUnderTopFrameDomains WHERE subresourceDomainID = ? AND topFrameDomainID = ?;"_s;
+constexpr auto countSubresourceUniqueRedirectsToQuery = "SELECT COUNT(*) FROM SubresourceUniqueRedirectsTo WHERE subresourceDomainID = ? AND toDomainID = ?;"_s;
+
+// INSERT Queries
 constexpr auto insertObservedDomainQuery = "INSERT INTO ObservedDomains (registrableDomain, lastSeen, hadUserInteraction,"
     "mostRecentUserInteractionTime, grandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, timesAccessedAsFirstPartyDueToUserInteraction,"
     "timesAccessedAsFirstPartyDueToStorageAccessAPI) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"_s;
 constexpr auto insertTopLevelDomainQuery = "INSERT INTO TopLevelDomains VALUES (?)"_s;
-constexpr auto domainIDFromStringQuery = "SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
-constexpr auto storageAccessUnderTopFrameDomainsQuery = "INSERT INTO StorageAccessUnderTopFrameDomains (domainID, topLevelDomainID) "
-    "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain == ?"_s;
-constexpr auto storageAccessUnderTopFrameDomainsExistsQuery = "SELECT EXISTS (SELECT 1 FROM StorageAccessUnderTopFrameDomains WHERE domainID = ? "
-"AND topLevelDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
-constexpr auto topFrameUniqueRedirectsToQuery = "INSERT INTO TopFrameUniqueRedirectsTo (sourceDomainID, toDomainID) SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain == ?"_s;
-constexpr auto topFrameUniqueRedirectsToExistsQuery = "SELECT EXISTS (SELECT 1 FROM TopFrameUniqueRedirectsTo WHERE sourceDomainID = ? "
-    "AND toDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
-constexpr auto topFrameUniqueRedirectsFromQuery = "INSERT INTO TopFrameUniqueRedirectsFrom (targetDomainID, fromDomainID) "
-    "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
-constexpr auto topFrameUniqueRedirectsFromExistsQuery = "SELECT EXISTS (SELECT 1 FROM TopFrameUniqueRedirectsFrom WHERE targetDomainID = ? "
-    "AND fromDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
-constexpr auto topFrameLinkDecorationsFromQuery = "INSERT INTO TopFrameLinkDecorationsFrom (fromDomainID, toDomainID) "
-    "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
-constexpr auto topFrameLinkDecorationsFromExistsQuery = "SELECT EXISTS (SELECT 1 FROM TopFrameLinkDecorationsFrom WHERE fromDomainID = ? "
-    "AND toDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
-constexpr auto subframeUnderTopFrameDomainsQuery = "INSERT INTO SubframeUnderTopFrameDomains (subFrameDomainID, topFrameDomainID) "
-    "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
+constexpr auto storageAccessUnderTopFrameDomainsQuery = "INSERT OR IGNORE INTO StorageAccessUnderTopFrameDomains (domainID, topLevelDomainID) SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
+constexpr auto topFrameUniqueRedirectsToQuery = "INSERT OR IGNORE into TopFrameUniqueRedirectsTo (sourceDomainID, toDomainID) SELECT ?, domainID FROM ObservedDomains where registrableDomain in ( "_s;
+constexpr auto subframeUnderTopFrameDomainsQuery = "INSERT OR IGNORE into SubframeUnderTopFrameDomains (subFrameDomainID, topFrameDomainID) SELECT ?, domainID FROM ObservedDomains where registrableDomain in ( "_s;
+constexpr auto topFrameUniqueRedirectsFromQuery = "INSERT OR IGNORE INTO TopFrameUniqueRedirectsFrom (targetDomainID, fromDomainID) SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
+constexpr auto topFrameLinkDecorationsFromQuery = "INSERT OR IGNORE INTO TopFrameLinkDecorationsFrom (fromDomainID, toDomainID) SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
+constexpr auto subresourceUnderTopFrameDomainsQuery = "INSERT OR IGNORE INTO SubresourceUnderTopFrameDomains (subresourceDomainID, topFrameDomainID) SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain in ( "_s;
+constexpr auto subresourceUniqueRedirectsToQuery = "INSERT OR IGNORE INTO SubresourceUniqueRedirectsTo (subresourceDomainID, toDomainID) 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;
+
+// EXISTS Queries
 constexpr auto subframeUnderTopFrameDomainExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubframeUnderTopFrameDomains WHERE subFrameDomainID = ? "
     "AND topFrameDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
-constexpr auto subresourceUnderTopFrameDomainsQuery = "INSERT INTO SubresourceUnderTopFrameDomains (subresourceDomainID, topFrameDomainID) "
-    "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
 constexpr auto subresourceUnderTopFrameDomainExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubresourceUnderTopFrameDomains "
     "WHERE subresourceDomainID = ? AND topFrameDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
-constexpr auto subresourceUniqueRedirectsToQuery = "INSERT INTO SubresourceUniqueRedirectsTo (subresourceDomainID, toDomainID) "
-    "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain == ?"_s;
 constexpr auto subresourceUniqueRedirectsToExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubresourceUniqueRedirectsTo WHERE subresourceDomainID = ? "
     "AND toDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
-constexpr auto subresourceUniqueRedirectsFromQuery = "INSERT INTO SubresourceUniqueRedirectsFrom (subresourceDomainID, fromDomainID) "
-    "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain == ?"_s;
-constexpr auto subresourceUniqueRedirectsFromExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubresourceUniqueRedirectsFrom WHERE subresourceDomainID = ? "
-    "AND fromDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
+constexpr auto topFrameLinkDecorationsFromExistsQuery = "SELECT EXISTS (SELECT 1 FROM TopFrameLinkDecorationsFrom WHERE fromDomainID = ? "
+"AND toDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
+
+// UPDATE Queries
 constexpr auto mostRecentUserInteractionQuery = "UPDATE ObservedDomains SET hadUserInteraction = ?, mostRecentUserInteractionTime = ? "
     "WHERE registrableDomain = ?"_s;
 constexpr auto updateLastSeenQuery = "UPDATE ObservedDomains SET lastSeen = ? WHERE registrableDomain = ?"_s;
 constexpr auto updateDataRecordsRemovedQuery = "UPDATE ObservedDomains SET dataRecordsRemoved = ? WHERE registrableDomain = ?"_s;
 constexpr auto updatePrevalentResourceQuery = "UPDATE ObservedDomains SET isPrevalent = ? WHERE registrableDomain = ?"_s;
+constexpr auto updateVeryPrevalentResourceQuery = "UPDATE ObservedDomains SET isVeryPrevalent = ? WHERE registrableDomain = ?"_s;
+constexpr auto clearPrevalentResourceQuery = "UPDATE ObservedDomains SET isPrevalent = 0, isVeryPrevalent = 0 WHERE registrableDomain = ?"_s;
+constexpr auto updateGrandfatheredQuery = "UPDATE ObservedDomains SET grandfathered = ? WHERE registrableDomain = ?"_s;
+
+
+// SELECT Queries
+constexpr auto domainIDFromStringQuery = "SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
 constexpr auto isPrevalentResourceQuery = "SELECT isPrevalent FROM ObservedDomains WHERE registrableDomain = ?"_s;
-constexpr auto updateVeryPrevalentResourceQuery = "UPDATE ObservedDomains SET isVeryPrevalent = ? WHERE registrableDomain = ?"_s;
 constexpr auto isVeryPrevalentResourceQuery = "SELECT isVeryPrevalent FROM ObservedDomains WHERE registrableDomain = ?"_s;
-constexpr auto clearPrevalentResourceQuery = "UPDATE ObservedDomains SET isPrevalent = 0, isVeryPrevalent = 0 WHERE registrableDomain = ?"_s;
 constexpr auto hadUserInteractionQuery = "SELECT hadUserInteraction, mostRecentUserInteractionTime FROM ObservedDomains WHERE registrableDomain = ?"_s;
-constexpr auto updateGrandfatheredQuery = "UPDATE ObservedDomains SET grandfathered = ? WHERE registrableDomain = ?"_s;
 constexpr auto isGrandfatheredQuery = "SELECT grandfathered FROM ObservedDomains WHERE registrableDomain = ?"_s;
 constexpr auto findExpiredUserInteractionQuery = "SELECT domainID FROM ObservedDomains WHERE hadUserInteraction = 1 AND mostRecentUserInteractionTime < ?"_s;
 
+// CREATE TABLE Queries
 constexpr auto createObservedDomain = "CREATE TABLE ObservedDomains ("
     "domainID INTEGER PRIMARY KEY, registrableDomain TEXT NOT NULL UNIQUE ON CONFLICT FAIL, lastSeen REAL NOT NULL, "
     "hadUserInteraction INTEGER NOT NULL, mostRecentUserInteractionTime REAL NOT NULL, grandfathered INTEGER NOT NULL, "
@@ -176,6 +175,17 @@
     "subresourceDomainID INTEGER NOT NULL, fromDomainID INTEGER NOT NULL, "
     "FOREIGN KEY(subresourceDomainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE, "
     "FOREIGN KEY(fromDomainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE);"_s;
+
+// CREATE UNIQUE INDEX Queries
+constexpr auto createUniqueIndexStorageAccessUnderTopFrameDomains = "CREATE UNIQUE INDEX IF NOT EXISTS StorageAccessUnderTopFrameDomains_domainID_topLevelDomainID on StorageAccessUnderTopFrameDomains ( domainID, topLevelDomainID );"_s;
+constexpr auto createUniqueIndexTopFrameUniqueRedirectsTo = "CREATE UNIQUE INDEX IF NOT EXISTS TopFrameUniqueRedirectsTo_sourceDomainID_toDomainID on TopFrameUniqueRedirectsTo ( sourceDomainID, toDomainID );"_s;
+constexpr auto createUniqueIndexTopFrameUniqueRedirectsFrom = "CREATE UNIQUE INDEX IF NOT EXISTS TopFrameUniqueRedirectsFrom_targetDomainID_fromDomainID on TopFrameUniqueRedirectsFrom ( targetDomainID, fromDomainID );"_s;
+constexpr auto createUniqueIndexTopFrameLinkDecorationsFrom = "CREATE UNIQUE INDEX IF NOT EXISTS TopFrameLinkDecorationsFrom_fromDomainID_toDomainID on TopFrameLinkDecorationsFrom ( fromDomainID, toDomainID );"_s;
+constexpr auto createUniqueIndexSubframeUnderTopFrameDomains = "CREATE UNIQUE INDEX IF NOT EXISTS SubframeUnderTopFrameDomains_subFrameDomainID_topFrameDomainID on SubframeUnderTopFrameDomains ( subFrameDomainID, topFrameDomainID );"_s;
+constexpr auto createUniqueIndexSubresourceUnderTopFrameDomains = "CREATE UNIQUE INDEX IF NOT EXISTS SubresourceUnderTopFrameDomains_subresourceDomainID_topFrameDomainID on SubresourceUnderTopFrameDomains ( subresourceDomainID, topFrameDomainID );"_s;
+constexpr auto createUniqueIndexSubresourceUniqueRedirectsTo = "CREATE UNIQUE INDEX IF NOT EXISTS SubresourceUniqueRedirectsTo_subresourceDomainID_toDomainID on SubresourceUniqueRedirectsTo ( subresourceDomainID, toDomainID );"_s;
+constexpr auto createUniqueIndexSubresourceUniqueRedirectsFrom = "CREATE UNIQUE INDEX IF NOT EXISTS SubresourceUniqueRedirectsFrom_subresourceDomainID_fromDomainID on SubresourceUnderTopFrameDomains ( subresourceDomainID, fromDomainID );"_s;
+
     
 ResourceLoadStatisticsDatabaseStore::ResourceLoadStatisticsDatabaseStore(WebResourceLoadStatisticsStore& store, WorkQueue& workQueue, ShouldIncludeLocalhost shouldIncludeLocalhost, const String& storageDirectoryPath, PAL::SessionID sessionID)
     : ResourceLoadStatisticsStore(store, workQueue, shouldIncludeLocalhost)
@@ -184,22 +194,10 @@
     , m_insertObservedDomainStatement(m_database, insertObservedDomainQuery)
     , m_insertTopLevelDomainStatement(m_database, insertTopLevelDomainQuery)
     , m_domainIDFromStringStatement(m_database, domainIDFromStringQuery)
-    , m_storageAccessUnderTopFrameDomainsStatement(m_database, storageAccessUnderTopFrameDomainsQuery)
-    , m_storageAccessUnderTopFrameDomainsExistsStatement(m_database, storageAccessUnderTopFrameDomainsExistsQuery)
-    , m_topFrameUniqueRedirectsTo(m_database, topFrameUniqueRedirectsToQuery)
-    , m_topFrameUniqueRedirectsToExists(m_database, topFrameUniqueRedirectsToExistsQuery)
-    , m_topFrameUniqueRedirectsFrom(m_database, topFrameUniqueRedirectsFromQuery)
-    , m_topFrameUniqueRedirectsFromExists(m_database, topFrameUniqueRedirectsFromExistsQuery)
-    , m_topFrameLinkDecorationsFrom(m_database, topFrameLinkDecorationsFromQuery)
     , m_topFrameLinkDecorationsFromExists(m_database, topFrameLinkDecorationsFromExistsQuery)
-    , m_subframeUnderTopFrameDomains(m_database, subframeUnderTopFrameDomainsQuery)
     , m_subframeUnderTopFrameDomainExists(m_database, subframeUnderTopFrameDomainExistsQuery)
-    , m_subresourceUnderTopFrameDomains(m_database, subresourceUnderTopFrameDomainsQuery)
     , m_subresourceUnderTopFrameDomainExists(m_database, subresourceUnderTopFrameDomainExistsQuery)
-    , m_subresourceUniqueRedirectsTo(m_database, subresourceUniqueRedirectsToQuery)
     , m_subresourceUniqueRedirectsToExists(m_database, subresourceUniqueRedirectsToExistsQuery)
-    , m_subresourceUniqueRedirectsFrom(m_database, subresourceUniqueRedirectsFromQuery)
-    , m_subresourceUniqueRedirectsFromExists(m_database, subresourceUniqueRedirectsFromExistsQuery)
     , m_mostRecentUserInteractionStatement(m_database, mostRecentUserInteractionQuery)
     , m_updateLastSeenStatement(m_database, updateLastSeenQuery)
     , m_updateDataRecordsRemovedStatement(m_database, updateDataRecordsRemovedQuery)
@@ -262,6 +260,22 @@
     return result;
 }
 
+bool ResourceLoadStatisticsDatabaseStore::createUniqueIndices()
+{
+    if (!m_database.executeCommand(createUniqueIndexStorageAccessUnderTopFrameDomains)
+        || !m_database.executeCommand(createUniqueIndexTopFrameUniqueRedirectsTo)
+        || !m_database.executeCommand(createUniqueIndexTopFrameUniqueRedirectsFrom)
+        || !m_database.executeCommand(createUniqueIndexTopFrameLinkDecorationsFrom)
+        || !m_database.executeCommand(createUniqueIndexSubframeUnderTopFrameDomains)
+        || !m_database.executeCommand(createUniqueIndexSubresourceUnderTopFrameDomains)
+        || !m_database.executeCommand(createUniqueIndexSubresourceUniqueRedirectsTo)
+        || !m_database.executeCommand(createUniqueIndexSubresourceUnderTopFrameDomains)) {
+        RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::createUniqueIndices failed to execute, error message: %{public}s", this, m_database.lastErrorMsg());
+        return false;
+    }
+    return true;
+}
+
 bool ResourceLoadStatisticsDatabaseStore::createSchema()
 {
     ASSERT(!RunLoop::isMain());
@@ -315,6 +329,9 @@
         LOG_ERROR("Could not create SubresourceUniqueRedirectsFrom table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
         return false;
     }
+    
+    if (!createUniqueIndices())
+        return false;
 
     return true;
 }
@@ -327,20 +344,9 @@
         || m_insertObservedDomainStatement.prepare() != SQLITE_OK
         || m_insertTopLevelDomainStatement.prepare() != SQLITE_OK
         || m_domainIDFromStringStatement.prepare() != SQLITE_OK
-        || m_storageAccessUnderTopFrameDomainsStatement.prepare() != SQLITE_OK
-        || m_storageAccessUnderTopFrameDomainsExistsStatement.prepare() != SQLITE_OK
-        || m_topFrameUniqueRedirectsTo.prepare() != SQLITE_OK
-        || m_topFrameUniqueRedirectsToExists.prepare() != SQLITE_OK
-        || m_topFrameUniqueRedirectsFrom.prepare() != SQLITE_OK
-        || m_topFrameUniqueRedirectsFromExists.prepare() != SQLITE_OK
-        || m_subframeUnderTopFrameDomains.prepare() != SQLITE_OK
         || m_subframeUnderTopFrameDomainExists.prepare() != SQLITE_OK
-        || m_subresourceUnderTopFrameDomains.prepare() != SQLITE_OK
         || m_subresourceUnderTopFrameDomainExists.prepare() != SQLITE_OK
-        || m_subresourceUniqueRedirectsTo.prepare() != SQLITE_OK
         || m_subresourceUniqueRedirectsToExists.prepare() != SQLITE_OK
-        || m_subresourceUniqueRedirectsFrom.prepare() != SQLITE_OK
-        || m_subresourceUniqueRedirectsFromExists.prepare() != SQLITE_OK
         || m_updateLastSeenStatement.prepare() != SQLITE_OK
         || m_updateDataRecordsRemovedStatement.prepare() != SQLITE_OK
         || m_mostRecentUserInteractionStatement.prepare() != SQLITE_OK
@@ -353,7 +359,6 @@
         || m_updateGrandfatheredStatement.prepare() != SQLITE_OK
         || m_isGrandfatheredStatement.prepare() != SQLITE_OK
         || m_findExpiredUserInteractionStatement.prepare() != SQLITE_OK
-        || m_topFrameLinkDecorationsFrom.prepare() != SQLITE_OK
         || m_topFrameLinkDecorationsFromExists.prepare() != SQLITE_OK) {
         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::prepareStatements failed to prepare, error message: %{public}s", this, m_database.lastErrorMsg());
         ASSERT_NOT_REACHED();
@@ -465,6 +470,37 @@
     return domainID;
 }
 
+String ResourceLoadStatisticsDatabaseStore::ensureAndMakeDomainList(const HashSet<RegistrableDomain>& subframeUnderTopFrameDomains)
+{
+    StringBuilder builder;
+    
+    for (auto& topFrameResource : subframeUnderTopFrameDomains) {
+        
+        // Insert query will fail if top frame domain is not already in the database
+        ensureResourceStatisticsForRegistrableDomain(topFrameResource);
+        
+        if (!builder.isEmpty())
+            builder.appendLiteral(", ");
+        builder.append('"');
+        builder.append(topFrameResource.string());
+        builder.append('"');
+    }
+
+    return builder.toString();
+}
+
+void ResourceLoadStatisticsDatabaseStore::insertDomainRelationshipList(const String& statement, const HashSet<RegistrableDomain>& subframeUnderTopFrameDomains, unsigned domainID)
+{
+    SQLiteStatement insertRelationshipStatement(m_database, makeString(statement, ensureAndMakeDomainList(subframeUnderTopFrameDomains), " );"));
+    
+    if (insertRelationshipStatement.prepare() != SQLITE_OK
+        || insertRelationshipStatement.bindInt(1, domainID) != SQLITE_OK
+        || insertRelationshipStatement.step() != SQLITE_DONE) {
+        RELEASE_LOG_ERROR_IF_ALLOWED(m_sessionID, "%p - ResourceLoadStatisticsDatabaseStore::insertDomainRelationshipList failed, error message: %{private}s", this, m_database.lastErrorMsg());
+        ASSERT_NOT_REACHED();
+    }
+}
+
 void ResourceLoadStatisticsDatabaseStore::insertDomainRelationships(const ResourceLoadStatistics& loadStatistics)
 {
     ASSERT(!RunLoop::isMain());
@@ -472,41 +508,14 @@
     
     if (!registrableDomainID)
         return;
-    
-    for (auto& topFrameDomain : loadStatistics.storageAccessUnderTopFrameDomains) {
-        if (!relationshipExists(m_storageAccessUnderTopFrameDomainsExistsStatement, registrableDomainID.value(), topFrameDomain))
-            insertDomainRelationship(m_storageAccessUnderTopFrameDomainsStatement, registrableDomainID.value(), topFrameDomain);
-    }
 
-    for (auto& toDomain : loadStatistics.topFrameUniqueRedirectsTo) {
-        if (!relationshipExists(m_topFrameUniqueRedirectsToExists, registrableDomainID.value(), toDomain))
-            insertDomainRelationship(m_topFrameUniqueRedirectsTo, registrableDomainID.value(), toDomain);
-    }
-    
-    for (auto& fromDomain : loadStatistics.topFrameUniqueRedirectsFrom) {
-        if (!relationshipExists(m_topFrameUniqueRedirectsFromExists, registrableDomainID.value(), fromDomain))
-            insertDomainRelationship(m_topFrameUniqueRedirectsFrom, registrableDomainID.value(), fromDomain);
-    }
-    
-    for (auto& topFrameDomain : loadStatistics.subframeUnderTopFrameDomains) {
-        if (!relationshipExists(m_subframeUnderTopFrameDomainExists, registrableDomainID.value(), topFrameDomain))
-            insertDomainRelationship(m_subframeUnderTopFrameDomains, registrableDomainID.value(), topFrameDomain);
-    }
-    
-    for (auto& topFrameDomain : loadStatistics.subresourceUnderTopFrameDomains) {
-        if (!relationshipExists(m_subresourceUnderTopFrameDomainExists, registrableDomainID.value(), topFrameDomain))
-            insertDomainRelationship(m_subresourceUnderTopFrameDomains, registrableDomainID.value(), topFrameDomain);
-    }
-    
-    for (auto& toDomain : loadStatistics.subresourceUniqueRedirectsTo) {
-        if (!relationshipExists(m_subresourceUniqueRedirectsToExists, registrableDomainID.value(), toDomain))
-            insertDomainRelationship(m_subresourceUniqueRedirectsTo, registrableDomainID.value(), toDomain);
-    }
-    
-    for (auto& fromDomain : loadStatistics.subresourceUniqueRedirectsFrom) {
-        if (!relationshipExists(m_subresourceUniqueRedirectsFromExists, registrableDomainID.value(), fromDomain))
-            insertDomainRelationship(m_subresourceUniqueRedirectsFrom, registrableDomainID.value(), fromDomain);
-    }
+    insertDomainRelationshipList(storageAccessUnderTopFrameDomainsQuery, loadStatistics.storageAccessUnderTopFrameDomains, registrableDomainID.value());
+    insertDomainRelationshipList(topFrameUniqueRedirectsToQuery, loadStatistics.topFrameUniqueRedirectsTo, registrableDomainID.value());
+    insertDomainRelationshipList(topFrameUniqueRedirectsFromQuery, loadStatistics.topFrameUniqueRedirectsFrom, registrableDomainID.value());
+    insertDomainRelationshipList(subframeUnderTopFrameDomainsQuery, loadStatistics.subframeUnderTopFrameDomains, registrableDomainID.value());
+    insertDomainRelationshipList(subresourceUnderTopFrameDomainsQuery, loadStatistics.subresourceUnderTopFrameDomains, registrableDomainID.value());
+    insertDomainRelationshipList(subresourceUniqueRedirectsToQuery, loadStatistics.subresourceUniqueRedirectsTo, registrableDomainID.value());
+    insertDomainRelationshipList(subresourceUniqueRedirectsFromQuery, loadStatistics.subresourceUniqueRedirectsFrom, registrableDomainID.value());
 }
 
 void ResourceLoadStatisticsDatabaseStore::populateFromMemoryStore(const ResourceLoadStatisticsMemoryStore& memoryStore)
@@ -917,7 +926,7 @@
         auto subFrameStatus = ensureResourceStatisticsForRegistrableDomain(subFrameDomain);
         ASSERT(subFrameStatus.first == AddedRecord::No);
         ASSERT(hasHadUserInteraction(subFrameDomain, OperatingDatesWindow::Long));
-        insertDomainRelationship(m_storageAccessUnderTopFrameDomainsStatement, subFrameStatus.second, topFrameDomain);
+        insertDomainRelationshipList(storageAccessUnderTopFrameDomainsQuery, HashSet<RegistrableDomain>({ topFrameDomain }), subFrameStatus.second);
     }
 
     grantStorageAccessInternal(WTFMove(subFrameDomain), WTFMove(topFrameDomain), frameID, pageID, promptWasShown, WTFMove(completionHandler));
@@ -1002,11 +1011,8 @@
     if (!isMainFrame && !(areTargetAndTopFrameDomainsSameSite || areTargetAndSourceDomainsSameSite)) {
         auto targetResult = ensureResourceStatisticsForRegistrableDomain(targetDomain);
         updateLastSeen(targetDomain, ResourceLoadStatistics::reduceTimeResolution(WallTime::now()));
-        ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
-        if (!relationshipExists(m_subframeUnderTopFrameDomainExists, targetResult.second, topFrameDomain)) {
-            insertDomainRelationship(m_subframeUnderTopFrameDomains, targetResult.second, topFrameDomain);
-            statisticsWereUpdated = true;
-        }
+        insertDomainRelationshipList(subframeUnderTopFrameDomainsQuery, HashSet<RegistrableDomain>({ topFrameDomain }), targetResult.second);
+        statisticsWereUpdated = true;
     }
 
     if (isRedirect && !areTargetAndSourceDomainsSameSite) {
@@ -1013,29 +1019,15 @@
         if (isMainFrame) {
             auto redirectingDomainResult = ensureResourceStatisticsForRegistrableDomain(sourceDomain);
             auto targetResult = ensureResourceStatisticsForRegistrableDomain(targetDomain);
-
-            if (!relationshipExists(m_topFrameUniqueRedirectsToExists, redirectingDomainResult.second, targetDomain)) {
-                insertDomainRelationship(m_topFrameUniqueRedirectsTo, redirectingDomainResult.second, targetDomain);
-                statisticsWereUpdated = true;
-            }
-
-            if (!relationshipExists(m_topFrameUniqueRedirectsFromExists, targetResult.second, sourceDomain)) {
-                insertDomainRelationship(m_topFrameUniqueRedirectsFrom, targetResult.second, sourceDomain);
-                statisticsWereUpdated = true;
-            }
+            insertDomainRelationshipList(topFrameUniqueRedirectsToQuery, HashSet<RegistrableDomain>({ targetDomain }), redirectingDomainResult.second);
+            insertDomainRelationshipList(topFrameUniqueRedirectsFromQuery, HashSet<RegistrableDomain>({ sourceDomain }), targetResult.second);
         } else {
             auto redirectingDomainResult = ensureResourceStatisticsForRegistrableDomain(sourceDomain);
             auto targetResult = ensureResourceStatisticsForRegistrableDomain(targetDomain);
-            if (!relationshipExists(m_subresourceUniqueRedirectsToExists, redirectingDomainResult.second, targetDomain)) {
-                insertDomainRelationship(m_subresourceUniqueRedirectsTo, redirectingDomainResult.second, targetDomain);
-                statisticsWereUpdated = true;
-            }
-
-            if (!relationshipExists(m_subresourceUniqueRedirectsFromExists, targetResult.second, sourceDomain)) {
-                insertDomainRelationship(m_subresourceUniqueRedirectsFrom, targetResult.second, sourceDomain);
-                statisticsWereUpdated = true;
-            }
+            insertDomainRelationshipList(subresourceUniqueRedirectsToQuery, HashSet<RegistrableDomain>({ targetDomain }), redirectingDomainResult.second);
+            insertDomainRelationshipList(subresourceUniqueRedirectsFromQuery, HashSet<RegistrableDomain>({ sourceDomain }), targetResult.second);
         }
+        statisticsWereUpdated = true;
     }
 
     if (statisticsWereUpdated)
@@ -1048,11 +1040,8 @@
     ASSERT(fromDomain != toDomain);
 
     auto fromDomainResult = ensureResourceStatisticsForRegistrableDomain(fromDomain);
-
-    if (!relationshipExists(m_topFrameLinkDecorationsFromExists, fromDomainResult.second, toDomain)) {
-        insertDomainRelationship(m_topFrameLinkDecorationsFrom, fromDomainResult.second, toDomain);
-        scheduleStatisticsProcessingRequestIfNecessary();
-    }
+    insertDomainRelationshipList(topFrameLinkDecorationsFromQuery, HashSet<RegistrableDomain>({ toDomain }), fromDomainResult.second);
+    scheduleStatisticsProcessingRequestIfNecessary();
 }
 
 void ResourceLoadStatisticsDatabaseStore::setUserInteraction(const RegistrableDomain& domain, bool hadUserInteraction, WallTime mostRecentInteraction)
@@ -1299,9 +1288,7 @@
     auto result = ensureResourceStatisticsForRegistrableDomain(subFrameDomain);
 
     // For consistency, make sure we also have a statistics entry for the top frame domain.
-    ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
-
-    insertDomainRelationship(m_subframeUnderTopFrameDomains, result.second, topFrameDomain);
+    insertDomainRelationshipList(subframeUnderTopFrameDomainsQuery, HashSet<RegistrableDomain>({ topFrameDomain }), result.second);
 }
 
 void ResourceLoadStatisticsDatabaseStore::setSubresourceUnderTopFrameDomain(const SubResourceDomain& subresourceDomain, const TopFrameDomain& topFrameDomain)
@@ -1311,9 +1298,7 @@
     auto result = ensureResourceStatisticsForRegistrableDomain(subresourceDomain);
 
     // For consistency, make sure we also have a statistics entry for the top frame domain.
-    ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
-
-    insertDomainRelationship(m_subresourceUnderTopFrameDomains, result.second, topFrameDomain);
+    insertDomainRelationshipList(subresourceUnderTopFrameDomainsQuery, HashSet<RegistrableDomain>({ topFrameDomain }), result.second);
 }
 
 void ResourceLoadStatisticsDatabaseStore::setSubresourceUniqueRedirectTo(const SubResourceDomain& subresourceDomain, const RedirectDomain& redirectDomain)
@@ -1323,9 +1308,7 @@
     auto result = ensureResourceStatisticsForRegistrableDomain(subresourceDomain);
 
     // For consistency, make sure we also have a statistics entry for the redirect domain.
-    ensureResourceStatisticsForRegistrableDomain(redirectDomain);
-
-    insertDomainRelationship(m_subresourceUniqueRedirectsTo, result.second, redirectDomain);
+    insertDomainRelationshipList(subresourceUniqueRedirectsToQuery, HashSet<RegistrableDomain>({ redirectDomain }), result.second);
 }
 
 void ResourceLoadStatisticsDatabaseStore::setSubresourceUniqueRedirectFrom(const SubResourceDomain& subresourceDomain, const RedirectDomain& redirectDomain)
@@ -1335,9 +1318,7 @@
     auto result = ensureResourceStatisticsForRegistrableDomain(subresourceDomain);
 
     // For consistency, make sure we also have a statistics entry for the redirect domain.
-    ensureResourceStatisticsForRegistrableDomain(redirectDomain);
-
-    insertDomainRelationship(m_subresourceUniqueRedirectsFrom, result.second, redirectDomain);
+    insertDomainRelationshipList(subresourceUniqueRedirectsFromQuery, HashSet<RegistrableDomain>({ redirectDomain }), result.second);
 }
 
 void ResourceLoadStatisticsDatabaseStore::setTopFrameUniqueRedirectTo(const TopFrameDomain& topFrameDomain, const RedirectDomain& redirectDomain)
@@ -1347,9 +1328,7 @@
     auto result = ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
 
     // For consistency, make sure we also have a statistics entry for the redirect domain.
-    ensureResourceStatisticsForRegistrableDomain(redirectDomain);
-
-    insertDomainRelationship(m_topFrameUniqueRedirectsTo, result.second, redirectDomain);
+    insertDomainRelationshipList(topFrameUniqueRedirectsToQuery, HashSet<RegistrableDomain>({ redirectDomain }), result.second);
 }
 
 void ResourceLoadStatisticsDatabaseStore::setTopFrameUniqueRedirectFrom(const TopFrameDomain& topFrameDomain, const RedirectDomain& redirectDomain)
@@ -1359,9 +1338,7 @@
     auto result = ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
 
     // For consistency, make sure we also have a statistics entry for the redirect domain.
-    ensureResourceStatisticsForRegistrableDomain(redirectDomain);
-
-    insertDomainRelationship(m_topFrameUniqueRedirectsFrom, result.second, redirectDomain);
+    insertDomainRelationshipList(topFrameUniqueRedirectsFromQuery, HashSet<RegistrableDomain>({ redirectDomain }), result.second);
 }
 
 std::pair<ResourceLoadStatisticsDatabaseStore::AddedRecord, unsigned> ResourceLoadStatisticsDatabaseStore::ensureResourceStatisticsForRegistrableDomain(const RegistrableDomain& domain)
@@ -1758,6 +1735,40 @@
     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
 }
 
+bool ResourceLoadStatisticsDatabaseStore::isCorrectSubStatisticsCount(const RegistrableDomain& subframeDomain, const TopFrameDomain& topFrameDomain)
+{
+    SQLiteStatement subFrameUnderTopFrameCount(m_database, countSubframeUnderTopFrameQuery);
+    SQLiteStatement subresourceUnderTopFrameCount(m_database, countSubresourceUnderTopFrameQuery);
+    SQLiteStatement subresourceUniqueRedirectsTo(m_database, countSubresourceUniqueRedirectsToQuery);
+    
+    if (subFrameUnderTopFrameCount.prepare() != SQLITE_OK
+        || subresourceUnderTopFrameCount.prepare() != SQLITE_OK
+        || subresourceUniqueRedirectsTo.prepare() != SQLITE_OK) {
+        RELEASE_LOG_ERROR_IF_ALLOWED(m_sessionID, "%p - ResourceLoadStatisticsDatabaseStore::countSubStatisticsTesting failed to prepare, error message: %{private}s", this, m_database.lastErrorMsg());
+        ASSERT_NOT_REACHED();
+        return false;
+    }
+    
+    if (subFrameUnderTopFrameCount.bindInt(1, domainID(subframeDomain).value()) != SQLITE_OK
+        || subFrameUnderTopFrameCount.bindInt(2, domainID(topFrameDomain).value()) != SQLITE_OK
+        || subresourceUnderTopFrameCount.bindInt(1, domainID(subframeDomain).value()) != SQLITE_OK
+        || subresourceUnderTopFrameCount.bindInt(2, domainID(topFrameDomain).value()) != SQLITE_OK
+        || subresourceUniqueRedirectsTo.bindInt(1, domainID(subframeDomain).value()) != SQLITE_OK
+        || subresourceUniqueRedirectsTo.bindInt(2, domainID(topFrameDomain).value()) != SQLITE_OK) {
+        RELEASE_LOG_ERROR_IF_ALLOWED(m_sessionID, "%p - ResourceLoadStatisticsDatabaseStore::countSubStatisticsTesting failed to bind, error message: %{private}s", this, m_database.lastErrorMsg());
+        ASSERT_NOT_REACHED();
+        return false;
+    }
+    
+    if (subFrameUnderTopFrameCount.step() != SQLITE_ROW
+        || subresourceUnderTopFrameCount.step() != SQLITE_ROW
+        || subresourceUniqueRedirectsTo.step() != SQLITE_ROW)
+        return false;
+    
+    return (subFrameUnderTopFrameCount.getColumnInt(0) == 1 && subresourceUnderTopFrameCount.getColumnInt(0) == 1 && subresourceUniqueRedirectsTo.getColumnInt(0) == 1);
+}
+
+
 } // namespace WebKit
 
 #endif

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h (250803 => 250804)


--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h	2019-10-07 23:17:28 UTC (rev 250804)
@@ -103,6 +103,7 @@
     bool hasHadUserInteraction(const RegistrableDomain&, OperatingDatesWindow) override;
 
     void setLastSeen(const RegistrableDomain&, Seconds) override;
+    bool isCorrectSubStatisticsCount(const RegistrableDomain&, const TopFrameDomain&);
 
 private:
     void mergeStatistic(const ResourceLoadStatistics&);
@@ -110,6 +111,7 @@
     void clearDatabaseContents();
     bool insertObservedDomain(const ResourceLoadStatistics&);
     void insertDomainRelationships(const ResourceLoadStatistics&);
+    void insertDomainRelationshipList(const String&, const HashSet<RegistrableDomain>&, unsigned);
     bool insertDomainRelationship(WebCore::SQLiteStatement&, unsigned domainID, const RegistrableDomain& topFrameDomain);
     bool relationshipExists(WebCore::SQLiteStatement&, Optional<unsigned> firstDomainID, const RegistrableDomain& secondDomain) const;
     Optional<unsigned> domainID(const RegistrableDomain&) const;
@@ -166,8 +168,11 @@
     Vector<std::pair<RegistrableDomain, WebsiteDataToRemove>> registrableDomainsToRemoveWebsiteDataFor() override;
     bool isDatabaseStore() const final { return true; }
 
+    bool createUniqueIndices();
     bool createSchema();
     bool prepareStatements();
+    String ensureAndMakeDomainList(const HashSet<RegistrableDomain>&);
+
     
     const String m_storageDirectoryPath;
     mutable WebCore::SQLiteDatabase m_database;
@@ -175,22 +180,10 @@
     WebCore::SQLiteStatement m_insertObservedDomainStatement;
     WebCore::SQLiteStatement m_insertTopLevelDomainStatement;
     mutable WebCore::SQLiteStatement m_domainIDFromStringStatement;
-    WebCore::SQLiteStatement m_storageAccessUnderTopFrameDomainsStatement;
-    WebCore::SQLiteStatement m_storageAccessUnderTopFrameDomainsExistsStatement;
-    WebCore::SQLiteStatement m_topFrameUniqueRedirectsTo;
-    mutable WebCore::SQLiteStatement m_topFrameUniqueRedirectsToExists;
-    WebCore::SQLiteStatement m_topFrameUniqueRedirectsFrom;
-    mutable WebCore::SQLiteStatement m_topFrameUniqueRedirectsFromExists;
-    WebCore::SQLiteStatement m_topFrameLinkDecorationsFrom;
     mutable WebCore::SQLiteStatement m_topFrameLinkDecorationsFromExists;
-    WebCore::SQLiteStatement m_subframeUnderTopFrameDomains;
     mutable WebCore::SQLiteStatement m_subframeUnderTopFrameDomainExists;
-    WebCore::SQLiteStatement m_subresourceUnderTopFrameDomains;
     mutable WebCore::SQLiteStatement m_subresourceUnderTopFrameDomainExists;
-    WebCore::SQLiteStatement m_subresourceUniqueRedirectsTo;
     mutable WebCore::SQLiteStatement m_subresourceUniqueRedirectsToExists;
-    WebCore::SQLiteStatement m_subresourceUniqueRedirectsFrom;
-    mutable WebCore::SQLiteStatement m_subresourceUniqueRedirectsFromExists;
     WebCore::SQLiteStatement m_mostRecentUserInteractionStatement;
     WebCore::SQLiteStatement m_updateLastSeenStatement;
     mutable WebCore::SQLiteStatement m_updateDataRecordsRemovedStatement;

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp (250803 => 250804)


--- trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -596,11 +596,11 @@
     });
 }
 
-void WebResourceLoadStatisticsStore::mergeStatisticForTesting(const RegistrableDomain& domain, const RegistrableDomain& topFrameDomain, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
+void WebResourceLoadStatisticsStore::mergeStatisticForTesting(const RegistrableDomain& domain, const RegistrableDomain& topFrameDomain1, const RegistrableDomain& topFrameDomain2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
 {
     ASSERT(RunLoop::isMain());
 
-    postTask([this, domain = domain.isolatedCopy(), topFrameDomain = topFrameDomain.isolatedCopy(), lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, completionHandler = WTFMove(completionHandler)]() mutable {
+    postTask([this, domain = domain.isolatedCopy(), topFrameDomain1 = topFrameDomain1.isolatedCopy(), topFrameDomain2 = topFrameDomain2.isolatedCopy(), lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, completionHandler = WTFMove(completionHandler)]() mutable {
         if (m_statisticsStore) {
             ResourceLoadStatistics statistic(domain);
             statistic.lastSeen = WallTime::fromRawSeconds(lastSeen.seconds());
@@ -611,12 +611,16 @@
             statistic.isVeryPrevalentResource = isVeryPrevalent;
             statistic.dataRecordsRemoved = dataRecordsRemoved;
             
-            if (!topFrameDomain.isEmpty()) {
-                HashSet<RegistrableDomain> topFrameDomains;
-                topFrameDomains.add(topFrameDomain);
-                statistic.subframeUnderTopFrameDomains = WTFMove(topFrameDomains);
-            }
+            HashSet<RegistrableDomain> topFrameDomains;
+            
+            if (!topFrameDomain1.isEmpty())
+                topFrameDomains.add(topFrameDomain1);
+            
+            if (!topFrameDomain2.isEmpty())
+                topFrameDomains.add(topFrameDomain2);
 
+            statistic.subframeUnderTopFrameDomains = WTFMove(topFrameDomains);
+
             Vector<ResourceLoadStatistics> statistics;
             statistics.append(WTFMove(statistic));
             m_statisticsStore->mergeStatistics(WTFMove(statistics));
@@ -624,6 +628,24 @@
         postTaskReply(WTFMove(completionHandler));
     });
 }
+
+void WebResourceLoadStatisticsStore::isRelationshipOnlyInDatabaseOnce(const RegistrableDomain& subDomain, const RegistrableDomain& topDomain, CompletionHandler<void(bool)>&& completionHandler)
+{
+    ASSERT(RunLoop::isMain());
+
+    postTask([this, subDomain = subDomain.isolatedCopy(), topDomain = topDomain.isolatedCopy(), completionHandler = WTFMove(completionHandler)]() mutable {
+        if (!m_statisticsStore || !is<ResourceLoadStatisticsDatabaseStore>(*m_statisticsStore)) {
+            completionHandler(false);
+            return;
+        }
+        
+        bool isRelationshipOnlyInDatabaseOnce = downcast<ResourceLoadStatisticsDatabaseStore>(*m_statisticsStore).isCorrectSubStatisticsCount(subDomain, topDomain);
+        
+        postTaskReply([isRelationshipOnlyInDatabaseOnce, completionHandler = WTFMove(completionHandler)]() mutable {
+            completionHandler(isRelationshipOnlyInDatabaseOnce);
+        });
+    });
+}
     
 void WebResourceLoadStatisticsStore::setPrevalentResource(const RegistrableDomain& domain, CompletionHandler<void()>&& completionHandler)
 {

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h (250803 => 250804)


--- trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h	2019-10-07 23:17:28 UTC (rev 250804)
@@ -129,7 +129,8 @@
     bool hasStorageAccessForFrame(const SubFrameDomain&, const TopFrameDomain&, WebCore::FrameIdentifier, WebCore::PageIdentifier);
     void requestStorageAccess(const SubFrameDomain&, const TopFrameDomain&, WebCore::FrameIdentifier, WebCore::PageIdentifier, WebPageProxyIdentifier, CompletionHandler<void(StorageAccessWasGranted, StorageAccessPromptWasShown)>&&);
     void setLastSeen(const RegistrableDomain&, Seconds, CompletionHandler<void()>&&);
-    void mergeStatisticForTesting(const RegistrableDomain&, const TopFrameDomain&, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&&);
+    void mergeStatisticForTesting(const RegistrableDomain&, const TopFrameDomain& topFrameDomain1, const TopFrameDomain& topFrameDomain2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&&);
+    void isRelationshipOnlyInDatabaseOnce(const RegistrableDomain& subDomain, const RegistrableDomain& topDomain, CompletionHandler<void(bool)>&&);
     void setPrevalentResource(const RegistrableDomain&, CompletionHandler<void()>&&);
     void setVeryPrevalentResource(const RegistrableDomain&, CompletionHandler<void()>&&);
     void dumpResourceLoadStatistics(CompletionHandler<void(String)>&&);

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp (250803 => 250804)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -968,11 +968,11 @@
     }
 }
 
-void NetworkProcess::mergeStatisticForTesting(PAL::SessionID sessionID, const RegistrableDomain& domain, const RegistrableDomain& topFrameDomain, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
+void NetworkProcess::mergeStatisticForTesting(PAL::SessionID sessionID, const RegistrableDomain& domain, const RegistrableDomain& topFrameDomain1, const RegistrableDomain& topFrameDomain2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
 {
     if (auto* networkSession = this->networkSession(sessionID)) {
         if (auto* resourceLoadStatistics = networkSession->resourceLoadStatistics())
-            resourceLoadStatistics->mergeStatisticForTesting(domain, topFrameDomain, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, WTFMove(completionHandler));
+            resourceLoadStatistics->mergeStatisticForTesting(domain, topFrameDomain1, topFrameDomain2, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, WTFMove(completionHandler));
         else
             completionHandler();
     } else {
@@ -1026,6 +1026,19 @@
     }
 }
 
+void NetworkProcess::isRelationshipOnlyInDatabaseOnce(PAL::SessionID sessionID, const RegistrableDomain& subDomain, const RegistrableDomain& topDomain, CompletionHandler<void(bool)>&& completionHandler)
+{
+    if (auto* networkSession = this->networkSession(sessionID)) {
+        if (auto* resourceLoadStatistics = networkSession->resourceLoadStatistics())
+            resourceLoadStatistics->isRelationshipOnlyInDatabaseOnce(subDomain, topDomain, WTFMove(completionHandler));
+        else
+            completionHandler(false);
+    } else {
+        ASSERT_NOT_REACHED();
+        completionHandler(false);
+    }
+}
+
 void NetworkProcess::clearUserInteraction(PAL::SessionID sessionID, const RegistrableDomain& domain, CompletionHandler<void()>&& completionHandler)
 {
     if (auto* networkSession = this->networkSession(sessionID)) {

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.h (250803 => 250804)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2019-10-07 23:17:28 UTC (rev 250804)
@@ -228,6 +228,7 @@
     void setVeryPrevalentResource(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void()>&&);
     void setPruneEntriesDownTo(PAL::SessionID, uint64_t pruneTargetCount, CompletionHandler<void()>&&);
     void hadUserInteraction(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void(bool)>&&);
+    void isRelationshipOnlyInDatabaseOnce(PAL::SessionID, const RegistrableDomain& subDomain, const RegistrableDomain& topDomain, CompletionHandler<void(bool)>&&);
     void hasLocalStorage(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void(bool)>&&);
     void getAllStorageAccessEntries(PAL::SessionID, CompletionHandler<void(Vector<String> domains)>&&);
     void logFrameNavigation(PAL::SessionID, const NavigatedToDomain&, const TopFrameDomain&, const NavigatedFromDomain&, bool isRedirect, bool isMainFrame);
@@ -242,7 +243,7 @@
     void setCacheMaxAgeCapForPrevalentResources(PAL::SessionID, Seconds, CompletionHandler<void()>&&);
     void setGrandfatheringTime(PAL::SessionID, Seconds, CompletionHandler<void()>&&);
     void setLastSeen(PAL::SessionID, const RegistrableDomain&, Seconds, CompletionHandler<void()>&&);
-    void mergeStatisticForTesting(PAL::SessionID, const RegistrableDomain&, const TopFrameDomain&, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&&);
+    void mergeStatisticForTesting(PAL::SessionID, const RegistrableDomain&, const TopFrameDomain& topFrameDomain1, const TopFrameDomain& topFrameDomain2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&&);
     void setMinimumTimeBetweenDataRecordsRemoval(PAL::SessionID, Seconds, CompletionHandler<void()>&&);
     void setNotifyPagesWhenDataRecordsWereScanned(PAL::SessionID, bool value, CompletionHandler<void()>&&);
     void setIsRunningResourceLoadStatisticsTest(PAL::SessionID, bool value, CompletionHandler<void()>&&);

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in (250803 => 250804)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in	2019-10-07 23:17:28 UTC (rev 250804)
@@ -94,10 +94,11 @@
     IsVeryPrevalentResource(PAL::SessionID sessionID, WebCore::RegistrableDomain targetDomain) -> (bool isVeryPrevalent) Async
     SetAgeCapForClientSideCookies(PAL::SessionID sessionID, Optional<Seconds> seconds) -> () Async
     SetLastSeen(PAL::SessionID sessionID, WebCore::RegistrableDomain resourceDomain, Seconds seconds) -> () Async
-    MergeStatisticForTesting(PAL::SessionID sessionID, WebCore::RegistrableDomain resourceDomain, WebCore::RegistrableDomain topFrameDomain, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, uint64_t dataRecordsRemoved) -> () Async
+    MergeStatisticForTesting(PAL::SessionID sessionID, WebCore::RegistrableDomain resourceDomain, WebCore::RegistrableDomain topFrameDomain1, WebCore::RegistrableDomain topFrameDomain2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, uint64_t dataRecordsRemoved) -> () Async
     SetPrevalentResource(PAL::SessionID sessionID, WebCore::RegistrableDomain resourceDomain) -> () Async
     SetPrevalentResourceForDebugMode(PAL::SessionID sessionID, WebCore::RegistrableDomain resourceDomain) -> () Async
     HadUserInteraction(PAL::SessionID sessionID, WebCore::RegistrableDomain resourceDomain) -> (bool hadUserInteraction) Async
+    IsRelationshipOnlyInDatabaseOnce(PAL::SessionID sessionID, WebCore::RegistrableDomain subDomain, WebCore::RegistrableDomain topDomain) -> (bool hadUserInteraction) Async
     HasLocalStorage(PAL::SessionID sessionID, WebCore::RegistrableDomain resourceDomain) -> (bool hadUserInteraction) Async
     GetAllStorageAccessEntries(PAL::SessionID sessionID) -> (Vector<String> domains) Async
     IsRegisteredAsRedirectingTo(PAL::SessionID sessionID, WebCore::RegistrableDomain redirectedFromDomain, WebCore::RegistrableDomain redirectedToDomain) -> (bool isRedirectingTo) Async

Modified: trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp (250803 => 250804)


--- trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -119,10 +119,10 @@
 #endif
 }
 
-void WKWebsiteDataStoreSetStatisticsMergeStatistic(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, WKStringRef topFrameDomain, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, void* context, WKWebsiteDataStoreStatisticsMergeStatisticFunction completionHandler)
+void WKWebsiteDataStoreSetStatisticsMergeStatistic(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, WKStringRef topFrameDomain1, WKStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, void* context, WKWebsiteDataStoreStatisticsMergeStatisticFunction completionHandler)
 {
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
-    WebKit::toImpl(dataStoreRef)->mergeStatisticForTesting(URL(URL(), WebKit::toImpl(host)->string()), URL(URL(), WebKit::toImpl(topFrameDomain)->string()), Seconds { lastSeen }, hadUserInteraction, Seconds { mostRecentUserInteraction }, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, [context, completionHandler] {
+    WebKit::toImpl(dataStoreRef)->mergeStatisticForTesting(URL(URL(), WebKit::toImpl(host)->string()), URL(URL(), WebKit::toImpl(topFrameDomain1)->string()), URL(URL(), WebKit::toImpl(topFrameDomain2)->string()), Seconds { lastSeen }, hadUserInteraction, Seconds { mostRecentUserInteraction }, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, [context, completionHandler] {
         completionHandler(context);
     });
 #else
@@ -261,6 +261,17 @@
 #endif
 }
 
+void WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnce(WKWebsiteDataStoreRef dataStoreRef, WKStringRef subHost, WKStringRef topHost, void* context, WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnceFunction callback)
+{
+#if ENABLE(RESOURCE_LOAD_STATISTICS)
+    WebKit::toImpl(dataStoreRef)->isRelationshipOnlyInDatabaseOnce(URL(URL(), WebKit::toImpl(subHost)->string()), URL(URL(), WebKit::toImpl(topHost)->string()), [context, callback](bool onlyInDatabaseOnce) {
+        callback(onlyInDatabaseOnce, context);
+    });
+#else
+    callback(false, context);
+#endif
+}
+
 void WKWebsiteDataStoreSetStatisticsGrandfathered(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, bool value)
 {
 #if ENABLE(RESOURCE_LOAD_STATISTICS)

Modified: trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h (250803 => 250804)


--- trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h	2019-10-07 23:17:28 UTC (rev 250804)
@@ -50,7 +50,7 @@
 typedef void (*WKWebsiteDataStoreStatisticsLastSeenFunction)(void* functionContext);
 WK_EXPORT void WKWebsiteDataStoreSetStatisticsLastSeen(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, double seconds, void* context, WKWebsiteDataStoreStatisticsLastSeenFunction completionHandler);
 typedef void (*WKWebsiteDataStoreStatisticsMergeStatisticFunction)(void* functionContext);
-WK_EXPORT void WKWebsiteDataStoreSetStatisticsMergeStatistic(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, WKStringRef topFrameDomain, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, void* context, WKWebsiteDataStoreStatisticsMergeStatisticFunction completionHandler);
+WK_EXPORT void WKWebsiteDataStoreSetStatisticsMergeStatistic(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, WKStringRef topFrameDomain1, WKStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, void* context, WKWebsiteDataStoreStatisticsMergeStatisticFunction completionHandler);
 typedef void (*WKWebsiteDataStoreStatisticsPrevalentResourceFunction)(void* functionContext);
 WK_EXPORT void WKWebsiteDataStoreSetStatisticsPrevalentResource(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, bool value, void* context, WKWebsiteDataStoreStatisticsPrevalentResourceFunction completionHandler);
 typedef void (*WKWebsiteDataStoreStatisticsVeryPrevalentResourceFunction)(void* functionContext);
@@ -70,6 +70,8 @@
 WK_EXPORT void WKWebsiteDataStoreSetStatisticsHasHadUserInteraction(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, bool value, void* context, WKWebsiteDataStoreStatisticsHasHadUserInteractionFunction completionHandler);
 typedef void (*WKWebsiteDataStoreIsStatisticsHasHadUserInteractionFunction)(bool hasHadUserInteraction, void* functionContext);
 WK_EXPORT void WKWebsiteDataStoreIsStatisticsHasHadUserInteraction(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, void* context, WKWebsiteDataStoreIsStatisticsHasHadUserInteractionFunction callback);
+typedef void (*WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnceFunction)(bool onlyInDatabaseOnce, void* functionContext);
+WK_EXPORT void WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnce(WKWebsiteDataStoreRef dataStoreRef, WKStringRef subHost, WKStringRef topHost, void* context, WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnceFunction callback);
 WK_EXPORT void WKWebsiteDataStoreSetStatisticsGrandfathered(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, bool value);
 typedef void (*WKWebsiteDataStoreIsStatisticsGrandfatheredFunction)(bool isGrandfathered, void* functionContext);
 WK_EXPORT void WKWebsiteDataStoreIsStatisticsGrandfathered(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, void* context, WKWebsiteDataStoreIsStatisticsGrandfatheredFunction callback);

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp (250803 => 250804)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -536,9 +536,9 @@
     sendWithAsyncReply(Messages::NetworkProcess::SetLastSeen(sessionID, resourceDomain, lastSeen), WTFMove(completionHandler));
 }
 
-void NetworkProcessProxy::mergeStatisticForTesting(PAL::SessionID sessionID, const RegistrableDomain& resourceDomain, const RegistrableDomain& topFrameDomain, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
+void NetworkProcessProxy::mergeStatisticForTesting(PAL::SessionID sessionID, const RegistrableDomain& resourceDomain, const RegistrableDomain& topFrameDomain1, const RegistrableDomain& topFrameDomain2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
 {
-    sendWithAsyncReply(Messages::NetworkProcess::MergeStatisticForTesting(sessionID, resourceDomain, topFrameDomain, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved), WTFMove(completionHandler));
+    sendWithAsyncReply(Messages::NetworkProcess::MergeStatisticForTesting(sessionID, resourceDomain, topFrameDomain1, topFrameDomain2, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved), WTFMove(completionHandler));
 }
 
 void NetworkProcessProxy::clearPrevalentResource(PAL::SessionID sessionID, const RegistrableDomain& resourceDomain, CompletionHandler<void()>&& completionHandler)
@@ -601,6 +601,11 @@
     sendWithAsyncReply(Messages::NetworkProcess::HadUserInteraction(sessionID, resourceDomain), WTFMove(completionHandler));
 }
 
+void NetworkProcessProxy::isRelationshipOnlyInDatabaseOnce(PAL::SessionID sessionID, const RegistrableDomain& subDomain, const RegistrableDomain& topDomain, CompletionHandler<void(bool)>&& completionHandler)
+{
+    sendWithAsyncReply(Messages::NetworkProcess::IsRelationshipOnlyInDatabaseOnce(sessionID, subDomain, topDomain), WTFMove(completionHandler));
+}
+
 void NetworkProcessProxy::clearUserInteraction(PAL::SessionID sessionID, const RegistrableDomain& resourceDomain, CompletionHandler<void()>&& completionHandler)
 {
     if (!canSendMessage()) {

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h (250803 => 250804)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2019-10-07 23:17:28 UTC (rev 250804)
@@ -103,6 +103,7 @@
     void dumpResourceLoadStatistics(PAL::SessionID, CompletionHandler<void(String)>&&);
     void updatePrevalentDomainsToBlockCookiesFor(PAL::SessionID, const Vector<RegistrableDomain>&, CompletionHandler<void()>&&);
     void hasHadUserInteraction(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void(bool)>&&);
+    void isRelationshipOnlyInDatabaseOnce(PAL::SessionID, const RegistrableDomain& subDomain, const RegistrableDomain& topDomain, CompletionHandler<void(bool)>&&);
     void hasLocalStorage(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void(bool)>&&);
     void isGrandfathered(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void(bool)>&&);
     void isPrevalentResource(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void(bool)>&&);
@@ -113,7 +114,7 @@
     void logUserInteraction(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void()>&&);
     void scheduleStatisticsAndDataRecordsProcessing(PAL::SessionID, CompletionHandler<void()>&&);
     void setLastSeen(PAL::SessionID, const RegistrableDomain&, Seconds, CompletionHandler<void()>&&);
-    void mergeStatisticForTesting(PAL::SessionID, const RegistrableDomain&, const TopFrameDomain&, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&&);
+    void mergeStatisticForTesting(PAL::SessionID, const RegistrableDomain&, const TopFrameDomain& topFrameDomain1, const TopFrameDomain& topFrameDomain2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&&);
     void setAgeCapForClientSideCookies(PAL::SessionID, Optional<Seconds>, CompletionHandler<void()>&&);
     void setCacheMaxAgeCap(PAL::SessionID, Seconds, CompletionHandler<void()>&&);
     void setGrandfathered(PAL::SessionID, const RegistrableDomain&, bool isGrandfathered, CompletionHandler<void()>&&);

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp (250803 => 250804)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -1551,7 +1551,7 @@
     }
 }
 
-void WebsiteDataStore::mergeStatisticForTesting(const URL& url , const URL& topFrameUrl, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
+void WebsiteDataStore::mergeStatisticForTesting(const URL& url , const URL& topFrameUrl1, const URL& topFrameUrl2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
 {
     if (url.protocolIsAbout() || url.isEmpty()) {
         completionHandler();
@@ -1562,7 +1562,7 @@
 
     for (auto& processPool : processPools()) {
         if (auto* process = processPool->networkProcess())
-            process->mergeStatisticForTesting(m_sessionID, WebCore::RegistrableDomain { url }, WebCore::RegistrableDomain { topFrameUrl }, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
+            process->mergeStatisticForTesting(m_sessionID, WebCore::RegistrableDomain { url }, WebCore::RegistrableDomain { topFrameUrl1 }, WebCore::RegistrableDomain { topFrameUrl2 }, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
     }
 }
 
@@ -1646,6 +1646,24 @@
     }
 }
 
+void WebsiteDataStore::isRelationshipOnlyInDatabaseOnce(const URL& subUrl, const URL& topUrl, CompletionHandler<void(bool)>&& completionHandler)
+{
+    ASSERT(RunLoop::isMain());
+    
+    if (subUrl.protocolIsAbout() || subUrl.isEmpty() || topUrl.protocolIsAbout() || topUrl.isEmpty()) {
+        completionHandler(false);
+        return;
+    }
+    
+    for (auto& processPool : processPools()) {
+        if (auto* process = processPool->networkProcess()) {
+            process->isRelationshipOnlyInDatabaseOnce(m_sessionID, WebCore::RegistrableDomain { subUrl }, WebCore::RegistrableDomain { topUrl }, WTFMove(completionHandler));
+            ASSERT(processPools().size() == 1);
+            break;
+        }
+    }
+}
+
 void WebsiteDataStore::clearUserInteraction(const URL& url, CompletionHandler<void()>&& completionHandler)
 {
     ASSERT(RunLoop::isMain());

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h (250803 => 250804)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h	2019-10-07 23:17:28 UTC (rev 250804)
@@ -147,6 +147,7 @@
     void logUserInteraction(const URL&, CompletionHandler<void()>&&);
     void getAllStorageAccessEntries(WebPageProxyIdentifier, CompletionHandler<void(Vector<String>&& domains)>&&);
     void hasHadUserInteraction(const URL&, CompletionHandler<void(bool)>&&);
+    void isRelationshipOnlyInDatabaseOnce(const URL& subUrl, const URL& topUrl, CompletionHandler<void(bool)>&&);
     void isPrevalentResource(const URL&, CompletionHandler<void(bool)>&&);
     void isRegisteredAsRedirectingTo(const URL& hostRedirectedFrom, const URL& hostRedirectedTo, CompletionHandler<void(bool)>&&);
     void isRegisteredAsSubresourceUnder(const URL& subresource, const URL& topFrame, CompletionHandler<void(bool)>&&);
@@ -163,7 +164,7 @@
     void setUseITPDatabase(bool);
     void setGrandfatheringTime(Seconds, CompletionHandler<void()>&&);
     void setLastSeen(const URL&, Seconds, CompletionHandler<void()>&&);
-    void mergeStatisticForTesting(const URL&, const URL& topFrameUrl, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&&);
+    void mergeStatisticForTesting(const URL&, const URL& topFrameUrl1, const URL& topFrameUrl2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&&);
     void setNotifyPagesWhenDataRecordsWereScanned(bool, CompletionHandler<void()>&&);
     void setIsRunningResourceLoadStatisticsTest(bool, CompletionHandler<void()>&&);
     void setPruneEntriesDownTo(size_t, CompletionHandler<void()>&&);

Modified: trunk/Tools/ChangeLog (250803 => 250804)


--- trunk/Tools/ChangeLog	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Tools/ChangeLog	2019-10-07 23:17:28 UTC (rev 250804)
@@ -1,3 +1,27 @@
+2019-10-07  Kate Cheney  <katherine_che...@apple.com>
+
+        Domain relationships in the ITP Database should be inserted in a single query and ignore repeat insert attempts. (202604)
+        https://bugs.webkit.org/show_bug.cgi?id=202604
+        <rdar://problem/55995831>
+
+        Reviewed by Chris Dumez. 
+
+        Updates to testing infrastructure to test successful list-merging of
+        top domains into the ITP SQLite database and test against repeat
+        inserts.
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::setStatisticsMergeStatistic):
+        (WTR::TestRunner::isStatisticsHasHadUserInteraction):
+        (WTR::TestRunner::isStatisticsOnlyInDatabaseOnce):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::setStatisticsMergeStatistic):
+        (WTR::TestController::isStatisticsOnlyInDatabaseOnce):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+
 2019-10-07  Matt Lewis  <jlew...@apple.com>
 
         Bring up queues for Catalina

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl (250803 => 250804)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl	2019-10-07 23:17:28 UTC (rev 250804)
@@ -298,7 +298,7 @@
     void setStatisticsDebugMode(boolean value, object completionHandler);
     void setStatisticsPrevalentResourceForDebugMode(DOMString hostName, object completionHandler);
     void setStatisticsLastSeen(DOMString hostName, double seconds, object completionHandler);
-    void setStatisticsMergeStatistic(DOMString hostName, DOMString topFrameDomain, double lastSeen, boolean hadUserInteraction, double mostRecentUserInteraction, boolean isGrandfathered, boolean isPrevalent, boolean isVeryPrevalent, unsigned long dataRecordsRemoved, object completionHandler);
+    void setStatisticsMergeStatistic(DOMString hostName, DOMString topFrameDomain1, DOMString topFrameDomain2, double lastSeen, boolean hadUserInteraction, double mostRecentUserInteraction, boolean isGrandfathered, boolean isPrevalent, boolean isVeryPrevalent, unsigned long dataRecordsRemoved, object completionHandler);
     void setStatisticsPrevalentResource(DOMString hostName, boolean value, object completionHandler);
     void setStatisticsVeryPrevalentResource(DOMString hostName, boolean value, object completionHandler);
     boolean isStatisticsPrevalentResource(DOMString hostName);
@@ -308,6 +308,7 @@
     boolean isStatisticsRegisteredAsRedirectingTo(DOMString hostRedirectedFrom, DOMString hostRedirectedTo);
     void setStatisticsHasHadUserInteraction(DOMString hostName, boolean value, object completionHandler);
     boolean isStatisticsHasHadUserInteraction(DOMString hostName);
+    boolean isStatisticsOnlyInDatabaseOnce(DOMString subHost, DOMString topHost);
     void setStatisticsGrandfathered(DOMString hostName, boolean value);
     boolean isStatisticsGrandfathered(DOMString hostName);
     void setUseITPDatabase(boolean value);

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp (250803 => 250804)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -1454,7 +1454,7 @@
     callTestRunnerCallback(SetStatisticsLastSeenCallbackID);
 }
 
-void TestRunner::setStatisticsMergeStatistic(JSStringRef hostName, JSStringRef topFrameDomain, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, JSValueRef completionHandler)
+void TestRunner::setStatisticsMergeStatistic(JSStringRef hostName, JSStringRef topFrameDomain1, JSStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, JSValueRef completionHandler)
 {
     cacheTestRunnerCallback(SetStatisticsMergeStatisticCallbackID, completionHandler);
 
@@ -1464,8 +1464,11 @@
     keys.append(adoptWK(WKStringCreateWithUTF8CString("HostName")));
     values.append(adoptWK(WKStringCreateWithJSString(hostName)));
     
-    keys.append(adoptWK(WKStringCreateWithUTF8CString("TopFrameDomain")));
-    values.append(adoptWK(WKStringCreateWithJSString(topFrameDomain)));
+    keys.append(adoptWK(WKStringCreateWithUTF8CString("TopFrameDomain1")));
+    values.append(adoptWK(WKStringCreateWithJSString(topFrameDomain1)));
+    
+    keys.append(adoptWK(WKStringCreateWithUTF8CString("TopFrameDomain2")));
+    values.append(adoptWK(WKStringCreateWithJSString(topFrameDomain2)));
 
     keys.append(adoptWK(WKStringCreateWithUTF8CString("LastSeen")));
     values.append(adoptWK(WKDoubleCreate(lastSeen)));
@@ -1724,6 +1727,34 @@
     return WKBooleanGetValue(adoptWK(static_cast<WKBooleanRef>(returnData)).get());
 }
 
+bool TestRunner::isStatisticsOnlyInDatabaseOnce(JSStringRef subHost, JSStringRef topHost)
+{
+    
+    Vector<WKRetainPtr<WKStringRef>> keys;
+    Vector<WKRetainPtr<WKTypeRef>> values;
+
+    keys.append(adoptWK(WKStringCreateWithUTF8CString("SubHost")));
+    values.append(adoptWK(WKStringCreateWithJSString(subHost)));
+    
+    keys.append(adoptWK(WKStringCreateWithUTF8CString("TopHost")));
+    values.append(adoptWK(WKStringCreateWithJSString(topHost)));
+    
+    Vector<WKStringRef> rawKeys(keys.size());
+    Vector<WKTypeRef> rawValues(values.size());
+
+    for (size_t i = 0; i < keys.size(); ++i) {
+        rawKeys[i] = keys[i].get();
+        rawValues[i] = values[i].get();
+    }
+
+    auto messageName = adoptWK(WKStringCreateWithUTF8CString("IsStatisticsOnlyInDatabaseOnce"));
+    auto messageBody = adoptWK(WKDictionaryCreate(rawKeys.data(), rawValues.data(), rawKeys.size()));
+    WKTypeRef returnData = nullptr;
+    WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get(), &returnData);
+    ASSERT(WKGetTypeID(returnData) == WKBooleanGetTypeID());
+    return WKBooleanGetValue(adoptWK(static_cast<WKBooleanRef>(returnData)).get());
+}
+
 void TestRunner::setStatisticsGrandfathered(JSStringRef hostName, bool value)
 {
     Vector<WKRetainPtr<WKStringRef>> keys;

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h (250803 => 250804)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h	2019-10-07 23:17:28 UTC (rev 250804)
@@ -396,7 +396,7 @@
     void statisticsCallDidSetPrevalentResourceForDebugModeCallback();
     void setStatisticsLastSeen(JSStringRef hostName, double seconds, JSValueRef completionHandler);
     void statisticsCallDidSetLastSeenCallback();
-    void setStatisticsMergeStatistic(JSStringRef hostName, JSStringRef topFrameDomain, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, JSValueRef completionHandler);
+    void setStatisticsMergeStatistic(JSStringRef hostName, JSStringRef topFrameDomain1, JSStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, JSValueRef completionHandler);
     void statisticsCallDidSetMergeStatisticCallback();
     void setStatisticsPrevalentResource(JSStringRef hostName, bool value, JSValueRef completionHandler);
     void statisticsCallDidSetPrevalentResourceCallback();
@@ -410,6 +410,7 @@
     void setStatisticsHasHadUserInteraction(JSStringRef hostName, bool value, JSValueRef completionHandler);
     void statisticsCallDidSetHasHadUserInteractionCallback();
     bool isStatisticsHasHadUserInteraction(JSStringRef hostName);
+    bool isStatisticsOnlyInDatabaseOnce(JSStringRef subHost, JSStringRef topHost);
     void setStatisticsGrandfathered(JSStringRef hostName, bool value);
     bool isStatisticsGrandfathered(JSStringRef hostName);
     void setUseITPDatabase(bool value);

Modified: trunk/Tools/WebKitTestRunner/TestController.cpp (250803 => 250804)


--- trunk/Tools/WebKitTestRunner/TestController.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Tools/WebKitTestRunner/TestController.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -3267,10 +3267,10 @@
     m_currentInvocation->didSetLastSeen();
 }
 
-void TestController::setStatisticsMergeStatistic(WKStringRef host, WKStringRef topFrameDomain, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, int dataRecordsRemoved)
+void TestController::setStatisticsMergeStatistic(WKStringRef host, WKStringRef topFrameDomain1, WKStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, int dataRecordsRemoved)
 {
     ResourceStatisticsCallbackContext context(*this);
-    WKWebsiteDataStoreSetStatisticsMergeStatistic(TestController::websiteDataStore(), host, topFrameDomain, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, &context, resourceStatisticsVoidResultCallback);
+    WKWebsiteDataStoreSetStatisticsMergeStatistic(TestController::websiteDataStore(), host, topFrameDomain1, topFrameDomain2, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, &context, resourceStatisticsVoidResultCallback);
     runUntil(context.done, noTimeout);
     m_currentInvocation->didMergeStatistic();
 }
@@ -3355,6 +3355,14 @@
     return context.result;
 }
 
+bool TestController::isStatisticsOnlyInDatabaseOnce(WKStringRef subHost, WKStringRef topHost)
+{
+    ResourceStatisticsCallbackContext context(*this);
+    WKWebsiteDataStoreIsStatisticsOnlyInDatabaseOnce(TestController::websiteDataStore(), subHost, topHost, &context, resourceStatisticsBooleanResultCallback);
+    runUntil(context.done, noTimeout);
+    return context.result;
+}
+
 void TestController::setStatisticsGrandfathered(WKStringRef host, bool value)
 {
     WKWebsiteDataStoreSetStatisticsGrandfathered(TestController::websiteDataStore(), host, value);

Modified: trunk/Tools/WebKitTestRunner/TestController.h (250803 => 250804)


--- trunk/Tools/WebKitTestRunner/TestController.h	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Tools/WebKitTestRunner/TestController.h	2019-10-07 23:17:28 UTC (rev 250804)
@@ -210,7 +210,7 @@
     void setStatisticsDebugMode(bool value);
     void setStatisticsPrevalentResourceForDebugMode(WKStringRef hostName);
     void setStatisticsLastSeen(WKStringRef hostName, double seconds);
-    void setStatisticsMergeStatistic(WKStringRef host, WKStringRef topFrameDomain, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, int dataRecordsRemoved);
+    void setStatisticsMergeStatistic(WKStringRef host, WKStringRef topFrameDomain1, WKStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, int dataRecordsRemoved);
     void setStatisticsPrevalentResource(WKStringRef hostName, bool value);
     void setStatisticsVeryPrevalentResource(WKStringRef hostName, bool value);
     String dumpResourceLoadStatistics();
@@ -221,6 +221,7 @@
     bool isStatisticsRegisteredAsRedirectingTo(WKStringRef hostRedirectedFrom, WKStringRef hostRedirectedTo);
     void setStatisticsHasHadUserInteraction(WKStringRef hostName, bool value);
     bool isStatisticsHasHadUserInteraction(WKStringRef hostName);
+    bool isStatisticsOnlyInDatabaseOnce(WKStringRef subHost, WKStringRef topHost);
     void setStatisticsGrandfathered(WKStringRef hostName, bool value);
     bool isStatisticsGrandfathered(WKStringRef hostName);
     void setUseITPDatabase(bool value);

Modified: trunk/Tools/WebKitTestRunner/TestInvocation.cpp (250803 => 250804)


--- trunk/Tools/WebKitTestRunner/TestInvocation.cpp	2019-10-07 23:13:45 UTC (rev 250803)
+++ trunk/Tools/WebKitTestRunner/TestInvocation.cpp	2019-10-07 23:17:28 UTC (rev 250804)
@@ -1105,7 +1105,8 @@
 
         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
         WKRetainPtr<WKStringRef> hostNameKey = adoptWK(WKStringCreateWithUTF8CString("HostName"));
-        WKRetainPtr<WKStringRef> topFrameDomainKey = adoptWK(WKStringCreateWithUTF8CString("TopFrameDomain"));
+        WKRetainPtr<WKStringRef> topFrameDomain1Key = adoptWK(WKStringCreateWithUTF8CString("TopFrameDomain1"));
+        WKRetainPtr<WKStringRef> topFrameDomain2Key = adoptWK(WKStringCreateWithUTF8CString("TopFrameDomain2"));
         WKRetainPtr<WKStringRef> lastSeenKey = adoptWK(WKStringCreateWithUTF8CString("LastSeen"));
         WKRetainPtr<WKStringRef> hadUserInteractionKey = adoptWK(WKStringCreateWithUTF8CString("HadUserInteraction"));
         WKRetainPtr<WKStringRef> mostRecentUserInteractionKey = adoptWK(WKStringCreateWithUTF8CString("MostRecentUserInteraction"));
@@ -1116,7 +1117,8 @@
         WKRetainPtr<WKStringRef> timesAccessedFirstPartyInteractionKey = adoptWK(WKStringCreateWithUTF8CString("TimesAccessedFirstPartyInteraction"));
 
         WKStringRef hostName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, hostNameKey.get()));
-        WKStringRef topFrameDomain = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, topFrameDomainKey.get()));
+        WKStringRef topFrameDomain1 = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, topFrameDomain1Key.get()));
+        WKStringRef topFrameDomain2 = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, topFrameDomain2Key.get()));
         WKDoubleRef lastSeen = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, lastSeenKey.get()));
         WKBooleanRef hadUserInteraction = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, hadUserInteractionKey.get()));
         WKDoubleRef mostRecentUserInteraction = static_cast<WKDoubleRef>(WKDictionaryGetItemForKey(messageBodyDictionary, mostRecentUserInteractionKey.get()));
@@ -1125,7 +1127,7 @@
         WKBooleanRef isVeryPrevalent = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, isVeryPrevalentKey.get()));
         WKUInt64Ref dataRecordsRemoved = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(messageBodyDictionary, dataRecordsRemovedKey.get()));
         
-        TestController::singleton().setStatisticsMergeStatistic(hostName, topFrameDomain, WKDoubleGetValue(lastSeen), WKBooleanGetValue(hadUserInteraction), WKDoubleGetValue(mostRecentUserInteraction), WKBooleanGetValue(isGrandfathered), WKBooleanGetValue(isPrevalent), WKBooleanGetValue(isVeryPrevalent), WKUInt64GetValue(dataRecordsRemoved));
+        TestController::singleton().setStatisticsMergeStatistic(hostName, topFrameDomain1, topFrameDomain2, WKDoubleGetValue(lastSeen), WKBooleanGetValue(hadUserInteraction), WKDoubleGetValue(mostRecentUserInteraction), WKBooleanGetValue(isGrandfathered), WKBooleanGetValue(isPrevalent), WKBooleanGetValue(isVeryPrevalent), WKUInt64GetValue(dataRecordsRemoved));
 
         return nullptr;
     }
@@ -1249,6 +1251,20 @@
         return result;
     }
     
+    if (WKStringIsEqualToUTF8CString(messageName, "IsStatisticsOnlyInDatabaseOnce")) {
+        ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+        
+        WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+        auto subHostKey = adoptWK(WKStringCreateWithUTF8CString("SubHost"));
+        auto topHostKey = adoptWK(WKStringCreateWithUTF8CString("TopHost"));
+        
+        WKStringRef subHost = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, subHostKey.get()));
+        WKStringRef topHost = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, topHostKey.get()));
+
+        bool statisticInDatabaseOnce = TestController::singleton().isStatisticsOnlyInDatabaseOnce(subHost, topHost);
+        return adoptWK(WKBooleanCreate(statisticInDatabaseOnce));
+    }
+    
     if (WKStringIsEqualToUTF8CString(messageName, "SetStatisticsGrandfathered")) {
         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
         
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to