[iOS] Replace "node assistance" terminology in WebKit with "focused element"
[WebKit-https.git] / Source / WebKit / UIProcess / ResourceLoadStatisticsPersistentStorage.cpp
1 /*
2  * Copyright (C) 2017-2018 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 "ResourceLoadStatisticsPersistentStorage.h"
28
29 #include "Logging.h"
30 #include "ResourceLoadStatisticsMemoryStore.h"
31 #include "WebResourceLoadStatisticsStore.h"
32 #include <WebCore/FileMonitor.h>
33 #include <WebCore/FileSystem.h>
34 #include <WebCore/KeyedCoding.h>
35 #include <WebCore/SharedBuffer.h>
36 #include <wtf/RunLoop.h>
37 #include <wtf/WorkQueue.h>
38
39 namespace WebKit {
40
41 constexpr Seconds minimumWriteInterval { 5_min };
42
43 using namespace WebCore;
44
45 static bool hasFileChangedSince(const String& path, WallTime since)
46 {
47     ASSERT(!RunLoop::isMain());
48     auto modificationTime = FileSystem::getFileModificationTime(path);
49     if (!modificationTime)
50         return true;
51
52     return modificationTime.value() > since;
53 }
54
55 static std::unique_ptr<KeyedDecoder> createDecoderForFile(const String& path)
56 {
57     ASSERT(!RunLoop::isMain());
58     auto handle = FileSystem::openAndLockFile(path, FileSystem::FileOpenMode::Read);
59     if (handle == FileSystem::invalidPlatformFileHandle)
60         return nullptr;
61
62     long long fileSize = 0;
63     if (!FileSystem::getFileSize(handle, fileSize)) {
64         FileSystem::unlockAndCloseFile(handle);
65         return nullptr;
66     }
67
68     size_t bytesToRead;
69     if (!WTF::convertSafely(fileSize, bytesToRead)) {
70         FileSystem::unlockAndCloseFile(handle);
71         return nullptr;
72     }
73
74     Vector<char> buffer(bytesToRead);
75     size_t totalBytesRead = FileSystem::readFromFile(handle, buffer.data(), buffer.size());
76
77     FileSystem::unlockAndCloseFile(handle);
78
79     if (totalBytesRead != bytesToRead)
80         return nullptr;
81
82     return KeyedDecoder::decoder(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
83 }
84
85 ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage(ResourceLoadStatisticsMemoryStore& memoryStore, WorkQueue& workQueue, const String& storageDirectoryPath)
86     : m_memoryStore(memoryStore)
87     , m_workQueue(workQueue)
88     , m_storageDirectoryPath(storageDirectoryPath)
89 {
90     ASSERT(!RunLoop::isMain());
91
92     m_memoryStore.setPersistentStorage(*this);
93
94     populateMemoryStoreFromDisk();
95     startMonitoringDisk();
96 }
97
98 ResourceLoadStatisticsPersistentStorage::~ResourceLoadStatisticsPersistentStorage()
99 {
100     ASSERT(!RunLoop::isMain());
101
102     if (m_hasPendingWrite)
103         writeMemoryStoreToDisk();
104 }
105
106 String ResourceLoadStatisticsPersistentStorage::storageDirectoryPath() const
107 {
108     return m_storageDirectoryPath.isolatedCopy();
109 }
110
111 String ResourceLoadStatisticsPersistentStorage::resourceLogFilePath() const
112 {
113     String storagePath = storageDirectoryPath();
114     if (storagePath.isEmpty())
115         return emptyString();
116
117     return FileSystem::pathByAppendingComponent(storagePath, "full_browsing_session_resourceLog.plist");
118 }
119
120 void ResourceLoadStatisticsPersistentStorage::startMonitoringDisk()
121 {
122     ASSERT(!RunLoop::isMain());
123     if (m_fileMonitor)
124         return;
125
126     String resourceLogPath = resourceLogFilePath();
127     if (resourceLogPath.isEmpty())
128         return;
129
130     m_fileMonitor = std::make_unique<FileMonitor>(resourceLogPath, m_workQueue.copyRef(), [this, weakThis = makeWeakPtr(*this)] (FileMonitor::FileChangeType type) {
131         ASSERT(!RunLoop::isMain());
132         if (!weakThis)
133             return;
134
135         switch (type) {
136         case FileMonitor::FileChangeType::Modification:
137             refreshMemoryStoreFromDisk();
138             break;
139         case FileMonitor::FileChangeType::Removal:
140             m_memoryStore.clear([] { });
141             m_fileMonitor = nullptr;
142             monitorDirectoryForNewStatistics();
143             break;
144         }
145     });
146 }
147
148 void ResourceLoadStatisticsPersistentStorage::monitorDirectoryForNewStatistics()
149 {
150     ASSERT(!RunLoop::isMain());
151
152     String storagePath = storageDirectoryPath();
153     ASSERT(!storagePath.isEmpty());
154
155     if (!FileSystem::fileExists(storagePath)) {
156         if (!FileSystem::makeAllDirectories(storagePath)) {
157             RELEASE_LOG_ERROR(ResourceLoadStatistics, "ResourceLoadStatisticsPersistentStorage: Failed to create directory path %s", storagePath.utf8().data());
158             return;
159         }
160     }
161
162     m_fileMonitor = std::make_unique<FileMonitor>(storagePath, m_workQueue.copyRef(), [this] (FileMonitor::FileChangeType type) {
163         ASSERT(!RunLoop::isMain());
164         if (type == FileMonitor::FileChangeType::Removal) {
165             // Directory was removed!
166             m_fileMonitor = nullptr;
167             return;
168         }
169
170         String resourceLogPath = resourceLogFilePath();
171         ASSERT(!resourceLogPath.isEmpty());
172
173         if (!FileSystem::fileExists(resourceLogPath))
174             return;
175
176         m_fileMonitor = nullptr;
177
178         refreshMemoryStoreFromDisk();
179         startMonitoringDisk();
180     });
181 }
182
183 void ResourceLoadStatisticsPersistentStorage::stopMonitoringDisk()
184 {
185     ASSERT(!RunLoop::isMain());
186     m_fileMonitor = nullptr;
187 }
188
189 // This is called when the file changes on disk.
190 void ResourceLoadStatisticsPersistentStorage::refreshMemoryStoreFromDisk()
191 {
192     ASSERT(!RunLoop::isMain());
193
194     String filePath = resourceLogFilePath();
195     if (filePath.isEmpty())
196         return;
197
198     // We sometimes see file changed events from before our load completed (we start
199     // reading at the first change event, but we might receive a series of events related
200     // to the same file operation). Catch this case to avoid reading overly often.
201     if (!hasFileChangedSince(filePath, m_lastStatisticsFileSyncTime))
202         return;
203
204     WallTime readTime = WallTime::now();
205
206     auto decoder = createDecoderForFile(filePath);
207     if (!decoder)
208         return;
209
210     m_memoryStore.mergeWithDataFromDecoder(*decoder);
211     m_lastStatisticsFileSyncTime = readTime;
212 }
213
214 void ResourceLoadStatisticsPersistentStorage::populateMemoryStoreFromDisk()
215 {
216     ASSERT(!RunLoop::isMain());
217
218     String filePath = resourceLogFilePath();
219     if (filePath.isEmpty() || !FileSystem::fileExists(filePath)) {
220         m_memoryStore.grandfatherExistingWebsiteData([]() { });
221         monitorDirectoryForNewStatistics();
222         return;
223     }
224
225     if (!hasFileChangedSince(filePath, m_lastStatisticsFileSyncTime)) {
226         // No need to grandfather in this case.
227         return;
228     }
229
230     WallTime readTime = WallTime::now();
231
232     auto decoder = createDecoderForFile(filePath);
233     if (!decoder) {
234         m_memoryStore.grandfatherExistingWebsiteData([]() { });
235         return;
236     }
237
238     // Debug mode has a prepoulated memory store.
239     ASSERT_WITH_MESSAGE(m_memoryStore.isEmpty() || m_memoryStore.isDebugModeEnabled(), "This is the initial import so the store should be empty");
240     m_memoryStore.mergeWithDataFromDecoder(*decoder);
241
242     m_lastStatisticsFileSyncTime = readTime;
243
244     m_memoryStore.logTestingEvent("PopulatedWithoutGrandfathering"_s);
245 }
246
247 void ResourceLoadStatisticsPersistentStorage::writeMemoryStoreToDisk()
248 {
249     ASSERT(!RunLoop::isMain());
250
251     m_hasPendingWrite = false;
252     stopMonitoringDisk();
253
254     auto encoder = m_memoryStore.createEncoderFromData();
255     auto rawData = encoder->finishEncoding();
256     if (!rawData)
257         return;
258
259     auto storagePath = storageDirectoryPath();
260     if (!storagePath.isEmpty()) {
261         FileSystem::makeAllDirectories(storagePath);
262         excludeFromBackup();
263     }
264
265     auto handle = FileSystem::openAndLockFile(resourceLogFilePath(), FileSystem::FileOpenMode::Write);
266     if (handle == FileSystem::invalidPlatformFileHandle)
267         return;
268
269     int64_t writtenBytes = FileSystem::writeToFile(handle, rawData->data(), rawData->size());
270     FileSystem::unlockAndCloseFile(handle);
271
272     if (writtenBytes != static_cast<int64_t>(rawData->size()))
273         RELEASE_LOG_ERROR(ResourceLoadStatistics, "ResourceLoadStatisticsPersistentStorage: We only wrote %d out of %zu bytes to disk", static_cast<unsigned>(writtenBytes), rawData->size());
274
275     m_lastStatisticsFileSyncTime = WallTime::now();
276     m_lastStatisticsWriteTime = MonotonicTime::now();
277
278     startMonitoringDisk();
279 }
280
281 void ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore(ForceImmediateWrite forceImmediateWrite)
282 {
283     ASSERT(!RunLoop::isMain());
284
285     auto timeSinceLastWrite = MonotonicTime::now() - m_lastStatisticsWriteTime;
286     if (forceImmediateWrite != ForceImmediateWrite::Yes && timeSinceLastWrite < minimumWriteInterval) {
287         if (!m_hasPendingWrite) {
288             m_hasPendingWrite = true;
289             Seconds delay = minimumWriteInterval - timeSinceLastWrite + 1_s;
290             m_workQueue->dispatchAfter(delay, [weakThis = makeWeakPtr(*this)] {
291                 if (weakThis)
292                     weakThis->writeMemoryStoreToDisk();
293             });
294         }
295         return;
296     }
297
298     writeMemoryStoreToDisk();
299 }
300
301 void ResourceLoadStatisticsPersistentStorage::clear()
302 {
303     ASSERT(!RunLoop::isMain());
304     String filePath = resourceLogFilePath();
305     if (filePath.isEmpty())
306         return;
307
308     stopMonitoringDisk();
309
310     if (!FileSystem::deleteFile(filePath) && FileSystem::fileExists(filePath))
311         RELEASE_LOG_ERROR(ResourceLoadStatistics, "ResourceLoadStatisticsPersistentStorage: Unable to delete statistics file: %s", filePath.utf8().data());
312 }
313
314 #if !PLATFORM(IOS_FAMILY)
315 void ResourceLoadStatisticsPersistentStorage::excludeFromBackup() const
316 {
317 }
318 #endif
319
320 } // namespace WebKit