Unreviewed, rolling out r217647.
[WebKit-https.git] / Source / WebCore / loader / ResourceLoadObserver.cpp
1 /*
2  * Copyright (C) 2016-2017 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 "ResourceLoadObserver.h"
28
29 #include "Document.h"
30 #include "Frame.h"
31 #include "Logging.h"
32 #include "MainFrame.h"
33 #include "NetworkStorageSession.h"
34 #include "Page.h"
35 #include "PlatformStrategies.h"
36 #include "PublicSuffix.h"
37 #include "ResourceLoadStatistics.h"
38 #include "ResourceLoadStatisticsStore.h"
39 #include "ResourceRequest.h"
40 #include "ResourceResponse.h"
41 #include "SecurityOrigin.h"
42 #include "Settings.h"
43 #include "SharedBuffer.h"
44 #include "URL.h"
45 #include <wtf/CrossThreadCopier.h>
46 #include <wtf/CurrentTime.h>
47 #include <wtf/NeverDestroyed.h>
48 #include <wtf/WorkQueue.h>
49 #include <wtf/text/StringBuilder.h>
50
51 namespace WebCore {
52
53 // One hour in seconds.
54 static auto timestampResolution = 3600;
55
56 ResourceLoadObserver& ResourceLoadObserver::sharedObserver()
57 {
58     static NeverDestroyed<ResourceLoadObserver> resourceLoadObserver;
59     return resourceLoadObserver;
60 }
61
62 void ResourceLoadObserver::setStatisticsStore(Ref<ResourceLoadStatisticsStore>&& store)
63 {
64     if (m_store && m_queue)
65         m_queue = nullptr;
66     m_store = WTFMove(store);
67 }
68     
69 void ResourceLoadObserver::setStatisticsQueue(Ref<WTF::WorkQueue>&& queue)
70 {
71     ASSERT(!m_queue);
72     m_queue = WTFMove(queue);
73 }
74     
75 void ResourceLoadObserver::clearInMemoryStore()
76 {
77     if (!m_store)
78         return;
79     
80     ASSERT(m_queue);
81     m_queue->dispatch([this] () {
82         m_store->clearInMemory();
83     });
84 }
85     
86 void ResourceLoadObserver::clearInMemoryAndPersistentStore()
87 {
88     if (!m_store)
89         return;
90     
91     ASSERT(m_queue);
92     m_queue->dispatch([this] () {
93         m_store->clearInMemoryAndPersistent();
94     });
95 }
96
97 void ResourceLoadObserver::clearInMemoryAndPersistentStore(std::chrono::system_clock::time_point modifiedSince)
98 {
99     // For now, be conservative and clear everything regardless of modifiedSince
100     UNUSED_PARAM(modifiedSince);
101     clearInMemoryAndPersistentStore();
102 }
103
104 static inline bool is3xxRedirect(const ResourceResponse& response)
105 {
106     return response.httpStatusCode() >= 300 && response.httpStatusCode() <= 399;
107 }
108
109 bool ResourceLoadObserver::shouldLog(Page* page)
110 {
111     // FIXME: Err on the safe side until we have sorted out what to do in worker contexts
112     if (!page)
113         return false;
114     return Settings::resourceLoadStatisticsEnabled()
115         && !page->usesEphemeralSession()
116         && m_store;
117 }
118
119 void ResourceLoadObserver::logFrameNavigation(const Frame& frame, const Frame& topFrame, const ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
120 {
121     ASSERT(frame.document());
122     ASSERT(topFrame.document());
123     ASSERT(topFrame.page());
124     
125     if (!shouldLog(topFrame.page()))
126         return;
127
128     bool isRedirect = is3xxRedirect(redirectResponse);
129     bool isMainFrame = frame.isMainFrame();
130     const URL& sourceURL = frame.document()->url();
131     const URL& targetURL = newRequest.url();
132     const URL& mainFrameURL = topFrame.document()->url();
133     
134     if (!targetURL.isValid() || !mainFrameURL.isValid())
135         return;
136
137     auto targetHost = targetURL.host();
138     auto mainFrameHost = mainFrameURL.host();
139
140     if (targetHost.isEmpty() || mainFrameHost.isEmpty() || targetHost == mainFrameHost || targetHost == sourceURL.host())
141         return;
142
143     auto targetPrimaryDomain = primaryDomain(targetURL);
144     auto mainFramePrimaryDomain = primaryDomain(mainFrameURL);
145     auto sourcePrimaryDomain = primaryDomain(sourceURL);
146     
147     if (targetPrimaryDomain == mainFramePrimaryDomain || targetPrimaryDomain == sourcePrimaryDomain)
148         return;
149     
150     ASSERT(m_queue);
151     m_queue->dispatch([this, isMainFrame, isRedirect, sourcePrimaryDomain = sourcePrimaryDomain.isolatedCopy(), mainFramePrimaryDomain = mainFramePrimaryDomain.isolatedCopy(), targetURL = CrossThreadCopier<URL>::copy(targetURL), mainFrameURL = CrossThreadCopier<URL>::copy(mainFrameURL), targetPrimaryDomain = targetPrimaryDomain.isolatedCopy()] () {
152         
153         auto targetOrigin = SecurityOrigin::create(targetURL);
154         bool shouldFireDataModificationHandler = false;
155         
156         {
157         auto locker = holdLock(m_store->statisticsLock());
158         // We must make a copy here, because later calls to 'ensureResourceStatisticsForPrimaryDomain' will invalidate the returned reference:
159         auto targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
160
161         // Always fire if we have previously removed data records for this domain
162         shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0;
163
164         if (isMainFrame)
165             targetStatistics.topFrameHasBeenNavigatedToBefore = true;
166         else {
167             targetStatistics.subframeHasBeenLoadedBefore = true;
168
169             auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL);
170             auto subframeUnderTopFrameOriginsResult = targetStatistics.subframeUnderTopFrameOrigins.add(mainFramePrimaryDomain);
171             if (subframeUnderTopFrameOriginsResult.isNewEntry)
172                 shouldFireDataModificationHandler = true;
173         }
174         
175         if (isRedirect) {
176             auto& redirectingOriginResourceStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
177             
178             if (m_store->isPrevalentResource(targetPrimaryDomain))
179                 redirectingOriginResourceStatistics.redirectedToOtherPrevalentResourceOrigins.add(targetPrimaryDomain);
180             
181             if (isMainFrame) {
182                 ++targetStatistics.topFrameHasBeenRedirectedTo;
183                 ++redirectingOriginResourceStatistics.topFrameHasBeenRedirectedFrom;
184             } else {
185                 ++targetStatistics.subframeHasBeenRedirectedTo;
186                 ++redirectingOriginResourceStatistics.subframeHasBeenRedirectedFrom;
187                 redirectingOriginResourceStatistics.subframeUniqueRedirectsTo.add(targetPrimaryDomain);
188                 
189                 ++targetStatistics.subframeSubResourceCount;
190             }
191         } else {
192             if (sourcePrimaryDomain.isNull() || sourcePrimaryDomain.isEmpty() || sourcePrimaryDomain == "nullOrigin") {
193                 if (isMainFrame)
194                     ++targetStatistics.topFrameInitialLoadCount;
195                 else
196                     ++targetStatistics.subframeSubResourceCount;
197             } else {
198                 auto& sourceOriginResourceStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
199
200                 if (isMainFrame) {
201                     ++sourceOriginResourceStatistics.topFrameHasBeenNavigatedFrom;
202                     ++targetStatistics.topFrameHasBeenNavigatedTo;
203                 } else {
204                     ++sourceOriginResourceStatistics.subframeHasBeenNavigatedFrom;
205                     ++targetStatistics.subframeHasBeenNavigatedTo;
206                 }
207             }
208         }
209             
210         m_store->setResourceStatisticsForPrimaryDomain(targetPrimaryDomain, WTFMove(targetStatistics));
211         } // Release lock
212         
213         if (shouldFireDataModificationHandler)
214             m_store->fireDataModificationHandler();
215     });
216 }
217     
218 void ResourceLoadObserver::logSubresourceLoading(const Frame* frame, const ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
219 {
220     ASSERT(frame->page());
221
222     if (!shouldLog(frame->page()))
223         return;
224
225     bool isRedirect = is3xxRedirect(redirectResponse);
226     const URL& sourceURL = redirectResponse.url();
227     const URL& targetURL = newRequest.url();
228     const URL& mainFrameURL = frame ? frame->mainFrame().document()->url() : URL();
229     
230     auto targetHost = targetURL.host();
231     auto mainFrameHost = mainFrameURL.host();
232
233     if (targetHost.isEmpty()
234         || mainFrameHost.isEmpty()
235         || targetHost == mainFrameHost
236         || (isRedirect && targetHost == sourceURL.host()))
237         return;
238
239     auto targetPrimaryDomain = primaryDomain(targetURL);
240     auto mainFramePrimaryDomain = primaryDomain(mainFrameURL);
241     auto sourcePrimaryDomain = primaryDomain(sourceURL);
242     
243     if (targetPrimaryDomain == mainFramePrimaryDomain || (isRedirect && targetPrimaryDomain == sourcePrimaryDomain))
244         return;
245     
246     ASSERT(m_queue);
247     m_queue->dispatch([this, isRedirect, sourcePrimaryDomain = sourcePrimaryDomain.isolatedCopy(), mainFramePrimaryDomain = mainFramePrimaryDomain.isolatedCopy(), targetPrimaryDomain = targetPrimaryDomain.isolatedCopy(), mainFrameURL = mainFrameURL.isolatedCopy()] () {
248         
249         bool shouldFireDataModificationHandler = false;
250         
251         {
252         auto locker = holdLock(m_store->statisticsLock());
253         auto& targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
254
255         // Always fire if we have previously removed data records for this domain
256         shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0;
257
258         auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL);
259         auto subresourceUnderTopFrameOriginsResult = targetStatistics.subresourceUnderTopFrameOrigins.add(mainFramePrimaryDomain);
260         if (subresourceUnderTopFrameOriginsResult.isNewEntry)
261             shouldFireDataModificationHandler = true;
262
263         if (isRedirect) {
264             auto& redirectingOriginStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
265             
266             // We just inserted to the store, so we need to reget 'targetStatistics'
267             auto& updatedTargetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
268
269             if (m_store->isPrevalentResource(targetPrimaryDomain))
270                 redirectingOriginStatistics.redirectedToOtherPrevalentResourceOrigins.add(targetPrimaryDomain);
271             
272             ++redirectingOriginStatistics.subresourceHasBeenRedirectedFrom;
273             ++updatedTargetStatistics.subresourceHasBeenRedirectedTo;
274
275             auto subresourceUniqueRedirectsToResult = redirectingOriginStatistics.subresourceUniqueRedirectsTo.add(targetPrimaryDomain);
276             if (subresourceUniqueRedirectsToResult.isNewEntry)
277                 shouldFireDataModificationHandler = true;
278
279             ++updatedTargetStatistics.subresourceHasBeenSubresourceCount;
280
281             auto totalVisited = std::max(m_originsVisitedMap.size(), 1U);
282             
283             updatedTargetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(updatedTargetStatistics.subresourceHasBeenSubresourceCount) / totalVisited;
284         } else {
285             ++targetStatistics.subresourceHasBeenSubresourceCount;
286
287             auto totalVisited = std::max(m_originsVisitedMap.size(), 1U);
288             
289             targetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(targetStatistics.subresourceHasBeenSubresourceCount) / totalVisited;
290         }
291         } // Release lock
292         
293         if (shouldFireDataModificationHandler)
294             m_store->fireDataModificationHandler();
295     });
296 }
297
298 void ResourceLoadObserver::logWebSocketLoading(const Frame* frame, const URL& targetURL)
299 {
300     // FIXME: Web sockets can run in detached frames. Decide how to count such connections.
301     // See LayoutTests/http/tests/websocket/construct-in-detached-frame.html
302     if (!frame)
303         return;
304
305     if (!shouldLog(frame->page()))
306         return;
307
308     const URL& mainFrameURL = frame->mainFrame().document()->url();
309
310     auto targetHost = targetURL.host();
311     auto mainFrameHost = mainFrameURL.host();
312     
313     if (targetHost.isEmpty()
314         || mainFrameHost.isEmpty()
315         || targetHost == mainFrameHost)
316         return;
317     
318     auto targetPrimaryDomain = primaryDomain(targetURL);
319     auto mainFramePrimaryDomain = primaryDomain(mainFrameURL);
320     
321     if (targetPrimaryDomain == mainFramePrimaryDomain)
322         return;
323
324     ASSERT(m_queue);
325     m_queue->dispatch([this, targetPrimaryDomain = targetPrimaryDomain.isolatedCopy(), mainFramePrimaryDomain = mainFramePrimaryDomain.isolatedCopy(), mainFrameURL = mainFrameURL.isolatedCopy()] () {
326         
327         bool shouldFireDataModificationHandler = false;
328         
329         {
330         auto locker = holdLock(m_store->statisticsLock());
331         auto& targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
332
333         // Always fire if we have previously removed data records for this domain
334         shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0;
335         
336         auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL);
337         auto subresourceUnderTopFrameOriginsResult = targetStatistics.subresourceUnderTopFrameOrigins.add(mainFramePrimaryDomain);
338         if (subresourceUnderTopFrameOriginsResult.isNewEntry)
339             shouldFireDataModificationHandler = true;
340
341         ++targetStatistics.subresourceHasBeenSubresourceCount;
342         
343         auto totalVisited = std::max(m_originsVisitedMap.size(), 1U);
344         
345         targetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(targetStatistics.subresourceHasBeenSubresourceCount) / totalVisited;
346         } // Release lock
347         
348         if (shouldFireDataModificationHandler)
349             m_store->fireDataModificationHandler();
350     });
351 }
352
353 static double reduceTimeResolution(double seconds)
354 {
355     return std::floor(seconds / timestampResolution) * timestampResolution;
356 }
357
358 void ResourceLoadObserver::logUserInteractionWithReducedTimeResolution(const Document& document)
359 {
360     ASSERT(document.page());
361
362     if (!shouldLog(document.page()))
363         return;
364
365     auto& url = document.url();
366     if (url.isBlankURL() || url.isEmpty())
367         return;
368
369     auto primaryDomainString = primaryDomain(url);
370     
371     ASSERT(m_queue);
372     m_queue->dispatch([this, primaryDomainString = primaryDomainString.isolatedCopy()] () {
373         {
374         auto locker = holdLock(m_store->statisticsLock());
375         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomainString);
376         double newTimestamp = reduceTimeResolution(WTF::currentTime());
377         if (newTimestamp == statistics.mostRecentUserInteraction)
378             return;
379
380         statistics.hadUserInteraction = true;
381         statistics.mostRecentUserInteraction = newTimestamp;
382         }
383         
384         m_store->fireDataModificationHandler();
385     });
386 }
387
388 void ResourceLoadObserver::logUserInteraction(const URL& url)
389 {
390     if (url.isBlankURL() || url.isEmpty())
391         return;
392
393     auto primaryDomainString = primaryDomain(url);
394
395     ASSERT(m_queue);
396     m_queue->dispatch([this, primaryDomainString = primaryDomainString.isolatedCopy()] () {
397         {
398         auto locker = holdLock(m_store->statisticsLock());
399         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomainString);
400         statistics.hadUserInteraction = true;
401         statistics.mostRecentUserInteraction = WTF::currentTime();
402         }
403         
404         m_store->fireShouldPartitionCookiesHandler({ primaryDomainString }, { }, false);
405     });
406 }
407
408 void ResourceLoadObserver::clearUserInteraction(const URL& url)
409 {
410     if (url.isBlankURL() || url.isEmpty())
411         return;
412
413     auto locker = holdLock(m_store->statisticsLock());
414     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
415     
416     statistics.hadUserInteraction = false;
417     statistics.mostRecentUserInteraction = 0;
418 }
419
420 bool ResourceLoadObserver::hasHadUserInteraction(const URL& url)
421 {
422     if (url.isBlankURL() || url.isEmpty())
423         return false;
424
425     auto locker = holdLock(m_store->statisticsLock());
426     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
427     
428     return m_store->hasHadRecentUserInteraction(statistics);
429 }
430
431 void ResourceLoadObserver::setPrevalentResource(const URL& url)
432 {
433     if (url.isBlankURL() || url.isEmpty())
434         return;
435
436     auto locker = holdLock(m_store->statisticsLock());
437     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
438     
439     statistics.isPrevalentResource = true;
440 }
441
442 bool ResourceLoadObserver::isPrevalentResource(const URL& url)
443 {
444     if (url.isBlankURL() || url.isEmpty())
445         return false;
446
447     auto locker = holdLock(m_store->statisticsLock());
448     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
449     
450     return statistics.isPrevalentResource;
451 }
452     
453 void ResourceLoadObserver::clearPrevalentResource(const URL& url)
454 {
455     if (url.isBlankURL() || url.isEmpty())
456         return;
457
458     auto locker = holdLock(m_store->statisticsLock());
459     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
460     
461     statistics.isPrevalentResource = false;
462 }
463     
464 void ResourceLoadObserver::setGrandfathered(const URL& url, bool value)
465 {
466     if (url.isBlankURL() || url.isEmpty())
467         return;
468     
469     auto locker = holdLock(m_store->statisticsLock());
470     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
471     
472     statistics.grandfathered = value;
473 }
474     
475 bool ResourceLoadObserver::isGrandfathered(const URL& url)
476 {
477     if (url.isBlankURL() || url.isEmpty())
478         return false;
479     
480     auto locker = holdLock(m_store->statisticsLock());
481     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
482     
483     return statistics.grandfathered;
484 }
485
486 void ResourceLoadObserver::setSubframeUnderTopFrameOrigin(const URL& subframe, const URL& topFrame)
487 {
488     if (subframe.isBlankURL() || subframe.isEmpty() || topFrame.isBlankURL() || topFrame.isEmpty())
489         return;
490     
491     auto primaryTopFrameDomainString = primaryDomain(topFrame);
492     auto primarySubFrameDomainString = primaryDomain(subframe);
493     
494     ASSERT(m_queue);
495     m_queue->dispatch([this, primaryTopFrameDomainString = primaryTopFrameDomainString.isolatedCopy(), primarySubFrameDomainString = primarySubFrameDomainString.isolatedCopy()] () {
496         auto locker = holdLock(m_store->statisticsLock());
497         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primarySubFrameDomainString);
498         statistics.subframeUnderTopFrameOrigins.add(primaryTopFrameDomainString);
499     });
500 }
501
502 void ResourceLoadObserver::setSubresourceUnderTopFrameOrigin(const URL& subresource, const URL& topFrame)
503 {
504     if (subresource.isBlankURL() || subresource.isEmpty() || topFrame.isBlankURL() || topFrame.isEmpty())
505         return;
506     
507     auto primaryTopFrameDomainString = primaryDomain(topFrame);
508     auto primarySubresourceDomainString = primaryDomain(subresource);
509     
510     ASSERT(m_queue);
511     m_queue->dispatch([this, primaryTopFrameDomainString = primaryTopFrameDomainString.isolatedCopy(), primarySubresourceDomainString = primarySubresourceDomainString.isolatedCopy()] () {
512         auto locker = holdLock(m_store->statisticsLock());
513         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomainString);
514         statistics.subresourceUnderTopFrameOrigins.add(primaryTopFrameDomainString);
515     });
516 }
517
518 void ResourceLoadObserver::setSubresourceUniqueRedirectTo(const URL& subresource, const URL& hostNameRedirectedTo)
519 {
520     if (subresource.isBlankURL() || subresource.isEmpty() || hostNameRedirectedTo.isBlankURL() || hostNameRedirectedTo.isEmpty())
521         return;
522     
523     auto primarySubresourceDomainString = primaryDomain(subresource);
524     auto primaryRedirectDomainString = primaryDomain(hostNameRedirectedTo);
525     
526     ASSERT(m_queue);
527     m_queue->dispatch([this, primaryRedirectDomainString = primaryRedirectDomainString.isolatedCopy(), primarySubresourceDomainString = primarySubresourceDomainString.isolatedCopy()] () {
528         auto locker = holdLock(m_store->statisticsLock());
529         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomainString);
530         statistics.subresourceUniqueRedirectsTo.add(primaryRedirectDomainString);
531     });
532 }
533
534 void ResourceLoadObserver::setTimeToLiveUserInteraction(double seconds)
535 {
536     m_store->setTimeToLiveUserInteraction(seconds);
537 }
538
539 void ResourceLoadObserver::setTimeToLiveCookiePartitionFree(double seconds)
540 {
541     m_store->setTimeToLiveCookiePartitionFree(seconds);
542 }
543
544 void ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval(double seconds)
545 {
546     m_store->setMinimumTimeBetweeenDataRecordsRemoval(seconds);
547 }
548     
549 void ResourceLoadObserver::setReducedTimestampResolution(double seconds)
550 {
551     if (seconds > 0)
552         timestampResolution = seconds;
553 }
554
555 void ResourceLoadObserver::setGrandfatheringTime(double seconds)
556 {
557     m_store->setMinimumTimeBetweeenDataRecordsRemoval(seconds);
558 }
559     
560 void ResourceLoadObserver::fireDataModificationHandler()
561 {
562     // Helper function used by testing system. Should only be called from the main thread.
563     ASSERT(isMainThread());
564     m_queue->dispatch([this] () {
565         m_store->fireDataModificationHandler();
566     });
567 }
568
569 void ResourceLoadObserver::fireShouldPartitionCookiesHandler()
570 {
571     // Helper function used by testing system. Should only be called from the main thread.
572     ASSERT(isMainThread());
573     m_queue->dispatch([this] () {
574         m_store->fireShouldPartitionCookiesHandler();
575     });
576 }
577
578 void ResourceLoadObserver::fireShouldPartitionCookiesHandler(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, bool clearFirst)
579 {
580     // Helper function used by testing system. Should only be called from the main thread.
581     ASSERT(isMainThread());
582     m_queue->dispatch([this, domainsToRemove = CrossThreadCopier<Vector<String>>::copy(domainsToRemove), domainsToAdd = CrossThreadCopier<Vector<String>>::copy(domainsToAdd), clearFirst] () {
583         m_store->fireShouldPartitionCookiesHandler(domainsToRemove, domainsToAdd, clearFirst);
584     });
585 }
586
587 String ResourceLoadObserver::primaryDomain(const URL& url)
588 {
589     return primaryDomain(url.host());
590 }
591
592 String ResourceLoadObserver::primaryDomain(const String& host)
593 {
594     String primaryDomain;
595     if (host.isNull() || host.isEmpty())
596         primaryDomain = "nullOrigin";
597 #if ENABLE(PUBLIC_SUFFIX_LIST)
598     else {
599         primaryDomain = topPrivatelyControlledDomain(host);
600         // We will have an empty string here if there is no TLD.
601         // Use the host in such case.
602         if (primaryDomain.isEmpty())
603             primaryDomain = host;
604     }
605 #else
606     else
607         primaryDomain = host;
608 #endif
609
610     return primaryDomain;
611 }
612
613 String ResourceLoadObserver::statisticsForOrigin(const String& origin)
614 {
615     if (!m_store)
616         return emptyString();
617     
618     return m_store->statisticsForOrigin(origin);
619 }
620
621 }