5846879ac894e59e47ed053add7f46310f2000ee
[WebKit.git] / Source / WebKit2 / UIProcess / WebResourceLoadStatisticsStore.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 "WebResourceLoadStatisticsStore.h"
28
29 #include "WebProcessMessages.h"
30 #include "WebProcessPool.h"
31 #include "WebProcessProxy.h"
32 #include "WebResourceLoadStatisticsStoreMessages.h"
33 #include "WebsiteDataFetchOption.h"
34 #include "WebsiteDataType.h"
35 #include <WebCore/KeyedCoding.h>
36 #include <WebCore/ResourceLoadObserver.h>
37 #include <WebCore/ResourceLoadStatistics.h>
38 #include <wtf/CurrentTime.h>
39 #include <wtf/MainThread.h>
40 #include <wtf/MathExtras.h>
41 #include <wtf/RunLoop.h>
42 #include <wtf/threads/BinarySemaphore.h>
43
44 using namespace WebCore;
45
46 namespace WebKit {
47
48 static auto minimumTimeBetweeenDataRecordsRemoval = 60;
49 static OptionSet<WebKit::WebsiteDataType> dataTypesToRemove;
50 static auto notifyPages = false;
51 static auto shouldClassifyResourcesBeforeDataRecordsRemoval = true;
52
53 Ref<WebResourceLoadStatisticsStore> WebResourceLoadStatisticsStore::create(const String& resourceLoadStatisticsDirectory)
54 {
55     return adoptRef(*new WebResourceLoadStatisticsStore(resourceLoadStatisticsDirectory));
56 }
57
58 WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore(const String& resourceLoadStatisticsDirectory)
59     : m_resourceLoadStatisticsStore(ResourceLoadStatisticsStore::create())
60     , m_statisticsQueue(WorkQueue::create("WebResourceLoadStatisticsStore Process Data Queue"))
61     , m_statisticsStoragePath(resourceLoadStatisticsDirectory)
62 {
63 }
64
65 WebResourceLoadStatisticsStore::~WebResourceLoadStatisticsStore()
66 {
67 }
68
69 void WebResourceLoadStatisticsStore::setNotifyPagesWhenDataRecordsWereScanned(bool always)
70 {
71     notifyPages = always;
72 }
73
74 void WebResourceLoadStatisticsStore::setShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
75 {
76     shouldClassifyResourcesBeforeDataRecordsRemoval = value;
77 }
78
79 void WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval(double seconds)
80 {
81     if (seconds >= 0)
82         minimumTimeBetweeenDataRecordsRemoval = seconds;
83 }
84
85 void WebResourceLoadStatisticsStore::classifyResource(ResourceLoadStatistics& resourceStatistic)
86 {
87     if (!resourceStatistic.isPrevalentResource
88         && m_resourceLoadStatisticsClassifier.hasPrevalentResourceCharacteristics(resourceStatistic))
89         resourceStatistic.isPrevalentResource = true;
90 }
91
92 void WebResourceLoadStatisticsStore::removeDataRecords()
93 {
94     if (m_dataRecordsRemovalPending)
95         return;
96
97     Vector<String> prevalentResourceDomains = coreStore().prevalentResourceDomainsWithoutUserInteraction();
98     if (!prevalentResourceDomains.size())
99         return;
100
101     double now = currentTime();
102     if (m_lastTimeDataRecordsWereRemoved
103         && now < m_lastTimeDataRecordsWereRemoved + minimumTimeBetweeenDataRecordsRemoval)
104         return;
105
106     m_dataRecordsRemovalPending = true;
107     m_lastTimeDataRecordsWereRemoved = now;
108
109     if (dataTypesToRemove.isEmpty()) {
110         dataTypesToRemove |= WebsiteDataType::Cookies;
111         dataTypesToRemove |= WebsiteDataType::DiskCache;
112         dataTypesToRemove |= WebsiteDataType::MemoryCache;
113         dataTypesToRemove |= WebsiteDataType::OfflineWebApplicationCache;
114         dataTypesToRemove |= WebsiteDataType::SessionStorage;
115         dataTypesToRemove |= WebsiteDataType::LocalStorage;
116         dataTypesToRemove |= WebsiteDataType::WebSQLDatabases;
117         dataTypesToRemove |= WebsiteDataType::IndexedDBDatabases;
118         dataTypesToRemove |= WebsiteDataType::MediaKeys;
119         dataTypesToRemove |= WebsiteDataType::HSTSCache;
120         dataTypesToRemove |= WebsiteDataType::SearchFieldRecentSearches;
121 #if ENABLE(NETSCAPE_PLUGIN_API)
122         dataTypesToRemove |= WebsiteDataType::PlugInData;
123 #endif
124 #if ENABLE(MEDIA_STREAM)
125         dataTypesToRemove |= WebsiteDataType::MediaDeviceIdentifier;
126 #endif
127     }
128
129     // Switch to the main thread to get the default website data store
130     RunLoop::main().dispatch([prevalentResourceDomains = WTFMove(prevalentResourceDomains), this] () mutable {
131         WebProcessProxy::deleteWebsiteDataForTopPrivatelyOwnedDomainsInAllPersistentDataStores(dataTypesToRemove, prevalentResourceDomains, notifyPages, [this](Vector<String> domainsWithDeletedWebsiteData) mutable {
132             this->coreStore().updateStatisticsForRemovedDataRecords(domainsWithDeletedWebsiteData);
133             m_dataRecordsRemovalPending = false;
134         });
135     });
136 }
137
138 void WebResourceLoadStatisticsStore::processStatisticsAndDataRecords()
139 {
140     if (shouldClassifyResourcesBeforeDataRecordsRemoval) {
141         coreStore().processStatistics([this] (ResourceLoadStatistics& resourceStatistic) {
142             classifyResource(resourceStatistic);
143         });
144     }
145     removeDataRecords();
146     
147     auto encoder = coreStore().createEncoderFromData();
148     
149     writeEncoderToDisk(*encoder.get(), "full_browsing_session");
150 }
151
152 void WebResourceLoadStatisticsStore::resourceLoadStatisticsUpdated(const Vector<WebCore::ResourceLoadStatistics>& origins)
153 {
154     coreStore().mergeStatistics(origins);
155     processStatisticsAndDataRecords();
156 }
157
158 void WebResourceLoadStatisticsStore::setResourceLoadStatisticsEnabled(bool enabled)
159 {
160     if (enabled == m_resourceLoadStatisticsEnabled)
161         return;
162
163     m_resourceLoadStatisticsEnabled = enabled;
164
165     readDataFromDiskIfNeeded();
166 }
167
168 bool WebResourceLoadStatisticsStore::resourceLoadStatisticsEnabled() const
169 {
170     return m_resourceLoadStatisticsEnabled;
171 }
172
173
174 void WebResourceLoadStatisticsStore::registerSharedResourceLoadObserver()
175 {
176     ResourceLoadObserver::sharedObserver().setStatisticsStore(m_resourceLoadStatisticsStore.copyRef());
177     m_resourceLoadStatisticsStore->setNotificationCallback([this] {
178         if (m_resourceLoadStatisticsStore->isEmpty())
179             return;
180         processStatisticsAndDataRecords();
181     });
182 }
183
184 void WebResourceLoadStatisticsStore::readDataFromDiskIfNeeded()
185 {
186     if (!m_resourceLoadStatisticsEnabled)
187         return;
188
189     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
190         coreStore().clear();
191
192         auto decoder = createDecoderFromDisk("full_browsing_session");
193         if (!decoder)
194             return;
195
196         coreStore().readDataFromDecoder(*decoder);
197     });
198 }
199
200 void WebResourceLoadStatisticsStore::processWillOpenConnection(WebProcessProxy&, IPC::Connection& connection)
201 {
202     connection.addWorkQueueMessageReceiver(Messages::WebResourceLoadStatisticsStore::messageReceiverName(), m_statisticsQueue.get(), this);
203 }
204
205 void WebResourceLoadStatisticsStore::processDidCloseConnection(WebProcessProxy&, IPC::Connection& connection)
206 {
207     connection.removeWorkQueueMessageReceiver(Messages::WebResourceLoadStatisticsStore::messageReceiverName());
208 }
209
210 void WebResourceLoadStatisticsStore::applicationWillTerminate()
211 {
212     BinarySemaphore semaphore;
213     m_statisticsQueue->dispatch([this, &semaphore] {
214         // Make sure any ongoing work in our queue is finished before we terminate.
215         semaphore.signal();
216     });
217     semaphore.wait(WallTime::infinity());
218 }
219
220 String WebResourceLoadStatisticsStore::persistentStoragePath(const String& label) const
221 {
222     if (m_statisticsStoragePath.isEmpty())
223         return emptyString();
224
225     // TODO Decide what to call this file
226     return pathByAppendingComponent(m_statisticsStoragePath, label + "_resourceLog.plist");
227 }
228
229 void WebResourceLoadStatisticsStore::writeEncoderToDisk(KeyedEncoder& encoder, const String& label) const
230 {
231     RefPtr<SharedBuffer> rawData = encoder.finishEncoding();
232     if (!rawData)
233         return;
234
235     String resourceLog = persistentStoragePath(label);
236     if (resourceLog.isEmpty())
237         return;
238
239     if (!m_statisticsStoragePath.isEmpty())
240         makeAllDirectories(m_statisticsStoragePath);
241
242     auto handle = openFile(resourceLog, OpenForWrite);
243     if (!handle)
244         return;
245     
246     int64_t writtenBytes = writeToFile(handle, rawData->data(), rawData->size());
247     closeFile(handle);
248
249     if (writtenBytes != static_cast<int64_t>(rawData->size()))
250         WTFLogAlways("WebResourceLoadStatisticsStore: We only wrote %d out of %d bytes to disk", static_cast<unsigned>(writtenBytes), rawData->size());
251 }
252
253 std::unique_ptr<KeyedDecoder> WebResourceLoadStatisticsStore::createDecoderFromDisk(const String& label) const
254 {
255     String resourceLog = persistentStoragePath(label);
256     if (resourceLog.isEmpty())
257         return nullptr;
258
259     RefPtr<SharedBuffer> rawData = SharedBuffer::createWithContentsOfFile(resourceLog);
260     if (!rawData)
261         return nullptr;
262
263     return KeyedDecoder::decoder(reinterpret_cast<const uint8_t*>(rawData->data()), rawData->size());
264 }
265
266 } // namespace WebKit