WebResourceLoadStatisticsStore.cpp [plain text]
#include "config.h"
#include "WebResourceLoadStatisticsStore.h"
#include "WebProcessMessages.h"
#include "WebProcessPool.h"
#include "WebResourceLoadStatisticsStoreMessages.h"
#include <WebCore/KeyedCoding.h>
#include <WebCore/ResourceLoadStatistics.h>
#include <wtf/threads/BinarySemaphore.h>
using namespace WebCore;
namespace WebKit {
static const unsigned subframeUnderTopFrameOriginsThreshold = 3;
static const unsigned subresourceUnderTopFrameOriginsThreshold = 5;
static const unsigned subresourceHasBeenRedirectedFromToUniqueDomainsThreshold = 3;
static const unsigned redirectedToOtherPrevalentResourceOriginsThreshold = 2;
Ref<WebResourceLoadStatisticsStore> WebResourceLoadStatisticsStore::create(const String& resourceLoadStatisticsDirectory)
{
return adoptRef(*new WebResourceLoadStatisticsStore(resourceLoadStatisticsDirectory));
}
WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore(const String& resourceLoadStatisticsDirectory)
: m_resourceStatisticsStore(WebCore::ResourceLoadStatisticsStore::create())
, m_statisticsQueue(WorkQueue::create("WebResourceLoadStatisticsStore Process Data Queue"))
, m_storagePath(resourceLoadStatisticsDirectory)
{
}
WebResourceLoadStatisticsStore::~WebResourceLoadStatisticsStore()
{
}
static inline bool hasPrevalentResourceCharacteristics(const ResourceLoadStatistics& resourceStatistic)
{
return resourceStatistic.subframeUnderTopFrameOrigins.size() > subframeUnderTopFrameOriginsThreshold
|| resourceStatistic.subresourceUnderTopFrameOrigins.size() > subresourceUnderTopFrameOriginsThreshold
|| resourceStatistic.subresourceUniqueRedirectsTo.size() > subresourceHasBeenRedirectedFromToUniqueDomainsThreshold
|| resourceStatistic.redirectedToOtherPrevalentResourceOrigins.size() > redirectedToOtherPrevalentResourceOriginsThreshold;
}
static inline void classifyPrevalentResources(ResourceLoadStatistics& resourceStatistic, Vector<String>& prevalentResources, Vector<String>& prevalentResourcesWithUserInteraction)
{
if (resourceStatistic.isPrevalentResource || hasPrevalentResourceCharacteristics(resourceStatistic)) {
resourceStatistic.isPrevalentResource = true;
if (resourceStatistic.hadUserInteraction)
prevalentResourcesWithUserInteraction.append(resourceStatistic.highLevelDomain);
else
prevalentResources.append(resourceStatistic.highLevelDomain);
}
}
void WebResourceLoadStatisticsStore::resourceLoadStatisticsUpdated(const Vector<WebCore::ResourceLoadStatistics>& origins)
{
coreStore().mergeStatistics(origins);
Vector<String> prevalentResources, prevalentResourcesWithUserInteraction;
if (coreStore().hasEnoughDataForStatisticsProcessing()) {
coreStore().processStatistics([this, &prevalentResources, &prevalentResourcesWithUserInteraction] (ResourceLoadStatistics& resourceStatistic) {
classifyPrevalentResources(resourceStatistic, prevalentResources, prevalentResourcesWithUserInteraction);
});
}
auto encoder = coreStore().createEncoderFromData();
writeEncoderToDisk(*encoder.get(), "full_browsing_session");
}
void WebResourceLoadStatisticsStore::setResourceLoadStatisticsEnabled(bool enabled)
{
if (enabled == m_resourceLoadStatisticsEnabled)
return;
m_resourceLoadStatisticsEnabled = enabled;
readDataFromDiskIfNeeded();
}
bool WebResourceLoadStatisticsStore::resourceLoadStatisticsEnabled() const
{
return m_resourceLoadStatisticsEnabled;
}
void WebResourceLoadStatisticsStore::readDataFromDiskIfNeeded()
{
if (!m_resourceLoadStatisticsEnabled)
return;
m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
coreStore().clear();
auto decoder = createDecoderFromDisk("full_browsing_session");
if (!decoder)
return;
coreStore().readDataFromDecoder(*decoder);
});
}
void WebResourceLoadStatisticsStore::processWillOpenConnection(WebProcessProxy&, IPC::Connection& connection)
{
connection.addWorkQueueMessageReceiver(Messages::WebResourceLoadStatisticsStore::messageReceiverName(), &m_statisticsQueue.get(), this);
}
void WebResourceLoadStatisticsStore::processDidCloseConnection(WebProcessProxy&, IPC::Connection& connection)
{
connection.removeWorkQueueMessageReceiver(Messages::WebResourceLoadStatisticsStore::messageReceiverName());
}
void WebResourceLoadStatisticsStore::applicationWillTerminate()
{
BinarySemaphore semaphore;
m_statisticsQueue->dispatch([this, &semaphore] {
semaphore.signal();
});
semaphore.wait(std::numeric_limits<double>::max());
}
String WebResourceLoadStatisticsStore::persistentStoragePath(const String& label) const
{
if (m_storagePath.isEmpty())
return emptyString();
return pathByAppendingComponent(m_storagePath, label + "_resourceLog.plist");
}
void WebResourceLoadStatisticsStore::writeEncoderToDisk(KeyedEncoder& encoder, const String& label) const
{
RefPtr<SharedBuffer> rawData = encoder.finishEncoding();
if (!rawData)
return;
String resourceLog = persistentStoragePath(label);
if (resourceLog.isEmpty())
return;
if (!m_storagePath.isEmpty())
makeAllDirectories(m_storagePath);
auto handle = openFile(resourceLog, OpenForWrite);
if (!handle)
return;
int64_t writtenBytes = writeToFile(handle, rawData->data(), rawData->size());
closeFile(handle);
if (writtenBytes != static_cast<int64_t>(rawData->size()))
WTFLogAlways("WebResourceLoadStatisticsStore: We only wrote %d out of %d bytes to disk", static_cast<unsigned>(writtenBytes), rawData->size());
}
std::unique_ptr<KeyedDecoder> WebResourceLoadStatisticsStore::createDecoderFromDisk(const String& label) const
{
String resourceLog = persistentStoragePath(label);
if (resourceLog.isEmpty())
return nullptr;
RefPtr<SharedBuffer> rawData = SharedBuffer::createWithContentsOfFile(resourceLog);
if (!rawData)
return nullptr;
return KeyedDecoder::decoder(reinterpret_cast<const uint8_t*>(rawData->data()), rawData->size());
}
}