Avoid iterator invalidation bug in WebCore::defaultPortForProtocol
[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     auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL);
151     
152     ASSERT(m_queue);
153     m_queue->dispatch([this, isMainFrame, isRedirect, sourcePrimaryDomain = sourcePrimaryDomain.isolatedCopy(), mainFramePrimaryDomain = mainFramePrimaryDomain.isolatedCopy(), targetURL = CrossThreadCopier<URL>::copy(targetURL), mainFrameOrigin = mainFrameOrigin->isolatedCopy(), targetPrimaryDomain = targetPrimaryDomain.isolatedCopy()] () {
154         
155         auto targetOrigin = SecurityOrigin::create(targetURL);
156         bool shouldFireDataModificationHandler = false;
157         
158         {
159         auto locker = holdLock(m_store->statisticsLock());
160         // We must make a copy here, because later calls to 'ensureResourceStatisticsForPrimaryDomain' will invalidate the returned reference:
161         auto targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
162
163         // Always fire if we have previously removed data records for this domain
164         shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0;
165
166         if (isMainFrame)
167             targetStatistics.topFrameHasBeenNavigatedToBefore = true;
168         else {
169             targetStatistics.subframeHasBeenLoadedBefore = true;
170
171             auto subframeUnderTopFrameOriginsResult = targetStatistics.subframeUnderTopFrameOrigins.add(mainFramePrimaryDomain);
172             if (subframeUnderTopFrameOriginsResult.isNewEntry)
173                 shouldFireDataModificationHandler = true;
174         }
175         
176         if (isRedirect) {
177             auto& redirectingOriginResourceStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
178             
179             if (m_store->isPrevalentResource(targetPrimaryDomain))
180                 redirectingOriginResourceStatistics.redirectedToOtherPrevalentResourceOrigins.add(targetPrimaryDomain);
181             
182             if (isMainFrame) {
183                 ++targetStatistics.topFrameHasBeenRedirectedTo;
184                 ++redirectingOriginResourceStatistics.topFrameHasBeenRedirectedFrom;
185             } else {
186                 ++targetStatistics.subframeHasBeenRedirectedTo;
187                 ++redirectingOriginResourceStatistics.subframeHasBeenRedirectedFrom;
188                 redirectingOriginResourceStatistics.subframeUniqueRedirectsTo.add(targetPrimaryDomain);
189                 
190                 ++targetStatistics.subframeSubResourceCount;
191             }
192         } else {
193             if (sourcePrimaryDomain.isNull() || sourcePrimaryDomain.isEmpty() || sourcePrimaryDomain == "nullOrigin") {
194                 if (isMainFrame)
195                     ++targetStatistics.topFrameInitialLoadCount;
196                 else
197                     ++targetStatistics.subframeSubResourceCount;
198             } else {
199                 auto& sourceOriginResourceStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
200
201                 if (isMainFrame) {
202                     ++sourceOriginResourceStatistics.topFrameHasBeenNavigatedFrom;
203                     ++targetStatistics.topFrameHasBeenNavigatedTo;
204                 } else {
205                     ++sourceOriginResourceStatistics.subframeHasBeenNavigatedFrom;
206                     ++targetStatistics.subframeHasBeenNavigatedTo;
207                 }
208             }
209         }
210             
211         m_store->setResourceStatisticsForPrimaryDomain(targetPrimaryDomain, WTFMove(targetStatistics));
212         } // Release lock
213         
214         if (shouldFireDataModificationHandler)
215             m_store->fireDataModificationHandler();
216     });
217 }
218     
219 void ResourceLoadObserver::logSubresourceLoading(const Frame* frame, const ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
220 {
221     ASSERT(frame->page());
222
223     if (!shouldLog(frame->page()))
224         return;
225
226     bool isRedirect = is3xxRedirect(redirectResponse);
227     const URL& sourceURL = redirectResponse.url();
228     const URL& targetURL = newRequest.url();
229     const URL& mainFrameURL = frame ? frame->mainFrame().document()->url() : URL();
230     
231     auto targetHost = targetURL.host();
232     auto mainFrameHost = mainFrameURL.host();
233
234     if (targetHost.isEmpty()
235         || mainFrameHost.isEmpty()
236         || targetHost == mainFrameHost
237         || (isRedirect && targetHost == sourceURL.host()))
238         return;
239
240     auto targetPrimaryDomain = primaryDomain(targetURL);
241     auto mainFramePrimaryDomain = primaryDomain(mainFrameURL);
242     auto sourcePrimaryDomain = primaryDomain(sourceURL);
243     
244     if (targetPrimaryDomain == mainFramePrimaryDomain || (isRedirect && targetPrimaryDomain == sourcePrimaryDomain))
245         return;
246     
247     auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL);
248     
249     ASSERT(m_queue);
250     m_queue->dispatch([this, isRedirect, sourcePrimaryDomain = sourcePrimaryDomain.isolatedCopy(), mainFramePrimaryDomain = mainFramePrimaryDomain.isolatedCopy(), targetPrimaryDomain = targetPrimaryDomain.isolatedCopy(), mainFrameOrigin = mainFrameOrigin->isolatedCopy()] () {
251         
252         bool shouldFireDataModificationHandler = false;
253         
254         {
255         auto locker = holdLock(m_store->statisticsLock());
256         auto& targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
257
258         // Always fire if we have previously removed data records for this domain
259         shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0;
260
261         auto subresourceUnderTopFrameOriginsResult = targetStatistics.subresourceUnderTopFrameOrigins.add(mainFramePrimaryDomain);
262         if (subresourceUnderTopFrameOriginsResult.isNewEntry)
263             shouldFireDataModificationHandler = true;
264
265         if (isRedirect) {
266             auto& redirectingOriginStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
267             
268             // We just inserted to the store, so we need to reget 'targetStatistics'
269             auto& updatedTargetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
270
271             if (m_store->isPrevalentResource(targetPrimaryDomain))
272                 redirectingOriginStatistics.redirectedToOtherPrevalentResourceOrigins.add(targetPrimaryDomain);
273             
274             ++redirectingOriginStatistics.subresourceHasBeenRedirectedFrom;
275             ++updatedTargetStatistics.subresourceHasBeenRedirectedTo;
276
277             auto subresourceUniqueRedirectsToResult = redirectingOriginStatistics.subresourceUniqueRedirectsTo.add(targetPrimaryDomain);
278             if (subresourceUniqueRedirectsToResult.isNewEntry)
279                 shouldFireDataModificationHandler = true;
280
281             ++updatedTargetStatistics.subresourceHasBeenSubresourceCount;
282
283             auto totalVisited = std::max(m_originsVisitedMap.size(), 1U);
284             
285             updatedTargetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(updatedTargetStatistics.subresourceHasBeenSubresourceCount) / totalVisited;
286         } else {
287             ++targetStatistics.subresourceHasBeenSubresourceCount;
288
289             auto totalVisited = std::max(m_originsVisitedMap.size(), 1U);
290             
291             targetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(targetStatistics.subresourceHasBeenSubresourceCount) / totalVisited;
292         }
293         } // Release lock
294         
295         if (shouldFireDataModificationHandler)
296             m_store->fireDataModificationHandler();
297     });
298 }
299
300 void ResourceLoadObserver::logWebSocketLoading(const Frame* frame, const URL& targetURL)
301 {
302     // FIXME: Web sockets can run in detached frames. Decide how to count such connections.
303     // See LayoutTests/http/tests/websocket/construct-in-detached-frame.html
304     if (!frame)
305         return;
306
307     if (!shouldLog(frame->page()))
308         return;
309
310     const URL& mainFrameURL = frame->mainFrame().document()->url();
311
312     auto targetHost = targetURL.host();
313     auto mainFrameHost = mainFrameURL.host();
314     
315     if (targetHost.isEmpty()
316         || mainFrameHost.isEmpty()
317         || targetHost == mainFrameHost)
318         return;
319     
320     auto targetPrimaryDomain = primaryDomain(targetURL);
321     auto mainFramePrimaryDomain = primaryDomain(mainFrameURL);
322     
323     if (targetPrimaryDomain == mainFramePrimaryDomain)
324         return;
325
326     auto mainFrameOrigin = SecurityOrigin::create(mainFrameURL);
327     
328     ASSERT(m_queue);
329     m_queue->dispatch([this, targetPrimaryDomain = targetPrimaryDomain.isolatedCopy(), mainFramePrimaryDomain = mainFramePrimaryDomain.isolatedCopy(), mainFrameOrigin = mainFrameOrigin->isolatedCopy()] () {
330         
331         bool shouldFireDataModificationHandler = false;
332         
333         {
334         auto locker = holdLock(m_store->statisticsLock());
335         auto& targetStatistics = m_store->ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
336
337         // Always fire if we have previously removed data records for this domain
338         shouldFireDataModificationHandler = targetStatistics.dataRecordsRemoved > 0;
339         
340         auto subresourceUnderTopFrameOriginsResult = targetStatistics.subresourceUnderTopFrameOrigins.add(mainFramePrimaryDomain);
341         if (subresourceUnderTopFrameOriginsResult.isNewEntry)
342             shouldFireDataModificationHandler = true;
343
344         ++targetStatistics.subresourceHasBeenSubresourceCount;
345         
346         auto totalVisited = std::max(m_originsVisitedMap.size(), 1U);
347         
348         targetStatistics.subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited = static_cast<double>(targetStatistics.subresourceHasBeenSubresourceCount) / totalVisited;
349         } // Release lock
350         
351         if (shouldFireDataModificationHandler)
352             m_store->fireDataModificationHandler();
353     });
354 }
355
356 static double reduceTimeResolution(double seconds)
357 {
358     return std::floor(seconds / timestampResolution) * timestampResolution;
359 }
360
361 void ResourceLoadObserver::logUserInteractionWithReducedTimeResolution(const Document& document)
362 {
363     ASSERT(document.page());
364
365     if (!shouldLog(document.page()))
366         return;
367
368     auto& url = document.url();
369     if (url.isBlankURL() || url.isEmpty())
370         return;
371
372     auto primaryDomainString = primaryDomain(url);
373     
374     ASSERT(m_queue);
375     m_queue->dispatch([this, primaryDomainString = primaryDomainString.isolatedCopy()] () {
376         {
377         auto locker = holdLock(m_store->statisticsLock());
378         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomainString);
379         double newTimestamp = reduceTimeResolution(WTF::currentTime());
380         if (newTimestamp == statistics.mostRecentUserInteraction)
381             return;
382
383         statistics.hadUserInteraction = true;
384         statistics.mostRecentUserInteraction = newTimestamp;
385         }
386         
387         m_store->fireDataModificationHandler();
388     });
389 }
390
391 void ResourceLoadObserver::logUserInteraction(const URL& url)
392 {
393     if (url.isBlankURL() || url.isEmpty())
394         return;
395
396     auto primaryDomainString = primaryDomain(url);
397
398     ASSERT(m_queue);
399     m_queue->dispatch([this, primaryDomainString = primaryDomainString.isolatedCopy()] () {
400         {
401         auto locker = holdLock(m_store->statisticsLock());
402         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomainString);
403         statistics.hadUserInteraction = true;
404         statistics.mostRecentUserInteraction = WTF::currentTime();
405         }
406         
407         m_store->fireShouldPartitionCookiesHandler({ primaryDomainString }, { }, false);
408     });
409 }
410
411 void ResourceLoadObserver::clearUserInteraction(const URL& url)
412 {
413     if (url.isBlankURL() || url.isEmpty())
414         return;
415
416     auto locker = holdLock(m_store->statisticsLock());
417     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
418     
419     statistics.hadUserInteraction = false;
420     statistics.mostRecentUserInteraction = 0;
421 }
422
423 bool ResourceLoadObserver::hasHadUserInteraction(const URL& url)
424 {
425     if (url.isBlankURL() || url.isEmpty())
426         return false;
427
428     auto locker = holdLock(m_store->statisticsLock());
429     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
430     
431     return m_store->hasHadRecentUserInteraction(statistics);
432 }
433
434 void ResourceLoadObserver::setPrevalentResource(const URL& url)
435 {
436     if (url.isBlankURL() || url.isEmpty())
437         return;
438
439     auto locker = holdLock(m_store->statisticsLock());
440     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
441     
442     statistics.isPrevalentResource = true;
443 }
444
445 bool ResourceLoadObserver::isPrevalentResource(const URL& url)
446 {
447     if (url.isBlankURL() || url.isEmpty())
448         return false;
449
450     auto locker = holdLock(m_store->statisticsLock());
451     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
452     
453     return statistics.isPrevalentResource;
454 }
455     
456 void ResourceLoadObserver::clearPrevalentResource(const URL& url)
457 {
458     if (url.isBlankURL() || url.isEmpty())
459         return;
460
461     auto locker = holdLock(m_store->statisticsLock());
462     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
463     
464     statistics.isPrevalentResource = false;
465 }
466     
467 void ResourceLoadObserver::setGrandfathered(const URL& url, bool value)
468 {
469     if (url.isBlankURL() || url.isEmpty())
470         return;
471     
472     auto locker = holdLock(m_store->statisticsLock());
473     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
474     
475     statistics.grandfathered = value;
476 }
477     
478 bool ResourceLoadObserver::isGrandfathered(const URL& url)
479 {
480     if (url.isBlankURL() || url.isEmpty())
481         return false;
482     
483     auto locker = holdLock(m_store->statisticsLock());
484     auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
485     
486     return statistics.grandfathered;
487 }
488
489 void ResourceLoadObserver::setSubframeUnderTopFrameOrigin(const URL& subframe, const URL& topFrame)
490 {
491     if (subframe.isBlankURL() || subframe.isEmpty() || topFrame.isBlankURL() || topFrame.isEmpty())
492         return;
493     
494     auto primaryTopFrameDomainString = primaryDomain(topFrame);
495     auto primarySubFrameDomainString = primaryDomain(subframe);
496     
497     ASSERT(m_queue);
498     m_queue->dispatch([this, primaryTopFrameDomainString = primaryTopFrameDomainString.isolatedCopy(), primarySubFrameDomainString = primarySubFrameDomainString.isolatedCopy()] () {
499         auto locker = holdLock(m_store->statisticsLock());
500         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primarySubFrameDomainString);
501         statistics.subframeUnderTopFrameOrigins.add(primaryTopFrameDomainString);
502     });
503 }
504
505 void ResourceLoadObserver::setSubresourceUnderTopFrameOrigin(const URL& subresource, const URL& topFrame)
506 {
507     if (subresource.isBlankURL() || subresource.isEmpty() || topFrame.isBlankURL() || topFrame.isEmpty())
508         return;
509     
510     auto primaryTopFrameDomainString = primaryDomain(topFrame);
511     auto primarySubresourceDomainString = primaryDomain(subresource);
512     
513     ASSERT(m_queue);
514     m_queue->dispatch([this, primaryTopFrameDomainString = primaryTopFrameDomainString.isolatedCopy(), primarySubresourceDomainString = primarySubresourceDomainString.isolatedCopy()] () {
515         auto locker = holdLock(m_store->statisticsLock());
516         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomainString);
517         statistics.subresourceUnderTopFrameOrigins.add(primaryTopFrameDomainString);
518     });
519 }
520
521 void ResourceLoadObserver::setSubresourceUniqueRedirectTo(const URL& subresource, const URL& hostNameRedirectedTo)
522 {
523     if (subresource.isBlankURL() || subresource.isEmpty() || hostNameRedirectedTo.isBlankURL() || hostNameRedirectedTo.isEmpty())
524         return;
525     
526     auto primarySubresourceDomainString = primaryDomain(subresource);
527     auto primaryRedirectDomainString = primaryDomain(hostNameRedirectedTo);
528     
529     ASSERT(m_queue);
530     m_queue->dispatch([this, primaryRedirectDomainString = primaryRedirectDomainString.isolatedCopy(), primarySubresourceDomainString = primarySubresourceDomainString.isolatedCopy()] () {
531         auto locker = holdLock(m_store->statisticsLock());
532         auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomainString);
533         statistics.subresourceUniqueRedirectsTo.add(primaryRedirectDomainString);
534     });
535 }
536
537 void ResourceLoadObserver::setTimeToLiveUserInteraction(double seconds)
538 {
539     m_store->setTimeToLiveUserInteraction(seconds);
540 }
541
542 void ResourceLoadObserver::setTimeToLiveCookiePartitionFree(double seconds)
543 {
544     m_store->setTimeToLiveCookiePartitionFree(seconds);
545 }
546
547 void ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval(double seconds)
548 {
549     m_store->setMinimumTimeBetweeenDataRecordsRemoval(seconds);
550 }
551     
552 void ResourceLoadObserver::setReducedTimestampResolution(double seconds)
553 {
554     if (seconds > 0)
555         timestampResolution = seconds;
556 }
557
558 void ResourceLoadObserver::setGrandfatheringTime(double seconds)
559 {
560     m_store->setMinimumTimeBetweeenDataRecordsRemoval(seconds);
561 }
562     
563 void ResourceLoadObserver::fireDataModificationHandler()
564 {
565     // Helper function used by testing system. Should only be called from the main thread.
566     ASSERT(isMainThread());
567     m_queue->dispatch([this] () {
568         m_store->fireDataModificationHandler();
569     });
570 }
571
572 void ResourceLoadObserver::fireShouldPartitionCookiesHandler()
573 {
574     // Helper function used by testing system. Should only be called from the main thread.
575     ASSERT(isMainThread());
576     m_queue->dispatch([this] () {
577         m_store->fireShouldPartitionCookiesHandler();
578     });
579 }
580
581 void ResourceLoadObserver::fireShouldPartitionCookiesHandler(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, bool clearFirst)
582 {
583     // Helper function used by testing system. Should only be called from the main thread.
584     ASSERT(isMainThread());
585     m_queue->dispatch([this, domainsToRemove = CrossThreadCopier<Vector<String>>::copy(domainsToRemove), domainsToAdd = CrossThreadCopier<Vector<String>>::copy(domainsToAdd), clearFirst] () {
586         m_store->fireShouldPartitionCookiesHandler(domainsToRemove, domainsToAdd, clearFirst);
587     });
588 }
589
590 String ResourceLoadObserver::primaryDomain(const URL& url)
591 {
592     return primaryDomain(url.host());
593 }
594
595 String ResourceLoadObserver::primaryDomain(const String& host)
596 {
597     String primaryDomain;
598     if (host.isNull() || host.isEmpty())
599         primaryDomain = "nullOrigin";
600 #if ENABLE(PUBLIC_SUFFIX_LIST)
601     else {
602         primaryDomain = topPrivatelyControlledDomain(host);
603         // We will have an empty string here if there is no TLD.
604         // Use the host in such case.
605         if (primaryDomain.isEmpty())
606             primaryDomain = host;
607     }
608 #else
609     else
610         primaryDomain = host;
611 #endif
612
613     return primaryDomain;
614 }
615
616 String ResourceLoadObserver::statisticsForOrigin(const String& origin)
617 {
618     if (!m_store)
619         return emptyString();
620     
621     return m_store->statisticsForOrigin(origin);
622 }
623
624 }