Resource Load Statistics: Make it possible exclude localhost from classification
[WebKit-https.git] / Source / WebKit / NetworkProcess / Classifier / ResourceLoadStatisticsDatabaseStore.cpp
1 /*
2  * Copyright (C) 2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ResourceLoadStatisticsDatabaseStore.h"
28
29 #if ENABLE(RESOURCE_LOAD_STATISTICS)
30
31 #include "Logging.h"
32 #include "NetworkSession.h"
33 #include "PluginProcessManager.h"
34 #include "PluginProcessProxy.h"
35 #include "ResourceLoadStatisticsMemoryStore.h"
36 #include "StorageAccessStatus.h"
37 #include "WebProcessProxy.h"
38 #include "WebResourceLoadStatisticsTelemetry.h"
39 #include "WebsiteDataStore.h"
40 #include <WebCore/KeyedCoding.h>
41 #include <WebCore/NetworkStorageSession.h>
42 #include <WebCore/ResourceLoadStatistics.h>
43 #include <WebCore/SQLiteDatabase.h>
44 #include <WebCore/SQLiteStatement.h>
45 #include <wtf/CallbackAggregator.h>
46 #include <wtf/DateMath.h>
47 #include <wtf/HashMap.h>
48 #include <wtf/MathExtras.h>
49 #include <wtf/StdSet.h>
50 #include <wtf/text/StringBuilder.h>
51
52 namespace WebKit {
53 using namespace WebCore;
54
55 #if PLATFORM(COCOA)
56 #define RELEASE_LOG_IF_ALLOWED(sessionID, fmt, ...) RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - ResourceLoadStatisticsDatabaseStore::" fmt, this, ##__VA_ARGS__)
57 #define RELEASE_LOG_ERROR_IF_ALLOWED(sessionID, fmt, ...) RELEASE_LOG_ERROR_IF(sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - ResourceLoadStatisticsDatabaseStore::" fmt, this, ##__VA_ARGS__)
58 #else
59 #define RELEASE_LOG_IF_ALLOWED(sessionID, fmt, ...)  ((void)0)
60 #define RELEASE_LOG_ERROR_IF_ALLOWED(sessionID, fmt, ...)  ((void)0)
61 #endif
62
63 constexpr auto observedDomainCountQuery = "SELECT COUNT(*) FROM ObservedDomains"_s;
64 constexpr auto insertObservedDomainQuery = "INSERT INTO ObservedDomains (registrableDomain, lastSeen, hadUserInteraction,"
65     "mostRecentUserInteractionTime, grandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, timesAccessedAsFirstPartyDueToUserInteraction,"
66     "timesAccessedAsFirstPartyDueToStorageAccessAPI) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"_s;
67 constexpr auto insertTopLevelDomainQuery = "INSERT INTO TopLevelDomains VALUES (?)"_s;
68 constexpr auto domainIDFromStringQuery = "SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
69 constexpr auto storageAccessUnderTopFrameDomainsQuery = "INSERT INTO StorageAccessUnderTopFrameDomains (domainID, topLevelDomainID) "
70     "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain == ?"_s;
71 constexpr auto topFrameUniqueRedirectsToQuery = "INSERT INTO TopFrameUniqueRedirectsTo (sourceDomainID, toDomainID) SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain == ?"_s;
72 constexpr auto topFrameUniqueRedirectsToExistsQuery = "SELECT EXISTS (SELECT 1 FROM TopFrameUniqueRedirectsTo WHERE sourceDomainID = ? "
73     "AND toDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
74 constexpr auto topFrameUniqueRedirectsFromQuery = "INSERT INTO TopFrameUniqueRedirectsFrom (targetDomainID, fromDomainID) "
75     "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
76 constexpr auto topFrameUniqueRedirectsFromExistsQuery = "SELECT EXISTS (SELECT 1 FROM TopFrameUniqueRedirectsFrom WHERE targetDomainID = ? "
77     "AND fromDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
78 constexpr auto topFrameLinkDecorationsFromQuery = "INSERT INTO TopFrameLinkDecorationsFrom (fromDomainID, toDomainID) "
79     "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
80 constexpr auto topFrameLinkDecorationsFromExistsQuery = "SELECT EXISTS (SELECT 1 FROM TopFrameLinkDecorationsFrom WHERE fromDomainID = ? "
81     "AND toDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
82 constexpr auto subframeUnderTopFrameDomainsQuery = "INSERT INTO SubframeUnderTopFrameDomains (subFrameDomainID, topFrameDomainID) "
83     "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
84 constexpr auto subframeUnderTopFrameDomainExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubframeUnderTopFrameDomains WHERE subFrameDomainID = ? "
85     "AND topFrameDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
86 constexpr auto subresourceUnderTopFrameDomainsQuery = "INSERT INTO SubresourceUnderTopFrameDomains (subresourceDomainID, topFrameDomainID) "
87     "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain = ?"_s;
88 constexpr auto subresourceUnderTopFrameDomainExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubresourceUnderTopFrameDomains "
89     "WHERE subresourceDomainID = ? AND topFrameDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
90 constexpr auto subresourceUniqueRedirectsToQuery = "INSERT INTO SubresourceUniqueRedirectsTo (subresourceDomainID, toDomainID) "
91     "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain == ?"_s;
92 constexpr auto subresourceUniqueRedirectsToExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubresourceUniqueRedirectsTo WHERE subresourceDomainID = ? "
93     "AND toDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
94 constexpr auto subresourceUniqueRedirectsFromQuery = "INSERT INTO SubresourceUniqueRedirectsFrom (subresourceDomainID, fromDomainID) "
95     "SELECT ?, domainID FROM ObservedDomains WHERE registrableDomain == ?"_s;
96 constexpr auto subresourceUniqueRedirectsFromExistsQuery = "SELECT EXISTS (SELECT 1 FROM SubresourceUniqueRedirectsFrom WHERE subresourceDomainID = ? "
97     "AND fromDomainID = (SELECT domainID FROM ObservedDomains WHERE registrableDomain = ?))"_s;
98 constexpr auto mostRecentUserInteractionQuery = "UPDATE ObservedDomains SET hadUserInteraction = ?, mostRecentUserInteractionTime = ? "
99     "WHERE registrableDomain = ?"_s;
100 constexpr auto updateLastSeenQuery = "UPDATE ObservedDomains SET lastSeen = ? WHERE registrableDomain = ?"_s;
101 constexpr auto updatePrevalentResourceQuery = "UPDATE ObservedDomains SET isPrevalent = ? WHERE registrableDomain = ?"_s;
102 constexpr auto isPrevalentResourceQuery = "SELECT isPrevalent FROM ObservedDomains WHERE registrableDomain = ?"_s;
103 constexpr auto updateVeryPrevalentResourceQuery = "UPDATE ObservedDomains SET isVeryPrevalent = ? WHERE registrableDomain = ?"_s;
104 constexpr auto isVeryPrevalentResourceQuery = "SELECT isVeryPrevalent FROM ObservedDomains WHERE registrableDomain = ?"_s;
105 constexpr auto clearPrevalentResourceQuery = "UPDATE ObservedDomains SET isPrevalent = 0, isVeryPrevalent = 0 WHERE registrableDomain = ?"_s;
106 constexpr auto hadUserInteractionQuery = "SELECT hadUserInteraction, mostRecentUserInteractionTime FROM ObservedDomains WHERE registrableDomain = ?"_s;
107 constexpr auto updateGrandfatheredQuery = "UPDATE ObservedDomains SET grandfathered = ? WHERE registrableDomain = ?"_s;
108 constexpr auto isGrandfatheredQuery = "SELECT grandfathered FROM ObservedDomains WHERE registrableDomain = ?"_s;
109 constexpr auto findExpiredUserInteractionQuery = "SELECT domainID FROM ObservedDomains WHERE hadUserInteraction = 1 AND mostRecentUserInteractionTime < ?"_s;
110
111 constexpr auto createObservedDomain = "CREATE TABLE ObservedDomains ("
112     "domainID INTEGER PRIMARY KEY, registrableDomain TEXT NOT NULL UNIQUE ON CONFLICT FAIL, lastSeen REAL NOT NULL, "
113     "hadUserInteraction INTEGER NOT NULL, mostRecentUserInteractionTime REAL NOT NULL, grandfathered INTEGER NOT NULL, "
114     "isPrevalent INTEGER NOT NULL, isVeryPrevalent INTEGER NOT NULL, dataRecordsRemoved INTEGER NOT NULL,"
115     "timesAccessedAsFirstPartyDueToUserInteraction INTEGER NOT NULL, timesAccessedAsFirstPartyDueToStorageAccessAPI INTEGER NOT NULL);"_s;
116     
117 constexpr auto createTopLevelDomains = "CREATE TABLE TopLevelDomains ("
118     "topLevelDomainID INTEGER PRIMARY KEY, CONSTRAINT fkDomainID FOREIGN KEY(topLevelDomainID) "
119     "REFERENCES ObservedDomains(domainID) ON DELETE CASCADE);"_s;
120     
121 constexpr auto createStorageAccessUnderTopFrameDomains = "CREATE TABLE StorageAccessUnderTopFrameDomains ("
122     "domainID INTEGER NOT NULL, topLevelDomainID INTEGER NOT NULL ON CONFLICT FAIL, "
123     "CONSTRAINT fkDomainID FOREIGN KEY(domainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE, "
124     "FOREIGN KEY(topLevelDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE);"_s;
125     
126 constexpr auto createTopFrameUniqueRedirectsTo = "CREATE TABLE TopFrameUniqueRedirectsTo ("
127     "sourceDomainID INTEGER NOT NULL, toDomainID INTEGER NOT NULL, "
128     "FOREIGN KEY(sourceDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE, "
129     "FOREIGN KEY(toDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE);"_s;
130     
131 constexpr auto createTopFrameUniqueRedirectsFrom = "CREATE TABLE TopFrameUniqueRedirectsFrom ("
132     "targetDomainID INTEGER NOT NULL, fromDomainID INTEGER NOT NULL, "
133     "FOREIGN KEY(targetDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE, "
134     "FOREIGN KEY(fromDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE);"_s;
135
136 constexpr auto createTopFrameLinkDecorationsFrom = "CREATE TABLE TopFrameLinkDecorationsFrom ("
137     "fromDomainID INTEGER NOT NULL, toDomainID INTEGER NOT NULL, "
138     "FOREIGN KEY(fromDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE, "
139     "FOREIGN KEY(toDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE);"_s;
140
141 constexpr auto createSubframeUnderTopFrameDomains = "CREATE TABLE SubframeUnderTopFrameDomains ("
142     "subFrameDomainID INTEGER NOT NULL, topFrameDomainID INTEGER NOT NULL, "
143     "FOREIGN KEY(subFrameDomainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE, "
144     "FOREIGN KEY(topFrameDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE);"_s;
145     
146 constexpr auto createSubresourceUnderTopFrameDomains = "CREATE TABLE SubresourceUnderTopFrameDomains ("
147     "subresourceDomainID INTEGER NOT NULL, topFrameDomainID INTEGER NOT NULL, "
148     "FOREIGN KEY(subresourceDomainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE, "
149     "FOREIGN KEY(topFrameDomainID) REFERENCES TopLevelDomains(topLevelDomainID) ON DELETE CASCADE);"_s;
150     
151 constexpr auto createSubresourceUniqueRedirectsTo = "CREATE TABLE SubresourceUniqueRedirectsTo ("
152     "subresourceDomainID INTEGER NOT NULL, toDomainID INTEGER NOT NULL, "
153     "FOREIGN KEY(subresourceDomainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE, "
154     "FOREIGN KEY(toDomainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE);"_s;
155     
156 constexpr auto createSubresourceUniqueRedirectsFrom = "CREATE TABLE SubresourceUniqueRedirectsFrom ("
157     "subresourceDomainID INTEGER NOT NULL, fromDomainID INTEGER NOT NULL, "
158     "FOREIGN KEY(subresourceDomainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE, "
159     "FOREIGN KEY(fromDomainID) REFERENCES ObservedDomains(domainID) ON DELETE CASCADE);"_s;
160     
161 ResourceLoadStatisticsDatabaseStore::ResourceLoadStatisticsDatabaseStore(WebResourceLoadStatisticsStore& store, WorkQueue& workQueue, ShouldIncludeLocalhost shouldIncludeLocalhost, const String& storageDirectoryPath)
162     : ResourceLoadStatisticsStore(store, workQueue, shouldIncludeLocalhost)
163     , m_storageDirectoryPath(storageDirectoryPath + "/observations.db")
164     , m_observedDomainCount(m_database, observedDomainCountQuery)
165     , m_insertObservedDomainStatement(m_database, insertObservedDomainQuery)
166     , m_insertTopLevelDomainStatement(m_database, insertTopLevelDomainQuery)
167     , m_domainIDFromStringStatement(m_database, domainIDFromStringQuery)
168     , m_storageAccessUnderTopFrameDomainsStatement(m_database, storageAccessUnderTopFrameDomainsQuery)
169     , m_topFrameUniqueRedirectsTo(m_database, topFrameUniqueRedirectsToQuery)
170     , m_topFrameUniqueRedirectsToExists(m_database, topFrameUniqueRedirectsToExistsQuery)
171     , m_topFrameUniqueRedirectsFrom(m_database, topFrameUniqueRedirectsFromQuery)
172     , m_topFrameUniqueRedirectsFromExists(m_database, topFrameUniqueRedirectsFromExistsQuery)
173     , m_topFrameLinkDecorationsFrom(m_database, topFrameLinkDecorationsFromQuery)
174     , m_topFrameLinkDecorationsFromExists(m_database, topFrameLinkDecorationsFromExistsQuery)
175     , m_subframeUnderTopFrameDomains(m_database, subframeUnderTopFrameDomainsQuery)
176     , m_subframeUnderTopFrameDomainExists(m_database, subframeUnderTopFrameDomainExistsQuery)
177     , m_subresourceUnderTopFrameDomains(m_database, subresourceUnderTopFrameDomainsQuery)
178     , m_subresourceUnderTopFrameDomainExists(m_database, subresourceUnderTopFrameDomainExistsQuery)
179     , m_subresourceUniqueRedirectsTo(m_database, subresourceUniqueRedirectsToQuery)
180     , m_subresourceUniqueRedirectsToExists(m_database, subresourceUniqueRedirectsToExistsQuery)
181     , m_subresourceUniqueRedirectsFrom(m_database, subresourceUniqueRedirectsFromQuery)
182     , m_subresourceUniqueRedirectsFromExists(m_database, subresourceUniqueRedirectsFromExistsQuery)
183     , m_mostRecentUserInteractionStatement(m_database, mostRecentUserInteractionQuery)
184     , m_updateLastSeenStatement(m_database, updateLastSeenQuery)
185     , m_updatePrevalentResourceStatement(m_database, updatePrevalentResourceQuery)
186     , m_isPrevalentResourceStatement(m_database, isPrevalentResourceQuery)
187     , m_updateVeryPrevalentResourceStatement(m_database, updateVeryPrevalentResourceQuery)
188     , m_isVeryPrevalentResourceStatement(m_database, isVeryPrevalentResourceQuery)
189     , m_clearPrevalentResourceStatement(m_database, clearPrevalentResourceQuery)
190     , m_hadUserInteractionStatement(m_database, hadUserInteractionQuery)
191     , m_updateGrandfatheredStatement(m_database, updateGrandfatheredQuery)
192     , m_isGrandfatheredStatement(m_database, isGrandfatheredQuery)
193     , m_findExpiredUserInteractionStatement(m_database, findExpiredUserInteractionQuery)
194 {
195     ASSERT(!RunLoop::isMain());
196
197 #if PLATFORM(COCOA)
198     registerUserDefaultsIfNeeded();
199 #endif
200
201     if (!m_database.open(m_storageDirectoryPath)) {
202         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::open failed, error message: %{public}s, database path: %{public}s", this, m_database.lastErrorMsg(), m_storageDirectoryPath.utf8().data());
203         ASSERT_NOT_REACHED();
204         return;
205     }
206     
207     // Since we are using a workerQueue, the sequential dispatch blocks may be called by different threads.
208     m_database.disableThreadingChecks();
209
210     if (!m_database.tableExists("ObservedDomains"_s)) {
211         if (!createSchema()) {
212             RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::createSchema failed, error message: %{public}s, database path: %{public}s", this, m_database.lastErrorMsg(), m_storageDirectoryPath.utf8().data());
213             ASSERT_NOT_REACHED();
214             return;
215         }
216     }
217
218     if (!prepareStatements()) {
219         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::prepareStatements failed, error message: %{public}s, database path: %{public}s", this, m_database.lastErrorMsg(), m_storageDirectoryPath.utf8().data());
220         ASSERT_NOT_REACHED();
221         return;
222     }
223
224     workQueue.dispatchAfter(5_s, [weakThis = makeWeakPtr(*this)] {
225         if (weakThis)
226             weakThis->calculateAndSubmitTelemetry();
227     });
228 }
229
230 bool ResourceLoadStatisticsDatabaseStore::isEmpty() const
231 {
232     ASSERT(!RunLoop::isMain());
233     
234     bool result = false;
235     if (m_observedDomainCount.step() == SQLITE_ROW)
236         result = !m_observedDomainCount.getColumnInt(0);
237     
238     int resetResult = m_observedDomainCount.reset();
239     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
240     
241     return result;
242 }
243
244 bool ResourceLoadStatisticsDatabaseStore::createSchema()
245 {
246     ASSERT(!RunLoop::isMain());
247
248     if (!m_database.executeCommand(createObservedDomain)) {
249         LOG_ERROR("Could not create ObservedDomains table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
250         return false;
251     }
252
253     if (!m_database.executeCommand(createTopLevelDomains)) {
254         LOG_ERROR("Could not create TopLevelDomains table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
255         return false;
256     }
257
258     if (!m_database.executeCommand(createStorageAccessUnderTopFrameDomains)) {
259         LOG_ERROR("Could not create StorageAccessUnderTopFrameDomains table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
260         return false;
261     }
262
263     if (!m_database.executeCommand(createTopFrameUniqueRedirectsTo)) {
264         LOG_ERROR("Could not create TopFrameUniqueRedirectsTo table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
265         return false;
266     }
267
268     if (!m_database.executeCommand(createTopFrameUniqueRedirectsFrom)) {
269         LOG_ERROR("Could not create TopFrameUniqueRedirectsFrom table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
270         return false;
271     }
272
273     if (!m_database.executeCommand(createTopFrameLinkDecorationsFrom)) {
274         LOG_ERROR("Could not create TopFrameLinkDecorationsFrom table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
275         return false;
276     }
277     
278     if (!m_database.executeCommand(createSubframeUnderTopFrameDomains)) {
279         LOG_ERROR("Could not create SubframeUnderTopFrameDomains table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
280         return false;
281     }
282
283     if (!m_database.executeCommand(createSubresourceUnderTopFrameDomains)) {
284         LOG_ERROR("Could not create SubresourceUnderTopFrameDomains table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
285         return false;
286     }
287
288     if (!m_database.executeCommand(createSubresourceUniqueRedirectsTo)) {
289         LOG_ERROR("Could not create SubresourceUniqueRedirectsTo table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
290         return false;
291     }
292
293     if (!m_database.executeCommand(createSubresourceUniqueRedirectsFrom)) {
294         LOG_ERROR("Could not create SubresourceUniqueRedirectsFrom table in database (%i) - %s", m_database.lastError(), m_database.lastErrorMsg());
295         return false;
296     }
297
298     return true;
299 }
300
301 bool ResourceLoadStatisticsDatabaseStore::prepareStatements()
302 {
303     ASSERT(!RunLoop::isMain());
304         
305     if (m_observedDomainCount.prepare() != SQLITE_OK
306         || m_insertObservedDomainStatement.prepare() != SQLITE_OK
307         || m_insertTopLevelDomainStatement.prepare() != SQLITE_OK
308         || m_domainIDFromStringStatement.prepare() != SQLITE_OK
309         || m_storageAccessUnderTopFrameDomainsStatement.prepare() != SQLITE_OK
310         || m_topFrameUniqueRedirectsTo.prepare() != SQLITE_OK
311         || m_topFrameUniqueRedirectsToExists.prepare() != SQLITE_OK
312         || m_topFrameUniqueRedirectsFrom.prepare() != SQLITE_OK
313         || m_topFrameUniqueRedirectsFromExists.prepare() != SQLITE_OK
314         || m_subframeUnderTopFrameDomains.prepare() != SQLITE_OK
315         || m_subframeUnderTopFrameDomainExists.prepare() != SQLITE_OK
316         || m_subresourceUnderTopFrameDomains.prepare() != SQLITE_OK
317         || m_subresourceUnderTopFrameDomainExists.prepare() != SQLITE_OK
318         || m_subresourceUniqueRedirectsTo.prepare() != SQLITE_OK
319         || m_subresourceUniqueRedirectsToExists.prepare() != SQLITE_OK
320         || m_subresourceUniqueRedirectsFrom.prepare() != SQLITE_OK
321         || m_subresourceUniqueRedirectsFromExists.prepare() != SQLITE_OK
322         || m_updateLastSeenStatement.prepare() != SQLITE_OK
323         || m_mostRecentUserInteractionStatement.prepare() != SQLITE_OK
324         || m_updatePrevalentResourceStatement.prepare() != SQLITE_OK
325         || m_isPrevalentResourceStatement.prepare() != SQLITE_OK
326         || m_updateVeryPrevalentResourceStatement.prepare() != SQLITE_OK
327         || m_isVeryPrevalentResourceStatement.prepare() != SQLITE_OK
328         || m_clearPrevalentResourceStatement.prepare() != SQLITE_OK
329         || m_hadUserInteractionStatement.prepare() != SQLITE_OK
330         || m_updateGrandfatheredStatement.prepare() != SQLITE_OK
331         || m_isGrandfatheredStatement.prepare() != SQLITE_OK
332         || m_findExpiredUserInteractionStatement.prepare() != SQLITE_OK
333         ) {
334         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::prepareStatements failed to prepare, error message: %{public}s", this, m_database.lastErrorMsg());
335         ASSERT_NOT_REACHED();
336         return false;
337     }
338
339     return true;
340 }
341
342 bool ResourceLoadStatisticsDatabaseStore::insertObservedDomain(const ResourceLoadStatistics& loadStatistics)
343 {
344     ASSERT(!RunLoop::isMain());
345
346 #ifndef NDEBUG
347     ASSERT(confirmDomainDoesNotExist(loadStatistics.registrableDomain));
348 #endif
349
350     if (m_insertObservedDomainStatement.bindText(1, loadStatistics.registrableDomain.string()) != SQLITE_OK
351         || m_insertObservedDomainStatement.bindDouble(2, loadStatistics.lastSeen.secondsSinceEpoch().value()) != SQLITE_OK
352         || m_insertObservedDomainStatement.bindInt(3, loadStatistics.hadUserInteraction) != SQLITE_OK
353         || m_insertObservedDomainStatement.bindDouble(4, loadStatistics.mostRecentUserInteractionTime.secondsSinceEpoch().value()) != SQLITE_OK
354         || m_insertObservedDomainStatement.bindInt(5, loadStatistics.grandfathered) != SQLITE_OK
355         || m_insertObservedDomainStatement.bindInt(6, loadStatistics.isPrevalentResource) != SQLITE_OK
356         || m_insertObservedDomainStatement.bindInt(7, loadStatistics.isVeryPrevalentResource) != SQLITE_OK
357         || m_insertObservedDomainStatement.bindInt(8, loadStatistics.dataRecordsRemoved) != SQLITE_OK
358         || m_insertObservedDomainStatement.bindInt(9, loadStatistics.timesAccessedAsFirstPartyDueToUserInteraction) != SQLITE_OK
359         || m_insertObservedDomainStatement.bindInt(10, loadStatistics.timesAccessedAsFirstPartyDueToStorageAccessAPI) != SQLITE_OK) {
360         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::insertObservedDomain failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
361         ASSERT_NOT_REACHED();
362         return false;
363     }
364
365     if (m_insertObservedDomainStatement.step() != SQLITE_DONE) {
366         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::insertObservedDomain failed to commit, error message: %{public}s", this, m_database.lastErrorMsg());
367         ASSERT_NOT_REACHED();
368         return false;
369     }
370
371     int resetResult = m_insertObservedDomainStatement.reset();
372     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
373
374     return true;
375 }
376
377 bool ResourceLoadStatisticsDatabaseStore::relationshipExists(WebCore::SQLiteStatement& statement, unsigned firstDomainID, const RegistrableDomain& secondDomain) const
378 {
379     ASSERT(!RunLoop::isMain());
380
381     if (statement.bindInt(1, firstDomainID) != SQLITE_OK
382         || statement.bindText(2, secondDomain.string()) != SQLITE_OK
383         || statement.step() != SQLITE_ROW) {
384         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::m_insertDomainRelationshipStatement failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
385         ASSERT_NOT_REACHED();
386         return false;
387     }
388     
389     bool relationShipExists = !!statement.getColumnInt(0);
390
391     int resetResult = statement.reset();
392     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
393     
394     return relationShipExists;
395 }
396
397 bool ResourceLoadStatisticsDatabaseStore::insertDomainRelationship(WebCore::SQLiteStatement& statement, unsigned domainID, const RegistrableDomain& topFrame)
398 {
399     ASSERT(!RunLoop::isMain());
400
401     if (statement.bindInt(1, domainID) != SQLITE_OK
402         || statement.bindText(2, topFrame.string()) != SQLITE_OK
403         || statement.step() != SQLITE_DONE) {
404         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::m_insertDomainRelationshipStatement failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
405         ASSERT_NOT_REACHED();
406         return false;
407     }
408
409     int resetResult = statement.reset();
410     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
411     
412     return true;
413 }
414
415 #ifndef NDEBUG
416 bool ResourceLoadStatisticsDatabaseStore::confirmDomainDoesNotExist(const RegistrableDomain& domain) const
417 {
418     ASSERT(!RunLoop::isMain());
419
420     if (m_domainIDFromStringStatement.bindText(1, domain.string()) != SQLITE_OK) {
421         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::confirmDomainDoesNotExist failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
422         ASSERT_NOT_REACHED();
423         return false;
424     }
425         
426     if (m_domainIDFromStringStatement.step() == SQLITE_ROW) {
427         int resetResult = m_domainIDFromStringStatement.reset();
428         ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
429         return false;
430     }
431
432     int resetResult = m_domainIDFromStringStatement.reset();
433     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
434     
435     return true;
436 }
437 #endif
438
439 unsigned ResourceLoadStatisticsDatabaseStore::domainID(const RegistrableDomain& domain) const
440 {
441     ASSERT(!RunLoop::isMain());
442
443     unsigned domainID = 0;
444
445     if (m_domainIDFromStringStatement.bindText(1, domain.string()) != SQLITE_OK
446         || m_domainIDFromStringStatement.step() != SQLITE_ROW) {
447         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::domainIDFromString failed, error message: %{public}s", this, m_database.lastErrorMsg());
448         ASSERT_NOT_REACHED();
449         return domainID;
450     }
451
452     domainID = m_domainIDFromStringStatement.getColumnInt(0);
453
454     int resetResult = m_domainIDFromStringStatement.reset();
455     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
456     
457     return domainID;
458 }
459
460 void ResourceLoadStatisticsDatabaseStore::insertDomainRelationships(const WebCore::ResourceLoadStatistics& loadStatistics)
461 {
462     ASSERT(!RunLoop::isMain());
463
464     auto registrableDomainID = domainID(loadStatistics.registrableDomain);
465     for (auto& topFrameDomain : loadStatistics.storageAccessUnderTopFrameDomains)
466         insertDomainRelationship(m_storageAccessUnderTopFrameDomainsStatement, registrableDomainID, topFrameDomain);
467
468     for (auto& toDomain : loadStatistics.topFrameUniqueRedirectsTo)
469         insertDomainRelationship(m_topFrameUniqueRedirectsTo, registrableDomainID, toDomain);
470     
471     for (auto& fromDomain : loadStatistics.topFrameUniqueRedirectsFrom)
472         insertDomainRelationship(m_topFrameUniqueRedirectsFrom, registrableDomainID, fromDomain);
473     
474     for (auto& topFrameDomain : loadStatistics.subframeUnderTopFrameDomains)
475         insertDomainRelationship(m_subframeUnderTopFrameDomains, registrableDomainID, topFrameDomain);
476     
477     for (auto& topFrameDomain : loadStatistics.subresourceUnderTopFrameDomains)
478         insertDomainRelationship(m_subresourceUnderTopFrameDomains, registrableDomainID, topFrameDomain);
479     
480     for (auto& toDomain : loadStatistics.subresourceUniqueRedirectsTo)
481         insertDomainRelationship(m_subresourceUniqueRedirectsTo, registrableDomainID, toDomain);
482     
483     for (auto& fromDomain : loadStatistics.subresourceUniqueRedirectsFrom)
484         insertDomainRelationship(m_subresourceUniqueRedirectsFrom, registrableDomainID, fromDomain);
485 }
486
487 void ResourceLoadStatisticsDatabaseStore::populateFromMemoryStore(const ResourceLoadStatisticsMemoryStore& memoryStore)
488 {
489     ASSERT(!RunLoop::isMain());
490
491     if (!isEmpty())
492         return;
493
494     auto& statisticsMap = memoryStore.data();
495     for (const auto& statistic : statisticsMap)
496         insertObservedDomain(statistic.value);
497
498     // Make a separate pass for inter-domain relationships so we
499     // can refer to the ObservedDomain table entries
500     for (auto& statistic : statisticsMap)
501         insertDomainRelationships(statistic.value);
502
503     m_database.runVacuumCommand();
504 }
505
506 void ResourceLoadStatisticsDatabaseStore::calculateAndSubmitTelemetry() const
507 {
508     ASSERT(!RunLoop::isMain());
509
510     // FIXME(195088): Implement for Database version.
511 }
512
513 static String domainsToString(const HashSet<RegistrableDomain>& domains)
514 {
515     StringBuilder builder;
516     for (auto domainName : domains) {
517         if (!builder.isEmpty())
518             builder.appendLiteral(", ");
519         builder.append('"');
520         builder.append(domainName.string());
521         builder.append('"');
522     }
523
524     return builder.toString();
525 }
526
527 void ResourceLoadStatisticsDatabaseStore::incrementRecordsDeletedCountForDomains(HashSet<RegistrableDomain>&& domains)
528 {
529     ASSERT(!RunLoop::isMain());
530
531     SQLiteStatement domainsToUpdateStatement(m_database, makeString("UPDATE ObservedDomains SET dataRecordsRemoved = dataRecordsRemoved + 1 WHERE registrableDomain IN (", domainsToString(domains), ")"));
532     if (domainsToUpdateStatement.prepare() != SQLITE_OK
533         || domainsToUpdateStatement.step() != SQLITE_DONE) {
534         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::incrementStatisticsForDomains failed, error message: %{public}s", this, m_database.lastErrorMsg());
535         ASSERT_NOT_REACHED();
536     }
537 }
538
539 unsigned ResourceLoadStatisticsDatabaseStore::recursivelyFindNonPrevalentDomainsThatRedirectedToThisDomain(unsigned primaryDomainID, StdSet<unsigned>& nonPrevalentRedirectionSources, unsigned numberOfRecursiveCalls)
540 {
541     ASSERT(!RunLoop::isMain());
542
543     if (numberOfRecursiveCalls >= maxNumberOfRecursiveCallsInRedirectTraceBack) {
544         RELEASE_LOG(ResourceLoadStatistics, "Hit %u recursive calls in redirect backtrace. Returning early.", maxNumberOfRecursiveCallsInRedirectTraceBack);
545         return numberOfRecursiveCalls;
546     }
547     
548     ++numberOfRecursiveCalls;
549
550     StdSet<unsigned> newlyIdentifiedDomains;
551     SQLiteStatement findSubresources(m_database, makeString("SELECT SubresourceUniqueRedirectsFrom.fromDomainID from SubresourceUniqueRedirectsFrom INNER JOIN ObservedDomains ON ObservedDomains.domainID = SubresourceUniqueRedirectsFrom.fromDomainID WHERE subresourceDomainID = ", String::number(primaryDomainID), "AND ObservedDomains.isPrevalent = 0"));
552     if (findSubresources.prepare() == SQLITE_OK) {
553         while (findSubresources.step() == SQLITE_ROW) {
554             auto newDomainID = findSubresources.getColumnInt(0);
555             auto insertResult = nonPrevalentRedirectionSources.insert(newDomainID);
556             if (insertResult.second)
557                 newlyIdentifiedDomains.insert(newDomainID);
558         }
559     }
560
561     SQLiteStatement findTopFrames(m_database, makeString("SELECT TopFrameUniqueRedirectsFrom.fromDomainID from TopFrameUniqueRedirectsFrom INNER JOIN ObservedDomains ON ObservedDomains.domainID = TopFrameUniqueRedirectsFrom.fromDomainID WHERE targetDomainID = ", String::number(primaryDomainID), "AND ObservedDomains.isPrevalent = 0"));
562     if (findTopFrames.prepare() == SQLITE_OK) {
563         while (findTopFrames.step() == SQLITE_ROW) {
564             auto newDomainID = findTopFrames.getColumnInt(0);
565             auto insertResult = nonPrevalentRedirectionSources.insert(newDomainID);
566             if (insertResult.second)
567                 newlyIdentifiedDomains.insert(newDomainID);
568         }
569     }
570
571     if (newlyIdentifiedDomains.empty())
572         return numberOfRecursiveCalls;
573
574     for (auto domainID : newlyIdentifiedDomains)
575         numberOfRecursiveCalls = recursivelyFindNonPrevalentDomainsThatRedirectedToThisDomain(domainID, nonPrevalentRedirectionSources, numberOfRecursiveCalls);
576
577     return numberOfRecursiveCalls;
578 }
579
580 template <typename IteratorType>
581 static String buildList(const WTF::IteratorRange<IteratorType>& values)
582 {
583     StringBuilder builder;
584     for (auto domainID : values) {
585         if (!builder.isEmpty())
586             builder.appendLiteral(", ");
587         builder.appendNumber(domainID);
588     }
589     return builder.toString();
590 }
591
592 void ResourceLoadStatisticsDatabaseStore::markAsPrevalentIfHasRedirectedToPrevalent()
593 {
594     ASSERT(!RunLoop::isMain());
595
596     StdSet<unsigned> prevalentDueToRedirect;
597     SQLiteStatement subresourceRedirectStatement(m_database, "SELECT DISTINCT SubresourceUniqueRedirectsTo.subresourceDomainID FROM SubresourceUniqueRedirectsTo JOIN ObservedDomains ON ObservedDomains.domainID = SubresourceUniqueRedirectsTo.toDomainID AND ObservedDomains.isPrevalent = 1"_s);
598     if (subresourceRedirectStatement.prepare() == SQLITE_OK) {
599         while (subresourceRedirectStatement.step() == SQLITE_ROW)
600             prevalentDueToRedirect.insert(subresourceRedirectStatement.getColumnInt(0));
601     }
602
603     SQLiteStatement topFrameRedirectStatement(m_database, "SELECT DISTINCT TopFrameUniqueRedirectsTo.sourceDomainID FROM TopFrameUniqueRedirectsTo JOIN ObservedDomains ON ObservedDomains.domainID = TopFrameUniqueRedirectsTo.toDomainID AND ObservedDomains.isPrevalent = 1"_s);
604     if (topFrameRedirectStatement.prepare() == SQLITE_OK) {
605         while (topFrameRedirectStatement.step() == SQLITE_ROW)
606             prevalentDueToRedirect.insert(topFrameRedirectStatement.getColumnInt(0));
607     }
608
609     SQLiteStatement markPrevalentStatement(m_database, makeString("UPDATE ObservedDomains SET isPrevalent = 1 WHERE domainID IN (", buildList(WTF::IteratorRange<StdSet<unsigned>::iterator>(prevalentDueToRedirect.begin(), prevalentDueToRedirect.end())), ")"));
610     if (markPrevalentStatement.prepare() != SQLITE_OK
611         || markPrevalentStatement.step() != SQLITE_DONE) {
612         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::markAsPrevalentIfHasRedirectedToPrevalent failed to execute, error message: %{public}s", this, m_database.lastErrorMsg());
613         ASSERT_NOT_REACHED();
614     }
615 }
616
617 HashMap<unsigned, ResourceLoadStatisticsDatabaseStore::NotVeryPrevalentResources> ResourceLoadStatisticsDatabaseStore::findNotVeryPrevalentResources()
618 {
619     ASSERT(!RunLoop::isMain());
620
621     HashMap<unsigned, NotVeryPrevalentResources> results;
622     SQLiteStatement notVeryPrevalentResourcesStatement(m_database, "SELECT domainID, registrableDomain, isPrevalent FROM ObservedDomains WHERE isVeryPrevalent = 0"_s);
623     if (notVeryPrevalentResourcesStatement.prepare() == SQLITE_OK) {
624         while (notVeryPrevalentResourcesStatement.step() == SQLITE_ROW) {
625             unsigned key = static_cast<unsigned>(notVeryPrevalentResourcesStatement.getColumnInt(0));
626             NotVeryPrevalentResources value({ RegistrableDomain::uncheckedCreateFromRegistrableDomainString(notVeryPrevalentResourcesStatement.getColumnText(1))
627                 , notVeryPrevalentResourcesStatement.getColumnInt(2) ? ResourceLoadPrevalence::High : ResourceLoadPrevalence::Low
628                 , 0, 0, 0, 0 });
629             results.add(key, value);
630         }
631     }
632
633     StringBuilder builder;
634     for (auto value : results.keys()) {
635         if (!builder.isEmpty())
636             builder.appendLiteral(", ");
637         builder.appendNumber(value);
638     }
639
640     auto domainIDsOfInterest = builder.toString();
641
642     SQLiteStatement subresourceUnderTopFrameDomainsStatement(m_database, makeString("SELECT subresourceDomainID, COUNT(topFrameDomainID) FROM SubresourceUnderTopFrameDomains WHERE subresourceDomainID IN (", domainIDsOfInterest, ") GROUP BY subresourceDomainID"));
643     if (subresourceUnderTopFrameDomainsStatement.prepare() == SQLITE_OK) {
644         while (subresourceUnderTopFrameDomainsStatement.step() == SQLITE_ROW) {
645             unsigned domainID = static_cast<unsigned>(subresourceUnderTopFrameDomainsStatement.getColumnInt(0));
646             auto result = results.find(domainID);
647             if (result != results.end())
648                 result->value.subresourceUnderTopFrameDomainsCount = static_cast<unsigned>(subresourceUnderTopFrameDomainsStatement.getColumnInt(1));
649         }
650     }
651
652     SQLiteStatement subresourceUniqueRedirectsToCountStatement(m_database, makeString("SELECT subresourceDomainID, COUNT(toDomainID) FROM SubresourceUniqueRedirectsTo WHERE subresourceDomainID IN (", domainIDsOfInterest, ") GROUP BY subresourceDomainID"));
653     if (subresourceUniqueRedirectsToCountStatement.prepare() == SQLITE_OK) {
654         while (subresourceUniqueRedirectsToCountStatement.step() == SQLITE_ROW) {
655             unsigned domainID = static_cast<unsigned>(subresourceUniqueRedirectsToCountStatement.getColumnInt(0));
656             auto result = results.find(domainID);
657             if (result != results.end())
658                 result->value.subresourceUniqueRedirectsToCount = static_cast<unsigned>(subresourceUniqueRedirectsToCountStatement.getColumnInt(1));
659         }
660     }
661
662     SQLiteStatement subframeUnderTopFrameDomainsCountStatement(m_database, makeString("SELECT subresourceDomainID, COUNT(topFrameDomainID) FROM SubresourceUnderTopFrameDomains WHERE subresourceDomainID IN (", domainIDsOfInterest, ") GROUP BY subresourceDomainID"));
663     if (subframeUnderTopFrameDomainsCountStatement.prepare() == SQLITE_OK) {
664         while (subframeUnderTopFrameDomainsCountStatement.step() == SQLITE_ROW) {
665             unsigned domainID = static_cast<unsigned>(subframeUnderTopFrameDomainsCountStatement.getColumnInt(0));
666             auto result = results.find(domainID);
667             if (result != results.end())
668                 result->value.subframeUnderTopFrameDomainsCount = static_cast<unsigned>(subframeUnderTopFrameDomainsCountStatement.getColumnInt(1));
669         }
670     }
671
672     SQLiteStatement topFrameUniqueRedirectsToCountStatement(m_database, makeString("SELECT sourceDomainID, COUNT(toDomainID) FROM TopFrameUniqueRedirectsTo WHERE sourceDomainID IN (", domainIDsOfInterest, ") GROUP BY sourceDomainID"));
673     if (topFrameUniqueRedirectsToCountStatement.prepare() == SQLITE_OK) {
674         while (topFrameUniqueRedirectsToCountStatement.step() == SQLITE_ROW) {
675             unsigned domainID = static_cast<unsigned>(topFrameUniqueRedirectsToCountStatement.getColumnInt(0));
676             auto result = results.find(domainID);
677             if (result != results.end())
678                 result->value.topFrameUniqueRedirectsToCount = static_cast<unsigned>(topFrameUniqueRedirectsToCountStatement.getColumnInt(1));
679         }
680     }
681
682     return results;
683 }
684
685 void ResourceLoadStatisticsDatabaseStore::reclassifyResources()
686 {
687     ASSERT(!RunLoop::isMain());
688
689     auto notVeryPrevalentResources = findNotVeryPrevalentResources();
690
691     for (auto& resourceStatistic : notVeryPrevalentResources.values()) {
692         if (shouldSkip(resourceStatistic.registerableDomain))
693             continue;
694
695         auto newPrevalence = classifier().calculateResourcePrevalence(resourceStatistic.subresourceUnderTopFrameDomainsCount, resourceStatistic.subresourceUniqueRedirectsToCount, resourceStatistic.subframeUnderTopFrameDomainsCount, resourceStatistic.topFrameUniqueRedirectsToCount, resourceStatistic.prevalence);
696         if (newPrevalence != resourceStatistic.prevalence)
697             setPrevalentResource(resourceStatistic.registerableDomain, newPrevalence);
698     }
699 }
700
701 void ResourceLoadStatisticsDatabaseStore::classifyPrevalentResources()
702 {
703     ASSERT(!RunLoop::isMain());
704     ensurePrevalentResourcesForDebugMode();
705     markAsPrevalentIfHasRedirectedToPrevalent();
706     reclassifyResources();
707 }
708
709 void ResourceLoadStatisticsDatabaseStore::syncStorageIfNeeded()
710 {
711     ASSERT(!RunLoop::isMain());
712     m_database.runVacuumCommand();
713 }
714
715 void ResourceLoadStatisticsDatabaseStore::syncStorageImmediately()
716 {
717     ASSERT(!RunLoop::isMain());
718     m_database.runVacuumCommand();
719 }
720
721 void ResourceLoadStatisticsDatabaseStore::hasStorageAccess(const SubFrameDomain& subFrameDomain, const TopFrameDomain& topFrameDomain, Optional<FrameID> frameID, PageID pageID, CompletionHandler<void(bool)>&& completionHandler)
722 {
723     ASSERT(!RunLoop::isMain());
724
725     ensureResourceStatisticsForRegistrableDomain(subFrameDomain);
726
727     switch (cookieTreatmentForOrigin(subFrameDomain)) {
728     case CookieTreatmentResult::BlockAndPurge:
729         completionHandler(false);
730         return;
731     case CookieTreatmentResult::BlockAndKeep:
732         completionHandler(true);
733         return;
734     case CookieTreatmentResult::Allow:
735         // Do nothing
736         break;
737     };
738
739     RunLoop::main().dispatch([store = makeRef(store()), subFrameDomain = subFrameDomain.isolatedCopy(), topFrameDomain = topFrameDomain.isolatedCopy(), frameID, pageID, completionHandler = WTFMove(completionHandler)]() mutable {
740         store->callHasStorageAccessForFrameHandler(subFrameDomain, topFrameDomain, frameID.value(), pageID, [store = store.copyRef(), completionHandler = WTFMove(completionHandler)](bool result) mutable {
741             store->statisticsQueue().dispatch([completionHandler = WTFMove(completionHandler), result] () mutable {
742                 completionHandler(result);
743             });
744         });
745     });
746 }
747
748 void ResourceLoadStatisticsDatabaseStore::requestStorageAccess(SubFrameDomain&& subFrameDomain, TopFrameDomain&& topFrameDomain, FrameID frameID, PageID pageID, bool promptEnabled, CompletionHandler<void(StorageAccessStatus)>&& completionHandler)
749 {
750     ASSERT(!RunLoop::isMain());
751
752     auto subFrameStatus = ensureResourceStatisticsForRegistrableDomain(subFrameDomain);
753
754     switch (cookieTreatmentForOrigin(subFrameDomain)) {
755     case CookieTreatmentResult::BlockAndPurge: {
756 #if !RELEASE_LOG_DISABLED
757         RELEASE_LOG_INFO_IF(debugLoggingEnabled(), ResourceLoadStatisticsDebug, "Cannot grant storage access to %{public}s since its cookies are blocked in third-party contexts and it has not received user interaction as first-party.", subFrameDomain.string().utf8().data());
758 #endif
759         completionHandler(StorageAccessStatus::CannotRequestAccess);
760         }
761         return;
762     case CookieTreatmentResult::BlockAndKeep: {
763 #if !RELEASE_LOG_DISABLED
764         RELEASE_LOG_INFO_IF(debugLoggingEnabled(), ResourceLoadStatisticsDebug, "No need to grant storage access to %{public}s since its cookies are not blocked in third-party contexts.", subFrameDomain.string().utf8().data());
765 #endif
766         completionHandler(StorageAccessStatus::HasAccess);
767         }
768         return;
769     case CookieTreatmentResult::Allow:
770         // Do nothing
771         break;
772     };
773
774     auto userWasPromptedEarlier = promptEnabled && hasUserGrantedStorageAccessThroughPrompt(subFrameStatus.second, topFrameDomain);
775     if (promptEnabled && !userWasPromptedEarlier) {
776 #if !RELEASE_LOG_DISABLED
777         RELEASE_LOG_INFO_IF(debugLoggingEnabled(), ResourceLoadStatisticsDebug, "About to ask the user whether they want to grant storage access to %{public}s under %{public}s or not.", subFrameDomain.string().utf8().data(), topFrameDomain.string().utf8().data());
778 #endif
779         completionHandler(StorageAccessStatus::RequiresUserPrompt);
780         return;
781     }
782
783 #if !RELEASE_LOG_DISABLED
784     if (userWasPromptedEarlier)
785         RELEASE_LOG_INFO_IF(debugLoggingEnabled(), ResourceLoadStatisticsDebug, "Storage access was granted to %{public}s under %{public}s.", subFrameDomain.string().utf8().data(), topFrameDomain.string().utf8().data());
786 #endif
787
788     SQLiteStatement incrementStorageAccess(m_database, makeString("UPDATE ObservedDomains SET timesAccessedAsFirstPartyDueToStorageAccessAPI = timesAccessedAsFirstPartyDueToStorageAccessAPI + 1 WHERE domainID = ", String::number(subFrameStatus.second)));
789     if (incrementStorageAccess.prepare() != SQLITE_OK
790         || incrementStorageAccess.step() != SQLITE_DONE) {
791         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::requestStorageAccess failed, error message: %{public}s", this, m_database.lastErrorMsg());
792         ASSERT_NOT_REACHED();
793     }
794     
795     grantStorageAccessInternal(WTFMove(subFrameDomain), WTFMove(topFrameDomain), frameID, pageID, userWasPromptedEarlier, [completionHandler = WTFMove(completionHandler)] (bool wasGrantedAccess) mutable {
796         completionHandler(wasGrantedAccess ? StorageAccessStatus::HasAccess : StorageAccessStatus::CannotRequestAccess);
797     });
798 }
799
800 void ResourceLoadStatisticsDatabaseStore::requestStorageAccessUnderOpener(DomainInNeedOfStorageAccess&& domainInNeedOfStorageAccess, OpenerPageID openerPageID, OpenerDomain&& openerDomain)
801 {
802     ASSERT(domainInNeedOfStorageAccess != openerDomain);
803     ASSERT(!RunLoop::isMain());
804
805     if (domainInNeedOfStorageAccess == openerDomain)
806         return;
807
808     ensureResourceStatisticsForRegistrableDomain(domainInNeedOfStorageAccess);
809
810     if (cookieTreatmentForOrigin(domainInNeedOfStorageAccess) != CookieTreatmentResult::Allow)
811         return;
812
813 #if !RELEASE_LOG_DISABLED
814     RELEASE_LOG_INFO_IF(debugLoggingEnabled(), ResourceLoadStatisticsDebug, "[Temporary combatibility fix] Storage access was granted for %{public}s under opener page from %{public}s, with user interaction in the opened window.", domainInNeedOfStorageAccess.string().utf8().data(), openerDomain.string().utf8().data());
815 #endif
816     grantStorageAccessInternal(WTFMove(domainInNeedOfStorageAccess), WTFMove(openerDomain), WTF::nullopt, openerPageID, false, [](bool) { });
817 }
818
819 void ResourceLoadStatisticsDatabaseStore::grantStorageAccess(SubFrameDomain&& subFrameDomain, TopFrameDomain&& topFrameDomain, uint64_t frameID, uint64_t pageID, bool userWasPromptedNow, CompletionHandler<void(bool)>&& completionHandler)
820 {
821     ASSERT(!RunLoop::isMain());
822
823     if (userWasPromptedNow) {
824         auto subFrameStatus = ensureResourceStatisticsForRegistrableDomain(subFrameDomain);
825         ASSERT(subFrameStatus.first == AddedRecord::No);
826         ASSERT(hasHadUserInteraction(subFrameDomain));
827         insertDomainRelationship(m_storageAccessUnderTopFrameDomainsStatement, subFrameStatus.second, topFrameDomain);
828     }
829
830     grantStorageAccessInternal(WTFMove(subFrameDomain), WTFMove(topFrameDomain), frameID, pageID, userWasPromptedNow, WTFMove(completionHandler));
831 }
832
833 void ResourceLoadStatisticsDatabaseStore::grantStorageAccessInternal(SubFrameDomain&& subFrameDomain, TopFrameDomain&& topFrameDomain, Optional<FrameID> frameID, PageID pageID, bool userWasPromptedNowOrEarlier, CompletionHandler<void(bool)>&& callback)
834 {
835     ASSERT(!RunLoop::isMain());
836
837     if (subFrameDomain == topFrameDomain) {
838         callback(true);
839         return;
840     }
841
842     // FIXME: Remove m_storageAccessPromptsEnabled check if prompting is no longer experimental.
843     if (userWasPromptedNowOrEarlier && storageAccessPromptsEnabled()) {
844 #ifndef NDEBUG
845         auto subFrameStatus = ensureResourceStatisticsForRegistrableDomain(subFrameDomain);
846         ASSERT(subFrameStatus.first == AddedRecord::No);
847         ASSERT(hasHadUserInteraction(subFrameDomain));
848         ASSERT(hasUserGrantedStorageAccessThroughPrompt(subFrameStatus.second, topFrameDomain));
849 #endif
850         setUserInteraction(subFrameDomain, true, WallTime::now());
851     }
852
853     RunLoop::main().dispatch([subFrameDomain = subFrameDomain.isolatedCopy(), topFrameDomain = topFrameDomain.isolatedCopy(), frameID, pageID, store = makeRef(store()), callback = WTFMove(callback)]() mutable {
854         store->callGrantStorageAccessHandler(subFrameDomain, topFrameDomain, frameID, pageID, [callback = WTFMove(callback), store = store.copyRef()](bool value) mutable {
855             store->statisticsQueue().dispatch([callback = WTFMove(callback), value] () mutable {
856                 callback(value);
857             });
858         });
859     });
860
861 }
862
863 void ResourceLoadStatisticsDatabaseStore::grandfatherDataForDomains(const HashSet<RegistrableDomain>& domains)
864 {
865     ASSERT(!RunLoop::isMain());
866
867     SQLiteStatement domainsToUpdateStatement(m_database, makeString("UPDATE ObservedDomains SET grandfathered = 1 WHERE registrableDomain IN (", domainsToString(domains), ")"));
868     if (domainsToUpdateStatement.prepare() != SQLITE_OK
869         || domainsToUpdateStatement.step() != SQLITE_DONE) {
870         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::grandfatherDataForDomains failed, error message: %{public}s", this, m_database.lastErrorMsg());
871         ASSERT_NOT_REACHED();
872     }
873 }
874
875 Vector<RegistrableDomain> ResourceLoadStatisticsDatabaseStore::ensurePrevalentResourcesForDebugMode()
876 {
877     ASSERT(!RunLoop::isMain());
878
879     if (!debugModeEnabled())
880         return { };
881
882     Vector<RegistrableDomain> primaryDomainsToBlock;
883     primaryDomainsToBlock.reserveInitialCapacity(2);
884
885     ensureResourceStatisticsForRegistrableDomain(debugStaticPrevalentResource());
886     setPrevalentResource(debugStaticPrevalentResource(), ResourceLoadPrevalence::High);
887     primaryDomainsToBlock.uncheckedAppend(debugStaticPrevalentResource());
888
889     if (!debugManualPrevalentResource().isEmpty()) {
890         ensureResourceStatisticsForRegistrableDomain(debugManualPrevalentResource());
891         setPrevalentResource(debugManualPrevalentResource(), ResourceLoadPrevalence::High);
892         primaryDomainsToBlock.uncheckedAppend(debugManualPrevalentResource());
893 #if !RELEASE_LOG_DISABLED
894         RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "Did set %{public}s as prevalent resource for the purposes of ITP Debug Mode.", debugManualPrevalentResource().string().utf8().data());
895 #endif
896     }
897
898     return primaryDomainsToBlock;
899 }
900
901 void ResourceLoadStatisticsDatabaseStore::logFrameNavigation(const RegistrableDomain& targetDomain, const RegistrableDomain& topFrameDomain, const RegistrableDomain& sourceDomain, bool isRedirect, bool isMainFrame)
902 {
903     ASSERT(!RunLoop::isMain());
904
905     bool areTargetAndTopFrameDomainsSameSite = targetDomain == topFrameDomain;
906     bool areTargetAndSourceDomainsSameSite = targetDomain == sourceDomain;
907
908     bool statisticsWereUpdated = false;
909     if (!isMainFrame && !(areTargetAndTopFrameDomainsSameSite || areTargetAndSourceDomainsSameSite)) {
910         auto targetResult = ensureResourceStatisticsForRegistrableDomain(targetDomain);
911         updateLastSeen(targetDomain, ResourceLoadStatistics::reduceTimeResolution(WallTime::now()));
912
913         if (!relationshipExists(m_subframeUnderTopFrameDomainExists, targetResult.second, topFrameDomain)) {
914             insertDomainRelationship(m_subframeUnderTopFrameDomains, targetResult.second, topFrameDomain);
915             statisticsWereUpdated = true;
916         }
917     }
918
919     if (isRedirect && !areTargetAndSourceDomainsSameSite) {
920         if (isMainFrame) {
921             auto redirectingDomainResult = ensureResourceStatisticsForRegistrableDomain(sourceDomain);
922             if (!relationshipExists(m_topFrameUniqueRedirectsToExists, redirectingDomainResult.second, targetDomain)) {
923                 insertDomainRelationship(m_topFrameUniqueRedirectsTo, redirectingDomainResult.second, targetDomain);
924                 statisticsWereUpdated = true;
925             }
926
927             auto targetResult = ensureResourceStatisticsForRegistrableDomain(targetDomain);
928             if (!relationshipExists(m_topFrameUniqueRedirectsFromExists, targetResult.second, sourceDomain)) {
929                 insertDomainRelationship(m_topFrameUniqueRedirectsFrom, targetResult.second, sourceDomain);
930                 statisticsWereUpdated = true;
931             }
932         } else {
933             auto redirectingDomainResult = ensureResourceStatisticsForRegistrableDomain(sourceDomain);
934             if (!relationshipExists(m_subresourceUniqueRedirectsToExists, redirectingDomainResult.second, targetDomain)) {
935                 insertDomainRelationship(m_subresourceUniqueRedirectsTo, redirectingDomainResult.second, targetDomain);
936                 statisticsWereUpdated = true;
937             }
938
939             auto targetResult = ensureResourceStatisticsForRegistrableDomain(targetDomain);
940             if (!relationshipExists(m_subresourceUniqueRedirectsFromExists, targetResult.second, sourceDomain)) {
941                 insertDomainRelationship(m_subresourceUniqueRedirectsFrom, targetResult.second, sourceDomain);
942                 statisticsWereUpdated = true;
943             }
944         }
945     }
946
947     if (statisticsWereUpdated)
948         scheduleStatisticsProcessingRequestIfNecessary();
949 }
950
951 void ResourceLoadStatisticsDatabaseStore::logSubresourceLoading(const SubResourceDomain& targetDomain, const TopFrameDomain& topFrameDomain, WallTime lastSeen)
952 {
953     ASSERT(!RunLoop::isMain());
954
955     auto result = ensureResourceStatisticsForRegistrableDomain(targetDomain);
956     updateLastSeen(targetDomain, lastSeen);
957
958     auto targetDomainID = result.second;
959     if (!relationshipExists(m_subresourceUnderTopFrameDomainExists, targetDomainID, topFrameDomain)) {
960         insertDomainRelationship(m_subresourceUnderTopFrameDomains, targetDomainID, topFrameDomain);
961         scheduleStatisticsProcessingRequestIfNecessary();
962     }
963 }
964
965 void ResourceLoadStatisticsDatabaseStore::logSubresourceRedirect(const RedirectedFromDomain& sourceDomain, const RedirectedToDomain& targetDomain)
966 {
967     ASSERT(!RunLoop::isMain());
968
969     auto sourceDomainResult = ensureResourceStatisticsForRegistrableDomain(sourceDomain);
970     auto targetDomainResult = ensureResourceStatisticsForRegistrableDomain(targetDomain);
971
972     bool isNewRedirectToEntry = false;
973     if (!relationshipExists(m_subresourceUniqueRedirectsToExists, sourceDomainResult.second, targetDomain)) {
974         insertDomainRelationship(m_subresourceUniqueRedirectsTo, sourceDomainResult.second, targetDomain);
975         isNewRedirectToEntry = true;
976     }
977
978     bool isNewRedirectFromEntry = false;
979     if (!relationshipExists(m_subresourceUniqueRedirectsFromExists, targetDomainResult.second, sourceDomain)) {
980         insertDomainRelationship(m_subresourceUniqueRedirectsFrom, targetDomainResult.second, sourceDomain);
981         isNewRedirectFromEntry = true;
982     }
983
984     if (isNewRedirectToEntry || isNewRedirectFromEntry)
985         scheduleStatisticsProcessingRequestIfNecessary();
986 }
987
988 void ResourceLoadStatisticsDatabaseStore::logCrossSiteLoadWithLinkDecoration(const NavigatedFromDomain& fromDomain, const NavigatedToDomain& toDomain)
989 {
990     ASSERT(!RunLoop::isMain());
991     ASSERT(fromDomain != toDomain);
992
993     auto fromDomainResult = ensureResourceStatisticsForRegistrableDomain(fromDomain);
994
995     if (!relationshipExists(m_topFrameLinkDecorationsFromExists, fromDomainResult.second, toDomain)) {
996         insertDomainRelationship(m_topFrameLinkDecorationsFrom, fromDomainResult.second, toDomain);
997         scheduleStatisticsProcessingRequestIfNecessary();
998     }
999 }
1000
1001 void ResourceLoadStatisticsDatabaseStore::setUserInteraction(const RegistrableDomain& domain, bool hadUserInteraction, WallTime mostRecentInteraction)
1002 {
1003     ASSERT(!RunLoop::isMain());
1004
1005     if (m_mostRecentUserInteractionStatement.bindInt(1, hadUserInteraction)
1006         || m_mostRecentUserInteractionStatement.bindDouble(2, mostRecentInteraction.secondsSinceEpoch().value()) != SQLITE_OK
1007         || m_mostRecentUserInteractionStatement.bindText(3, domain.string()) != SQLITE_OK
1008         || m_mostRecentUserInteractionStatement.step() != SQLITE_DONE) {
1009         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::setUserInteraction, error message: %{public}s", this, m_database.lastErrorMsg());
1010         ASSERT_NOT_REACHED();
1011         return;
1012     }
1013
1014     int resetResult = m_mostRecentUserInteractionStatement.reset();
1015     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1016 }
1017
1018 void ResourceLoadStatisticsDatabaseStore::logUserInteraction(const TopFrameDomain& domain)
1019 {
1020     ASSERT(!RunLoop::isMain());
1021
1022     ensureResourceStatisticsForRegistrableDomain(domain);
1023     setUserInteraction(domain, true, WallTime::now());
1024 }
1025
1026 void ResourceLoadStatisticsDatabaseStore::clearUserInteraction(const RegistrableDomain& domain)
1027 {
1028     ASSERT(!RunLoop::isMain());
1029
1030     auto targetResult = ensureResourceStatisticsForRegistrableDomain(domain);
1031     setUserInteraction(domain, false, { });
1032
1033     SQLiteStatement removeStorageAccess(m_database, makeString("DELETE FROM StorageAccessUnderTopFrameDomains WHERE domainID = ", String::number(targetResult.second)));
1034     if (removeStorageAccess.prepare() != SQLITE_OK
1035         || removeStorageAccess.step() != SQLITE_DONE) {
1036         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::logUserInteraction failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
1037         ASSERT_NOT_REACHED();
1038     }
1039 }
1040
1041 bool ResourceLoadStatisticsDatabaseStore::hasHadUserInteraction(const RegistrableDomain& domain)
1042 {
1043     ASSERT(!RunLoop::isMain());
1044
1045     if (m_hadUserInteractionStatement.bindText(1, domain.string()) != SQLITE_OK
1046         || m_hadUserInteractionStatement.step() != SQLITE_DONE) {
1047         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::m_updatePrevalentResourceStatement failed, error message: %{public}s", this, m_database.lastErrorMsg());
1048         ASSERT_NOT_REACHED();
1049         return false;
1050     }
1051
1052     bool hadUserInteraction = !!m_hadUserInteractionStatement.getColumnInt(0);
1053     if (!hadUserInteraction)
1054         return false;
1055     
1056     WallTime mostRecentUserInteractionTime = WallTime::fromRawSeconds(m_hadUserInteractionStatement.getColumnDouble(1));
1057
1058     if (hasStatisticsExpired(mostRecentUserInteractionTime)) {
1059         // Drop privacy sensitive data because we no longer need it.
1060         // Set timestamp to 0 so that statistics merge will know
1061         // it has been reset as opposed to its default -1.
1062         clearUserInteraction(domain);
1063         hadUserInteraction = false;
1064     }
1065     
1066     return hadUserInteraction;
1067 }
1068
1069 void ResourceLoadStatisticsDatabaseStore::setPrevalentResource(const RegistrableDomain& domain, ResourceLoadPrevalence newPrevalence)
1070 {
1071     ASSERT(!RunLoop::isMain());
1072     if (shouldSkip(domain))
1073         return;
1074
1075     if (m_updatePrevalentResourceStatement.bindInt(1, 1) != SQLITE_OK
1076         || m_updatePrevalentResourceStatement.bindText(2, domain.string()) != SQLITE_OK
1077         || m_updatePrevalentResourceStatement.step() != SQLITE_DONE) {
1078         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::m_updatePrevalentResourceStatement failed, error message: %{public}s", this, m_database.lastErrorMsg());
1079         ASSERT_NOT_REACHED();
1080         return;
1081     }
1082     
1083     int resetResult = m_updatePrevalentResourceStatement.reset();
1084     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1085
1086     if (newPrevalence == ResourceLoadPrevalence::VeryHigh) {
1087         if (m_updateVeryPrevalentResourceStatement.bindInt(1, 1) != SQLITE_OK
1088             || m_updateVeryPrevalentResourceStatement.bindText(2, domain.string()) != SQLITE_OK
1089             || m_updateVeryPrevalentResourceStatement.step() != SQLITE_DONE) {
1090             RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::m_updateVeryPrevalentResourceStatement failed, error message: %{public}s", this, m_database.lastErrorMsg());
1091             ASSERT_NOT_REACHED();
1092             return;
1093         }
1094
1095         int resetResult = m_updateVeryPrevalentResourceStatement.reset();
1096         ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1097     }
1098
1099     StdSet<unsigned> nonPrevalentRedirectionSources;
1100     recursivelyFindNonPrevalentDomainsThatRedirectedToThisDomain(domainID(domain), nonPrevalentRedirectionSources, 0);
1101     setDomainsAsPrevalent(WTFMove(nonPrevalentRedirectionSources));
1102 }
1103
1104 void ResourceLoadStatisticsDatabaseStore::setDomainsAsPrevalent(StdSet<unsigned>&& domains)
1105 {
1106     ASSERT(!RunLoop::isMain());
1107
1108     SQLiteStatement domainsToUpdateStatement(m_database, makeString("UPDATE ObservedDomains SET isPrevalent = 1 WHERE domainID IN (", buildList(WTF::IteratorRange<StdSet<unsigned>::iterator>(domains.begin(), domains.end())), ")"));
1109     if (domainsToUpdateStatement.prepare() != SQLITE_OK
1110         || domainsToUpdateStatement.step() != SQLITE_DONE) {
1111         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::setDomainsAsPrevalent failed, error message: %{public}s", this, m_database.lastErrorMsg());
1112         ASSERT_NOT_REACHED();
1113         return;
1114     }
1115 }
1116
1117 String ResourceLoadStatisticsDatabaseStore::dumpResourceLoadStatistics() const
1118 {
1119     ASSERT(!RunLoop::isMain());
1120
1121     // FIXME(195088): Implement SQLite-based dumping routines.
1122     ASSERT_NOT_REACHED();
1123     return { };
1124 }
1125
1126 bool ResourceLoadStatisticsDatabaseStore::predicateValueForDomain(WebCore::SQLiteStatement& predicateStatement, const RegistrableDomain& domain) const
1127 {
1128     ASSERT(!RunLoop::isMain());
1129     
1130     if (predicateStatement.bindText(1, domain.string()) != SQLITE_OK
1131         || predicateStatement.step() != SQLITE_DONE) {
1132         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::predicateValueForDomain failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
1133         ASSERT_NOT_REACHED();
1134         return false;
1135     }
1136
1137     bool result = !!predicateStatement.getColumnInt(0);
1138     int resetResult = predicateStatement.reset();
1139     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1140     return result;
1141 }
1142
1143 bool ResourceLoadStatisticsDatabaseStore::isPrevalentResource(const RegistrableDomain& domain) const
1144 {
1145     ASSERT(!RunLoop::isMain());
1146
1147     if (shouldSkip(domain))
1148         return false;
1149
1150     return predicateValueForDomain(m_isPrevalentResourceStatement, domain);
1151 }
1152
1153 bool ResourceLoadStatisticsDatabaseStore::isVeryPrevalentResource(const RegistrableDomain& domain) const
1154 {
1155     ASSERT(!RunLoop::isMain());
1156
1157     if (shouldSkip(domain))
1158         return false;
1159
1160     return predicateValueForDomain(m_isVeryPrevalentResourceStatement, domain);
1161 }
1162
1163 bool ResourceLoadStatisticsDatabaseStore::isRegisteredAsSubresourceUnder(const SubResourceDomain& subresourceDomain, const TopFrameDomain& topFrameDomain) const
1164 {
1165     ASSERT(!RunLoop::isMain());
1166
1167     return relationshipExists(m_subresourceUnderTopFrameDomainExists, domainID(subresourceDomain), topFrameDomain);
1168 }
1169
1170 bool ResourceLoadStatisticsDatabaseStore::isRegisteredAsSubFrameUnder(const SubFrameDomain& subFrameDomain, const TopFrameDomain& topFrameDomain) const
1171 {
1172     ASSERT(!RunLoop::isMain());
1173
1174     return relationshipExists(m_subframeUnderTopFrameDomainExists, domainID(subFrameDomain), topFrameDomain);
1175 }
1176
1177 bool ResourceLoadStatisticsDatabaseStore::isRegisteredAsRedirectingTo(const RedirectedFromDomain& redirectedFromDomain, const RedirectedToDomain& redirectedToDomain) const
1178 {
1179     ASSERT(!RunLoop::isMain());
1180
1181     return relationshipExists(m_subresourceUniqueRedirectsToExists, domainID(redirectedFromDomain), redirectedToDomain);
1182 }
1183
1184 void ResourceLoadStatisticsDatabaseStore::clearPrevalentResource(const RegistrableDomain& domain)
1185 {
1186     ASSERT(!RunLoop::isMain());
1187
1188     ensureResourceStatisticsForRegistrableDomain(domain);
1189     
1190     if (m_clearPrevalentResourceStatement.bindText(1, domain.string()) != SQLITE_OK
1191         || m_clearPrevalentResourceStatement.step() != SQLITE_DONE) {
1192         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::clearPrevalentResource, error message: %{public}s", this, m_database.lastErrorMsg());
1193         ASSERT_NOT_REACHED();
1194         return;
1195     }
1196     
1197     int resetResult = m_clearPrevalentResourceStatement.reset();
1198     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1199 }
1200
1201 void ResourceLoadStatisticsDatabaseStore::setGrandfathered(const RegistrableDomain& domain, bool value)
1202 {
1203     ASSERT(!RunLoop::isMain());
1204
1205     ensureResourceStatisticsForRegistrableDomain(domain);
1206     
1207     if (m_updateGrandfatheredStatement.bindInt(1, value) != SQLITE_OK
1208         || m_updateGrandfatheredStatement.bindText(1, domain.string()) != SQLITE_OK
1209         || m_updateGrandfatheredStatement.step() != SQLITE_DONE) {
1210         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::setGrandfathered failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
1211         ASSERT_NOT_REACHED();
1212         return;
1213     }
1214     
1215     int resetResult = m_updateGrandfatheredStatement.reset();
1216     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1217 }
1218
1219 bool ResourceLoadStatisticsDatabaseStore::isGrandfathered(const RegistrableDomain& domain) const
1220 {
1221     ASSERT(!RunLoop::isMain());
1222
1223     return predicateValueForDomain(m_isGrandfatheredStatement, domain);
1224 }
1225
1226 void ResourceLoadStatisticsDatabaseStore::setSubframeUnderTopFrameDomain(const SubFrameDomain& subFrameDomain, const TopFrameDomain& topFrameDomain)
1227 {
1228     ASSERT(!RunLoop::isMain());
1229
1230     auto result = ensureResourceStatisticsForRegistrableDomain(subFrameDomain);
1231
1232     // For consistency, make sure we also have a statistics entry for the top frame domain.
1233     ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
1234
1235     insertDomainRelationship(m_subframeUnderTopFrameDomains, result.second, topFrameDomain);
1236 }
1237
1238 void ResourceLoadStatisticsDatabaseStore::setSubresourceUnderTopFrameDomain(const SubResourceDomain& subresourceDomain, const TopFrameDomain& topFrameDomain)
1239 {
1240     ASSERT(!RunLoop::isMain());
1241
1242     auto result = ensureResourceStatisticsForRegistrableDomain(subresourceDomain);
1243
1244     // For consistency, make sure we also have a statistics entry for the top frame domain.
1245     ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
1246
1247     insertDomainRelationship(m_subresourceUnderTopFrameDomains, result.second, topFrameDomain);
1248 }
1249
1250 void ResourceLoadStatisticsDatabaseStore::setSubresourceUniqueRedirectTo(const SubResourceDomain& subresourceDomain, const RedirectDomain& redirectDomain)
1251 {
1252     ASSERT(!RunLoop::isMain());
1253
1254     auto result = ensureResourceStatisticsForRegistrableDomain(subresourceDomain);
1255
1256     // For consistency, make sure we also have a statistics entry for the redirect domain.
1257     ensureResourceStatisticsForRegistrableDomain(redirectDomain);
1258
1259     insertDomainRelationship(m_subresourceUniqueRedirectsTo, result.second, redirectDomain);
1260 }
1261
1262 void ResourceLoadStatisticsDatabaseStore::setSubresourceUniqueRedirectFrom(const SubResourceDomain& subresourceDomain, const RedirectDomain& redirectDomain)
1263 {
1264     ASSERT(!RunLoop::isMain());
1265
1266     auto result = ensureResourceStatisticsForRegistrableDomain(subresourceDomain);
1267
1268     // For consistency, make sure we also have a statistics entry for the redirect domain.
1269     ensureResourceStatisticsForRegistrableDomain(redirectDomain);
1270
1271     insertDomainRelationship(m_subresourceUniqueRedirectsFrom, result.second, redirectDomain);
1272 }
1273
1274 void ResourceLoadStatisticsDatabaseStore::setTopFrameUniqueRedirectTo(const TopFrameDomain& topFrameDomain, const RedirectDomain& redirectDomain)
1275 {
1276     ASSERT(!RunLoop::isMain());
1277
1278     auto result = ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
1279
1280     // For consistency, make sure we also have a statistics entry for the redirect domain.
1281     ensureResourceStatisticsForRegistrableDomain(redirectDomain);
1282
1283     insertDomainRelationship(m_topFrameUniqueRedirectsTo, result.second, redirectDomain);
1284 }
1285
1286 void ResourceLoadStatisticsDatabaseStore::setTopFrameUniqueRedirectFrom(const TopFrameDomain& topFrameDomain, const RedirectDomain& redirectDomain)
1287 {
1288     ASSERT(!RunLoop::isMain());
1289
1290     auto result = ensureResourceStatisticsForRegistrableDomain(topFrameDomain);
1291
1292     // For consistency, make sure we also have a statistics entry for the redirect domain.
1293     ensureResourceStatisticsForRegistrableDomain(redirectDomain);
1294
1295     insertDomainRelationship(m_topFrameUniqueRedirectsFrom, result.second, redirectDomain);
1296 }
1297
1298 std::pair<ResourceLoadStatisticsDatabaseStore::AddedRecord, unsigned> ResourceLoadStatisticsDatabaseStore::ensureResourceStatisticsForRegistrableDomain(const RegistrableDomain& domain)
1299 {
1300     ASSERT(!RunLoop::isMain());
1301
1302     if (m_domainIDFromStringStatement.bindText(1, domain.string()) != SQLITE_OK) {
1303         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::createSchema failed, error message: %{public}s", this, m_database.lastErrorMsg());
1304         ASSERT_NOT_REACHED();
1305         return { AddedRecord::No, 0 };
1306     }
1307     
1308     if (m_domainIDFromStringStatement.step() == SQLITE_ROW) {
1309         unsigned domainID = m_domainIDFromStringStatement.getColumnInt(0);
1310
1311         int resetResult = m_domainIDFromStringStatement.reset();
1312         ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1313         return { AddedRecord::No, domainID };
1314     }
1315
1316     int resetResult = m_domainIDFromStringStatement.reset();
1317     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1318
1319     ResourceLoadStatistics newObservation(domain);
1320     insertObservedDomain(newObservation);
1321
1322     return { AddedRecord::Yes, domainID(domain) };
1323 }
1324
1325 void ResourceLoadStatisticsDatabaseStore::clear(CompletionHandler<void()>&& completionHandler)
1326 {
1327     ASSERT(!RunLoop::isMain());
1328
1329     clearOperatingDates();
1330
1331     auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1332
1333     removeAllStorageAccess([callbackAggregator = callbackAggregator.copyRef()] { });
1334
1335     auto primaryDomainsToBlock = ensurePrevalentResourcesForDebugMode();
1336     updateCookieBlockingForDomains(primaryDomainsToBlock, [callbackAggregator = callbackAggregator.copyRef()] { });
1337 }
1338
1339 ResourceLoadStatisticsDatabaseStore::CookieTreatmentResult ResourceLoadStatisticsDatabaseStore::cookieTreatmentForOrigin(const RegistrableDomain& domain) const
1340 {
1341     ASSERT(!RunLoop::isMain());
1342
1343     SQLiteStatement statement(m_database, makeString("SELECT isPrevalent, hadUserInteraction FROM ObservedDomains WHERE registrableDomain = ", domain.string()));
1344     if (statement.prepare() != SQLITE_OK
1345         || statement.step() != SQLITE_ROW) {
1346         return CookieTreatmentResult::Allow;
1347     }
1348     
1349     bool isPrevalent = !!statement.getColumnInt(0);
1350     if (!isPrevalent)
1351         return CookieTreatmentResult::Allow;
1352
1353     bool hadUserInteraction = statement.getColumnInt(1) ? true : false;
1354     return hadUserInteraction ? CookieTreatmentResult::BlockAndKeep : CookieTreatmentResult::BlockAndPurge;
1355 }
1356     
1357 bool ResourceLoadStatisticsDatabaseStore::hasUserGrantedStorageAccessThroughPrompt(unsigned requestingDomainID, const RegistrableDomain& firstPartyDomain) const
1358 {
1359     ASSERT(!RunLoop::isMain());
1360
1361     auto firstPartyPrimaryDomainID = domainID(firstPartyDomain);
1362
1363     SQLiteStatement statement(m_database, makeString("SELECT COUNT(*) FROM StorageAccessUnderTopFrameDomains WHERE domainID = ", String::number(requestingDomainID), " AND topLevelDomainID = ", String::number(firstPartyPrimaryDomainID)));
1364     if (statement.prepare() != SQLITE_OK
1365         || statement.step() != SQLITE_ROW)
1366         return false;
1367
1368     return !statement.getColumnInt(0);
1369 }
1370
1371 Vector<RegistrableDomain> ResourceLoadStatisticsDatabaseStore::domainsToBlock() const
1372 {
1373     ASSERT(!RunLoop::isMain());
1374
1375     Vector<RegistrableDomain> results;
1376     SQLiteStatement statement(m_database, "SELECT registrableDomain FROM ObservedDomains WHERE isPrevalent = 1"_s);
1377     if (statement.prepare() != SQLITE_OK)
1378         return results;
1379     
1380     while (statement.step() == SQLITE_ROW)
1381         results.append(RegistrableDomain::uncheckedCreateFromRegistrableDomainString(statement.getColumnText(0)));
1382
1383     return results;
1384 }
1385
1386 void ResourceLoadStatisticsDatabaseStore::updateCookieBlocking(CompletionHandler<void()>&& completionHandler)
1387 {
1388     ASSERT(!RunLoop::isMain());
1389
1390     auto domainsToBlock = this->domainsToBlock();
1391
1392     if (domainsToBlock.isEmpty()) {
1393         completionHandler();
1394         return;
1395     }
1396
1397     if (debugLoggingEnabled() && !domainsToBlock.isEmpty())
1398         debugLogDomainsInBatches("block", domainsToBlock);
1399
1400     RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), store = makeRef(store()), domainsToBlock = crossThreadCopy(domainsToBlock), completionHandler = WTFMove(completionHandler)] () mutable {
1401         store->callUpdatePrevalentDomainsToBlockCookiesForHandler(domainsToBlock, [weakThis = WTFMove(weakThis), store = store.copyRef(), completionHandler = WTFMove(completionHandler)]() mutable {
1402             store->statisticsQueue().dispatch([weakThis = WTFMove(weakThis), completionHandler = WTFMove(completionHandler)]() mutable {
1403                 completionHandler();
1404                 if (!weakThis)
1405                     return;
1406 #if !RELEASE_LOG_DISABLED
1407                 RELEASE_LOG_INFO_IF(weakThis->debugLoggingEnabled(), ResourceLoadStatisticsDebug, "Done updating cookie blocking.");
1408 #endif
1409             });
1410         });
1411     });
1412 }
1413
1414 Vector<ResourceLoadStatisticsDatabaseStore::PrevalentDomainData> ResourceLoadStatisticsDatabaseStore::prevalentDomains() const
1415 {
1416     ASSERT(!RunLoop::isMain());
1417     
1418     Vector<PrevalentDomainData> results;
1419     SQLiteStatement statement(m_database, "SELECT domainID, registrableDomain, mostRecentUserInteractionTime, hadUserInteraction, grandfathered FROM ObservedDomains WHERE isPrevalent = 1"_s);
1420     if (statement.prepare() != SQLITE_OK)
1421         return results;
1422     
1423     while (statement.step() == SQLITE_ROW) {
1424         results.append({ static_cast<unsigned>(statement.getColumnInt(0))
1425             , RegistrableDomain::uncheckedCreateFromRegistrableDomainString(statement.getColumnText(1))
1426             , WallTime::fromRawSeconds(statement.getColumnDouble(2))
1427             , statement.getColumnInt(3) ? true : false
1428             , statement.getColumnInt(4) ? true : false
1429         });
1430     }
1431     
1432     return results;
1433 }
1434
1435 Vector<unsigned> ResourceLoadStatisticsDatabaseStore::findExpiredUserInteractions() const
1436 {
1437     ASSERT(!RunLoop::isMain());
1438
1439     Vector<unsigned> results;
1440     Optional<Seconds> expirationDateTime = statisticsEpirationTime();
1441     if (!expirationDateTime)
1442         return results;
1443
1444     if (m_findExpiredUserInteractionStatement.bindDouble(1, expirationDateTime.value().value()) != SQLITE_OK)
1445         return results;
1446
1447     while (m_findExpiredUserInteractionStatement.step() == SQLITE_ROW)
1448         results.append(m_findExpiredUserInteractionStatement.getColumnInt(0));
1449
1450     int resetResult = m_findExpiredUserInteractionStatement.reset();
1451     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1452
1453     return results;
1454 }
1455
1456 void ResourceLoadStatisticsDatabaseStore::clearExpiredUserInteractions()
1457 {
1458     ASSERT(!RunLoop::isMain());
1459
1460     auto expiredRecords = findExpiredUserInteractions();
1461     if (expiredRecords.isEmpty())
1462         return;
1463
1464     auto expiredRecordIDs = buildList(WTF::IteratorRange<Vector<unsigned>::iterator>(expiredRecords.begin(), expiredRecords.end()));
1465
1466     SQLiteStatement clearExpiredInteraction(m_database, makeString("UPDATE ObservedDomains SET mostRecentUserInteractionTime = 0, hadUserInteraction = 1 WHERE domainID IN (", expiredRecordIDs, ")"));
1467     if (clearExpiredInteraction.prepare() != SQLITE_OK)
1468         return;
1469
1470     SQLiteStatement removeStorageAccess(m_database, makeString("DELETE FROM StorageAccessUnderTopFrameDomains ", expiredRecordIDs, ")"));
1471     if (removeStorageAccess.prepare() != SQLITE_OK)
1472         return;
1473
1474     if (clearExpiredInteraction.step() != SQLITE_DONE
1475         || removeStorageAccess.step() != SQLITE_DONE) {
1476         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::logUserInteraction failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
1477         ASSERT_NOT_REACHED();
1478     }
1479 }
1480
1481 void ResourceLoadStatisticsDatabaseStore::clearGrandfathering(Vector<unsigned>&& domainIDsToClear)
1482 {
1483     ASSERT(!RunLoop::isMain());
1484
1485     if (domainIDsToClear.isEmpty())
1486         return;
1487
1488     auto listToClear = buildList(WTF::IteratorRange<Vector<unsigned>::iterator>(domainIDsToClear.begin(), domainIDsToClear.end()));
1489
1490     SQLiteStatement clearGrandfatheringStatement(m_database, makeString("UPDATE ObservedDomains SET grandfathered = 0 WHERE domainID IN (", listToClear, ")"));
1491     if (clearGrandfatheringStatement.prepare() != SQLITE_OK)
1492         return;
1493     
1494     if (clearGrandfatheringStatement.step() != SQLITE_DONE) {
1495         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::clearGrandfathering failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
1496         ASSERT_NOT_REACHED();
1497     }
1498 }
1499
1500 Vector<RegistrableDomain> ResourceLoadStatisticsDatabaseStore::registrableDomainsToRemoveWebsiteDataFor()
1501 {
1502     ASSERT(!RunLoop::isMain());
1503
1504     bool shouldCheckForGrandfathering = endOfGrandfatheringTimestamp() > WallTime::now();
1505     bool shouldClearGrandfathering = !shouldCheckForGrandfathering && endOfGrandfatheringTimestamp();
1506
1507     if (shouldClearGrandfathering)
1508         clearEndOfGrandfatheringTimeStamp();
1509
1510     clearExpiredUserInteractions();
1511     
1512     Vector<RegistrableDomain> prevalentResources;
1513
1514     Vector<PrevalentDomainData> prevalentDomains = this->prevalentDomains();
1515     Vector<unsigned> domainIDsToClearGrandfathering;
1516     for (auto& statistic : prevalentDomains) {
1517         if (!statistic.hadUserInteraction && (!shouldCheckForGrandfathering || !statistic.grandfathered))
1518             prevalentResources.append(statistic.registerableDomain);
1519
1520         if (shouldClearGrandfathering && statistic.grandfathered)
1521             domainIDsToClearGrandfathering.append(statistic.domainID);
1522     }
1523
1524     clearGrandfathering(WTFMove(domainIDsToClearGrandfathering));
1525     
1526     return prevalentResources;
1527 }
1528
1529 void ResourceLoadStatisticsDatabaseStore::pruneStatisticsIfNeeded()
1530 {
1531     ASSERT(!RunLoop::isMain());
1532
1533     unsigned count = 0;
1534     if (m_observedDomainCount.step() == SQLITE_ROW)
1535         count = m_observedDomainCount.getColumnInt(0);
1536
1537     int resetResult = m_observedDomainCount.reset();
1538     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1539
1540     if (count <= parameters().maxStatisticsEntries)
1541         return;
1542
1543     ASSERT(parameters().pruneEntriesDownTo <= parameters().maxStatisticsEntries);
1544
1545     size_t countLeftToPrune = count - parameters().pruneEntriesDownTo;
1546     ASSERT(countLeftToPrune);
1547
1548     SQLiteStatement recordsToPrune(m_database, makeString("SELECT domainID FROM ObservedDomains ORDER BY hadUserInteraction, isPrevalent, lastSeen DESC LIMIT ", String::number(countLeftToPrune)));
1549     if (recordsToPrune.prepare() != SQLITE_OK) {
1550         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::pruneStatisticsIfNeeded failed, error message: %{public}s", this, m_database.lastErrorMsg());
1551         ASSERT_NOT_REACHED();
1552         return;
1553     }
1554
1555     Vector<unsigned> entriesToPrune;
1556     while (recordsToPrune.step() == SQLITE_ROW)
1557         entriesToPrune.append(recordsToPrune.getColumnInt(0));
1558
1559     auto listToPrune = buildList(WTF::IteratorRange<Vector<unsigned>::iterator>(entriesToPrune.begin(), entriesToPrune.end()));
1560
1561     SQLiteStatement pruneCommand(m_database, makeString("DELETE from ObservedDomains WHERE domainID IN (", listToPrune, ")"));
1562     if (pruneCommand.prepare() != SQLITE_OK
1563         || pruneCommand.step() != SQLITE_DONE) {
1564         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::pruneStatisticsIfNeeded failed, error message: %{public}s", this, m_database.lastErrorMsg());
1565         ASSERT_NOT_REACHED();
1566         return;
1567     }
1568 }
1569
1570 void ResourceLoadStatisticsDatabaseStore::updateLastSeen(const RegistrableDomain& domain, WallTime lastSeen)
1571 {
1572     ASSERT(!RunLoop::isMain());
1573
1574     if (m_updateLastSeenStatement.bindDouble(1, lastSeen.secondsSinceEpoch().value()) != SQLITE_OK
1575         || m_updateLastSeenStatement.bindText(2, domain.string()) != SQLITE_OK
1576         || m_updateLastSeenStatement.step() != SQLITE_DONE) {
1577         RELEASE_LOG_ERROR(Network, "%p - ResourceLoadStatisticsDatabaseStore::updateLastSeen failed to bind, error message: %{public}s", this, m_database.lastErrorMsg());
1578         ASSERT_NOT_REACHED();
1579         return;
1580     }
1581     
1582     int resetResult = m_updateLastSeenStatement.reset();
1583     ASSERT_UNUSED(resetResult, resetResult == SQLITE_OK);
1584 }
1585
1586 void ResourceLoadStatisticsDatabaseStore::setLastSeen(const RegistrableDomain& domain, Seconds seconds)
1587 {
1588     ASSERT(!RunLoop::isMain());
1589
1590     ensureResourceStatisticsForRegistrableDomain(domain);
1591     updateLastSeen(domain, WallTime::fromRawSeconds(seconds.seconds()));
1592 }
1593
1594 void ResourceLoadStatisticsDatabaseStore::setPrevalentResource(const RegistrableDomain& domain)
1595 {
1596     ASSERT(!RunLoop::isMain());
1597
1598     if (shouldSkip(domain))
1599         return;
1600
1601     ensureResourceStatisticsForRegistrableDomain(domain);
1602     setPrevalentResource(domain, ResourceLoadPrevalence::High);
1603 }
1604
1605 void ResourceLoadStatisticsDatabaseStore::setVeryPrevalentResource(const RegistrableDomain& domain)
1606 {
1607     ASSERT(!RunLoop::isMain());
1608
1609     if (shouldSkip(domain))
1610         return;
1611     
1612     ensureResourceStatisticsForRegistrableDomain(domain);
1613     setPrevalentResource(domain, ResourceLoadPrevalence::VeryHigh);
1614 }
1615
1616 } // namespace WebKit
1617
1618 #endif