Moved filesystem code out of WebResourceLoadStatisticsStore class
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Jul 2017 17:52:43 +0000 (17:52 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Jul 2017 17:52:43 +0000 (17:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174435

Reviewed by Brent Fulgham.

Moved filesystem code out of WebResourceLoadStatisticsStore class and into
a new ResourceLoadStatisticsPersistentStorage class to decrease complexity.

* CMakeLists.txt:
* UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm:
* UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp: Added.
(WebKit::hasFileChangedSince):
(WebKit::createDecoderForFile):
(WebKit::ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage):
(WebKit::ResourceLoadStatisticsPersistentStorage::~ResourceLoadStatisticsPersistentStorage):
(WebKit::ResourceLoadStatisticsPersistentStorage::storageDirectoryPath):
(WebKit::ResourceLoadStatisticsPersistentStorage::resourceLogFilePath):
(WebKit::ResourceLoadStatisticsPersistentStorage::startMonitoringDisk):
(WebKit::ResourceLoadStatisticsPersistentStorage::stopMonitoringDisk):
(WebKit::ResourceLoadStatisticsPersistentStorage::refreshMemoryStoreFromDisk):
(WebKit::ResourceLoadStatisticsPersistentStorage::populateMemoryStoreFromDisk):
(WebKit::ResourceLoadStatisticsPersistentStorage::writeMemoryStoreToDisk):
(WebKit::ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore):
(WebKit::ResourceLoadStatisticsPersistentStorage::clear):
(WebKit::ResourceLoadStatisticsPersistentStorage::finishAllPendingWorkSynchronously):
(WebKit::ResourceLoadStatisticsPersistentStorage::excludeFromBackup):
* UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.h: Added.
* UIProcess/Storage/ios/ResourceLoadStatisticsPersistentStorageIOS.mm: Added.
(WebKit::ResourceLoadStatisticsPersistentStorage::excludeFromBackup):
* UIProcess/WebResourceLoadStatisticsStore.cpp:
(WebKit::WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore):
(WebKit::WebResourceLoadStatisticsStore::processStatisticsAndDataRecords):
(WebKit::WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData):
(WebKit::WebResourceLoadStatisticsStore::applicationWillTerminate):
(WebKit::WebResourceLoadStatisticsStore::scheduleClearInMemoryAndPersistent):
(WebKit::WebResourceLoadStatisticsStore::resetDataFromDecoder):
* UIProcess/WebResourceLoadStatisticsStore.h:
* WebKit2.xcodeproj/project.pbxproj:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@219453 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit2/CMakeLists.txt
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm
Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp [new file with mode: 0644]
Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.h [new file with mode: 0644]
Source/WebKit2/UIProcess/Storage/ios/ResourceLoadStatisticsPersistentStorageIOS.mm [new file with mode: 0644]
Source/WebKit2/UIProcess/WebResourceLoadStatisticsStore.cpp
Source/WebKit2/UIProcess/WebResourceLoadStatisticsStore.h
Source/WebKit2/WebKit2.xcodeproj/project.pbxproj

index 15e71f1..b87c913 100644 (file)
@@ -434,6 +434,7 @@ set(WebKit2_SOURCES
 
     UIProcess/Storage/LocalStorageDatabase.cpp
     UIProcess/Storage/LocalStorageDatabaseTracker.cpp
+    UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp
 
     UIProcess/UserContent/WebScriptMessageHandler.cpp
     UIProcess/UserContent/WebUserContentControllerProxy.cpp
index aec37d3..27465f4 100644 (file)
@@ -1,3 +1,44 @@
+2017-07-13  Chris Dumez  <cdumez@apple.com>
+
+        Moved filesystem code out of WebResourceLoadStatisticsStore class
+        https://bugs.webkit.org/show_bug.cgi?id=174435
+
+        Reviewed by Brent Fulgham.
+
+        Moved filesystem code out of WebResourceLoadStatisticsStore class and into
+        a new ResourceLoadStatisticsPersistentStorage class to decrease complexity.
+
+        * CMakeLists.txt:
+        * UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm:
+        * UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp: Added.
+        (WebKit::hasFileChangedSince):
+        (WebKit::createDecoderForFile):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::~ResourceLoadStatisticsPersistentStorage):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::storageDirectoryPath):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::resourceLogFilePath):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::startMonitoringDisk):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::stopMonitoringDisk):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::refreshMemoryStoreFromDisk):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::populateMemoryStoreFromDisk):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::writeMemoryStoreToDisk):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::clear):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::finishAllPendingWorkSynchronously):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::excludeFromBackup):
+        * UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.h: Added.
+        * UIProcess/Storage/ios/ResourceLoadStatisticsPersistentStorageIOS.mm: Added.
+        (WebKit::ResourceLoadStatisticsPersistentStorage::excludeFromBackup):
+        * UIProcess/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore):
+        (WebKit::WebResourceLoadStatisticsStore::processStatisticsAndDataRecords):
+        (WebKit::WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData):
+        (WebKit::WebResourceLoadStatisticsStore::applicationWillTerminate):
+        (WebKit::WebResourceLoadStatisticsStore::scheduleClearInMemoryAndPersistent):
+        (WebKit::WebResourceLoadStatisticsStore::resetDataFromDecoder):
+        * UIProcess/WebResourceLoadStatisticsStore.h:
+        * WebKit2.xcodeproj/project.pbxproj:
+
 2017-07-12  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Web Automation: evaluateJavaScriptFunction should start the callback timeout after the function is applied
index 0c0f8a2..0a5d695 100644 (file)
@@ -32,13 +32,6 @@ using namespace WebCore;
 
 namespace WebKit {
 
-void WebResourceLoadStatisticsStore::platformExcludeFromBackup() const
-{
-#if PLATFORM(IOS)
-    [[NSURL fileURLWithPath:(NSString *)m_statisticsStoragePath] setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
-#endif
-}
-
 void WebResourceLoadStatisticsStore::registerUserDefaultsIfNeeded()
 {
     static dispatch_once_t initOnce;
diff --git a/Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp b/Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.cpp
new file mode 100644 (file)
index 0000000..7357906
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ResourceLoadStatisticsPersistentStorage.h"
+
+#include "Logging.h"
+#include "WebResourceLoadStatisticsStore.h"
+#include <WebCore/FileMonitor.h>
+#include <WebCore/FileSystem.h>
+#include <WebCore/KeyedCoding.h>
+#include <WebCore/SharedBuffer.h>
+#include <wtf/RunLoop.h>
+#include <wtf/WorkQueue.h>
+#include <wtf/threads/BinarySemaphore.h>
+
+namespace WebKit {
+
+constexpr Seconds minimumWriteInterval { 5_min };
+
+using namespace WebCore;
+
+static bool hasFileChangedSince(const String& path, WallTime since)
+{
+    ASSERT(!RunLoop::isMain());
+    time_t modificationTime;
+    if (!getFileModificationTime(path, modificationTime))
+        return true;
+
+    return WallTime::fromRawSeconds(modificationTime) > since;
+}
+
+static std::unique_ptr<KeyedDecoder> createDecoderForFile(const String& path)
+{
+    ASSERT(!RunLoop::isMain());
+    auto handle = openAndLockFile(path, OpenForRead);
+    if (handle == invalidPlatformFileHandle)
+        return nullptr;
+
+    long long fileSize = 0;
+    if (!getFileSize(handle, fileSize)) {
+        unlockAndCloseFile(handle);
+        return nullptr;
+    }
+
+    size_t bytesToRead;
+    if (!WTF::convertSafely(fileSize, bytesToRead)) {
+        unlockAndCloseFile(handle);
+        return nullptr;
+    }
+
+    Vector<char> buffer(bytesToRead);
+    size_t totalBytesRead = readFromFile(handle, buffer.data(), buffer.size());
+
+    unlockAndCloseFile(handle);
+
+    if (totalBytesRead != bytesToRead)
+        return nullptr;
+
+    return KeyedDecoder::decoder(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
+}
+
+ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage(WebResourceLoadStatisticsStore& store, const String& storageDirectoryPath)
+    : m_memoryStore(store)
+    , m_storageDirectoryPath(storageDirectoryPath)
+{
+    ASSERT(RunLoop::isMain());
+    m_memoryStore.statisticsQueue().dispatch([this] {
+        populateMemoryStoreFromDisk();
+        startMonitoringDisk();
+        m_memoryStore.includeTodayAsOperatingDateIfNecessary();
+    });
+}
+
+ResourceLoadStatisticsPersistentStorage::~ResourceLoadStatisticsPersistentStorage()
+{
+    finishAllPendingWorkSynchronously();
+    ASSERT(!m_hasPendingWrite);
+}
+
+String ResourceLoadStatisticsPersistentStorage::storageDirectoryPath() const
+{
+    return m_storageDirectoryPath.isolatedCopy();
+}
+
+String ResourceLoadStatisticsPersistentStorage::resourceLogFilePath() const
+{
+    String storagePath = this->storageDirectoryPath();
+    if (storagePath.isEmpty())
+        return emptyString();
+
+    return pathByAppendingComponent(storagePath, "full_browsing_session_resourceLog.plist");
+}
+
+void ResourceLoadStatisticsPersistentStorage::startMonitoringDisk()
+{
+    ASSERT(!RunLoop::isMain());
+    if (m_fileMonitor)
+        return;
+
+    String resourceLogPath = resourceLogFilePath();
+    if (resourceLogPath.isEmpty())
+        return;
+
+    m_fileMonitor = std::make_unique<FileMonitor>(resourceLogPath, m_memoryStore.statisticsQueue(), [this] (FileMonitor::FileChangeType type) {
+        ASSERT(!RunLoop::isMain());
+        switch (type) {
+        case FileMonitor::FileChangeType::Modification:
+            refreshMemoryStoreFromDisk();
+            break;
+        case FileMonitor::FileChangeType::Removal:
+            m_memoryStore.clearInMemory();
+            m_fileMonitor = nullptr;
+            break;
+        }
+    });
+}
+
+void ResourceLoadStatisticsPersistentStorage::stopMonitoringDisk()
+{
+    ASSERT(!RunLoop::isMain());
+    m_fileMonitor = nullptr;
+}
+
+// This is called when the file changes on disk.
+void ResourceLoadStatisticsPersistentStorage::refreshMemoryStoreFromDisk()
+{
+    ASSERT(!RunLoop::isMain());
+
+    String filePath = resourceLogFilePath();
+    if (filePath.isEmpty())
+        return;
+
+    // We sometimes see file changed events from before our load completed (we start
+    // reading at the first change event, but we might receive a series of events related
+    // to the same file operation). Catch this case to avoid reading overly often.
+    if (!hasFileChangedSince(filePath, m_lastStatisticsFileSyncTime))
+        return;
+
+    WallTime readTime = WallTime::now();
+
+    auto decoder = createDecoderForFile(filePath);
+    if (!decoder)
+        return;
+
+    m_memoryStore.resetDataFromDecoder(*decoder);
+    m_lastStatisticsFileSyncTime = readTime;
+}
+
+void ResourceLoadStatisticsPersistentStorage::populateMemoryStoreFromDisk()
+{
+    ASSERT(!RunLoop::isMain());
+
+    String filePath = resourceLogFilePath();
+    if (filePath.isEmpty() || !fileExists(filePath)) {
+        m_memoryStore.grandfatherExistingWebsiteData();
+        return;
+    }
+
+    if (!hasFileChangedSince(filePath, m_lastStatisticsFileSyncTime)) {
+        // No need to grandfather in this case.
+        return;
+    }
+
+    WallTime readTime = WallTime::now();
+
+    auto decoder = createDecoderForFile(filePath);
+    if (!decoder) {
+        m_memoryStore.grandfatherExistingWebsiteData();
+        return;
+    }
+
+    m_memoryStore.resetDataFromDecoder(*decoder);
+
+    m_lastStatisticsFileSyncTime = readTime;
+
+    if (m_memoryStore.isEmpty())
+        m_memoryStore.grandfatherExistingWebsiteData();
+}
+
+void ResourceLoadStatisticsPersistentStorage::writeMemoryStoreToDisk()
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_hasPendingWrite = false;
+    stopMonitoringDisk();
+
+    auto encoder = m_memoryStore.createEncoderFromData();
+    auto rawData = encoder->finishEncoding();
+    if (!rawData)
+        return;
+
+    auto storagePath = this->storageDirectoryPath();
+    if (!storagePath.isEmpty()) {
+        makeAllDirectories(storagePath);
+        excludeFromBackup();
+    }
+
+    auto handle = openAndLockFile(resourceLogFilePath(), OpenForWrite);
+    if (handle == invalidPlatformFileHandle)
+        return;
+
+    int64_t writtenBytes = writeToFile(handle, rawData->data(), rawData->size());
+    unlockAndCloseFile(handle);
+
+    if (writtenBytes != static_cast<int64_t>(rawData->size()))
+        RELEASE_LOG_ERROR(ResourceLoadStatistics, "WebResourceLoadStatisticsStore: We only wrote %d out of %zu bytes to disk", static_cast<unsigned>(writtenBytes), rawData->size());
+
+    m_lastStatisticsFileSyncTime = WallTime::now();
+    m_lastStatisticsWriteTime = MonotonicTime::now();
+
+    startMonitoringDisk();
+}
+
+void ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore()
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto timeSinceLastWrite = MonotonicTime::now() - m_lastStatisticsWriteTime;
+    if (timeSinceLastWrite < minimumWriteInterval) {
+        if (!m_hasPendingWrite) {
+            m_hasPendingWrite = true;
+            Seconds delay = minimumWriteInterval - timeSinceLastWrite + 1_s;
+            m_memoryStore.statisticsQueue().dispatchAfter(delay, [this] () mutable {
+                writeMemoryStoreToDisk();
+            });
+        }
+        return;
+    }
+
+    writeMemoryStoreToDisk();
+}
+
+void ResourceLoadStatisticsPersistentStorage::clear()
+{
+    ASSERT(!RunLoop::isMain());
+    String filePath = resourceLogFilePath();
+    if (filePath.isEmpty())
+        return;
+
+    stopMonitoringDisk();
+
+    if (!deleteFile(filePath))
+        RELEASE_LOG_ERROR(ResourceLoadStatistics, "Unable to delete statistics file: %s", filePath.utf8().data());
+}
+
+void ResourceLoadStatisticsPersistentStorage::finishAllPendingWorkSynchronously()
+{
+    BinarySemaphore semaphore;
+    // Make sure any pending work in our queue is finished before we terminate.
+    m_memoryStore.statisticsQueue().dispatch([&semaphore, this] {
+        // Write final file state to disk.
+        if (m_hasPendingWrite)
+            writeMemoryStoreToDisk();
+        semaphore.signal();
+    });
+    semaphore.wait(WallTime::infinity());
+}
+
+#if !PLATFORM(IOS)
+void ResourceLoadStatisticsPersistentStorage::excludeFromBackup() const
+{
+}
+#endif
+
+} // namespace WebKit
diff --git a/Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.h b/Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsPersistentStorage.h
new file mode 100644 (file)
index 0000000..18285f8
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Forward.h>
+#include <wtf/MonotonicTime.h>
+#include <wtf/WallTime.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+class FileMonitor;
+}
+
+namespace WebKit {
+
+class WebResourceLoadStatisticsStore;
+
+class ResourceLoadStatisticsPersistentStorage {
+public:
+    ResourceLoadStatisticsPersistentStorage(WebResourceLoadStatisticsStore&, const String& storageDirectoryPath);
+    ~ResourceLoadStatisticsPersistentStorage();
+
+    void scheduleOrWriteMemoryStore();
+    void clear();
+
+    void finishAllPendingWorkSynchronously();
+
+private:
+    String storageDirectoryPath() const;
+    String resourceLogFilePath() const;
+
+    void startMonitoringDisk();
+    void stopMonitoringDisk();
+
+    void writeMemoryStoreToDisk();
+    void populateMemoryStoreFromDisk();
+    void excludeFromBackup() const;
+    void refreshMemoryStoreFromDisk();
+
+    WebResourceLoadStatisticsStore& m_memoryStore;
+    const String m_storageDirectoryPath;
+    std::unique_ptr<WebCore::FileMonitor> m_fileMonitor;
+    WallTime m_lastStatisticsFileSyncTime;
+    MonotonicTime m_lastStatisticsWriteTime;
+    bool m_hasPendingWrite { false };
+};
+
+}
diff --git a/Source/WebKit2/UIProcess/Storage/ios/ResourceLoadStatisticsPersistentStorageIOS.mm b/Source/WebKit2/UIProcess/Storage/ios/ResourceLoadStatisticsPersistentStorageIOS.mm
new file mode 100644 (file)
index 0000000..5b5cbd2
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "ResourceLoadStatisticsPersistentStorage.h"
+
+#if PLATFORM(IOS)
+
+namespace WebKit {
+
+void ResourceLoadStatisticsPersistentStorage::excludeFromBackup() const
+{
+    [[NSURL fileURLWithPath:(NSString *)m_storageDirectoryPath] setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
+}
+
+}
+
+#endif
index 185a18b..80b8abb 100644 (file)
 #include "WebsiteDataFetchOption.h"
 #include "WebsiteDataStore.h"
 #include "WebsiteDataType.h"
-#include <WebCore/FileMonitor.h>
-#include <WebCore/FileSystem.h>
 #include <WebCore/KeyedCoding.h>
 #include <WebCore/ResourceLoadStatistics.h>
-#include <WebCore/SharedBuffer.h>
 #include <wtf/CrossThreadCopier.h>
 #include <wtf/MathExtras.h>
 #include <wtf/NeverDestroyed.h>
-#include <wtf/threads/BinarySemaphore.h>
 
 using namespace WebCore;
 
 namespace WebKit {
 
-constexpr Seconds minimumStatisticsFileWriteInterval { 5_min };
 constexpr unsigned operatingDatesWindow { 30 };
 constexpr unsigned statisticsModelVersion { 7 };
 constexpr unsigned maxImportance { 3 };
@@ -84,8 +79,8 @@ static const OptionSet<WebsiteDataType>& dataTypesToRemove()
 
 WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore(const String& resourceLoadStatisticsDirectory, UpdateCookiePartitioningForDomainsHandler&& updateCookiePartitioningForDomainsHandler)
     : m_statisticsQueue(WorkQueue::create("WebResourceLoadStatisticsStore Process Data Queue", WorkQueue::Type::Serial, WorkQueue::QOS::Utility))
+    , m_persistentStorage(*this, resourceLoadStatisticsDirectory)
     , m_updateCookiePartitioningForDomainsHandler(WTFMove(updateCookiePartitioningForDomainsHandler))
-    , m_statisticsStoragePath(resourceLoadStatisticsDirectory)
     , m_dailyTasksTimer(RunLoop::main(), this, &WebResourceLoadStatisticsStore::performDailyTasks)
 {
     ASSERT(RunLoop::isMain());
@@ -94,10 +89,6 @@ WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore(const String& res
     registerUserDefaultsIfNeeded();
 #endif
 
-    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
-        readDataFromDiskIfNeeded();
-        startMonitoringStatisticsStorage();
-    });
     m_statisticsQueue->dispatchAfter(5_s, [this, protectedThis = makeRef(*this)] {
         if (m_parameters.shouldSubmitTelemetry)
             WebResourceLoadStatisticsTelemetry::calculateAndSubmit(*this);
@@ -155,7 +146,7 @@ void WebResourceLoadStatisticsStore::processStatisticsAndDataRecords()
             });
         }
 
-        scheduleOrWriteStoreToDisk();
+        m_persistentStorage.scheduleOrWriteMemoryStore();
     });
 }
 
@@ -185,79 +176,6 @@ void WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData()
         });
     });
 }
-
-WallTime WebResourceLoadStatisticsStore::statisticsFileModificationTime(const String& path) const
-{
-    ASSERT(!RunLoop::isMain());
-    time_t modificationTime;
-    if (!getFileModificationTime(path, modificationTime))
-        return { };
-
-    return WallTime::fromRawSeconds(modificationTime);
-}
-
-bool WebResourceLoadStatisticsStore::hasStatisticsFileChangedSinceLastSync(const String& path) const
-{
-    return statisticsFileModificationTime(path) > m_lastStatisticsFileSyncTime;
-}
-
-void WebResourceLoadStatisticsStore::readDataFromDiskIfNeeded()
-{
-    ASSERT(!RunLoop::isMain());
-
-    String resourceLog = resourceLogFilePath();
-    if (resourceLog.isEmpty() || !fileExists(resourceLog)) {
-        grandfatherExistingWebsiteData();
-        return;
-    }
-
-    if (!hasStatisticsFileChangedSinceLastSync(resourceLog)) {
-        // No need to grandfather in this case.
-        return;
-    }
-
-    WallTime readTime = WallTime::now();
-
-    auto decoder = createDecoderFromDisk(resourceLog);
-    if (!decoder) {
-        grandfatherExistingWebsiteData();
-        return;
-    }
-
-    clearInMemory();
-    populateFromDecoder(*decoder);
-
-    m_lastStatisticsFileSyncTime = readTime;
-
-    if (m_resourceStatisticsMap.isEmpty())
-        grandfatherExistingWebsiteData();
-
-    includeTodayAsOperatingDateIfNecessary();
-}
-    
-void WebResourceLoadStatisticsStore::refreshFromDisk()
-{
-    ASSERT(!RunLoop::isMain());
-
-    String resourceLog = resourceLogFilePath();
-    if (resourceLog.isEmpty())
-        return;
-
-    // We sometimes see file changed events from before our load completed (we start
-    // reading at the first change event, but we might receive a series of events related
-    // to the same file operation). Catch this case to avoid reading overly often.
-    if (!hasStatisticsFileChangedSinceLastSync(resourceLog))
-        return;
-
-    WallTime readTime = WallTime::now();
-
-    auto decoder = createDecoderFromDisk(resourceLog);
-    if (!decoder)
-        return;
-
-    populateFromDecoder(*decoder);
-    m_lastStatisticsFileSyncTime = readTime;
-}
     
 void WebResourceLoadStatisticsStore::processWillOpenConnection(WebProcessProxy&, IPC::Connection& connection)
 {
@@ -271,177 +189,7 @@ void WebResourceLoadStatisticsStore::processDidCloseConnection(WebProcessProxy&,
 
 void WebResourceLoadStatisticsStore::applicationWillTerminate()
 {
-    BinarySemaphore semaphore;
-    // Make sure any pending work in our queue is finished before we terminate.
-    m_statisticsQueue->dispatch([&semaphore, this, protectedThis = makeRef(*this)] {
-        // Write final file state to disk.
-        if (m_didScheduleWrite)
-            writeStoreToDisk();
-
-        semaphore.signal();
-    });
-    semaphore.wait(WallTime::infinity());
-}
-
-String WebResourceLoadStatisticsStore::statisticsStoragePath() const
-{
-    return m_statisticsStoragePath.isolatedCopy();
-}
-
-String WebResourceLoadStatisticsStore::resourceLogFilePath() const
-{
-    String statisticsStoragePath = this->statisticsStoragePath();
-    if (statisticsStoragePath.isEmpty())
-        return emptyString();
-
-    return pathByAppendingComponent(statisticsStoragePath, "full_browsing_session_resourceLog.plist");
-}
-
-void WebResourceLoadStatisticsStore::writeStoreToDisk()
-{
-    ASSERT(!RunLoop::isMain());
-    
-    stopMonitoringStatisticsStorage();
-
-    syncWithExistingStatisticsStorageIfNeeded();
-
-    auto encoder = createEncoderFromData();
-    RefPtr<SharedBuffer> rawData = encoder->finishEncoding();
-    if (!rawData)
-        return;
-
-    auto statisticsStoragePath = this->statisticsStoragePath();
-    if (!statisticsStoragePath.isEmpty()) {
-        makeAllDirectories(statisticsStoragePath);
-        platformExcludeFromBackup();
-    }
-
-    auto handle = openAndLockFile(resourceLogFilePath(), OpenForWrite);
-    if (handle == invalidPlatformFileHandle)
-        return;
-
-    int64_t writtenBytes = writeToFile(handle, rawData->data(), rawData->size());
-    unlockAndCloseFile(handle);
-
-    if (writtenBytes != static_cast<int64_t>(rawData->size()))
-        RELEASE_LOG_ERROR(ResourceLoadStatistics, "WebResourceLoadStatisticsStore: We only wrote %d out of %zu bytes to disk", static_cast<unsigned>(writtenBytes), rawData->size());
-
-    m_lastStatisticsFileSyncTime = WallTime::now();
-    m_lastStatisticsWriteTime = MonotonicTime::now();
-
-    startMonitoringStatisticsStorage();
-    m_didScheduleWrite = false;
-}
-
-void WebResourceLoadStatisticsStore::scheduleOrWriteStoreToDisk()
-{
-    ASSERT(!RunLoop::isMain());
-
-    auto timeSinceLastWrite = MonotonicTime::now() - m_lastStatisticsWriteTime;
-    if (timeSinceLastWrite < minimumStatisticsFileWriteInterval) {
-        if (!m_didScheduleWrite) {
-            m_didScheduleWrite = true;
-            Seconds delay = minimumStatisticsFileWriteInterval - timeSinceLastWrite + 1_s;
-            m_statisticsQueue->dispatchAfter(delay, [this, protectedThis = makeRef(*this)] {
-                writeStoreToDisk();
-            });
-        }
-        return;
-    }
-
-    writeStoreToDisk();
-}
-
-void WebResourceLoadStatisticsStore::deleteStoreFromDisk()
-{
-    ASSERT(!RunLoop::isMain());
-    String resourceLogPath = resourceLogFilePath();
-    if (resourceLogPath.isEmpty())
-        return;
-
-    stopMonitoringStatisticsStorage();
-
-    if (!deleteFile(resourceLogPath))
-        RELEASE_LOG_ERROR(ResourceLoadStatistics, "Unable to delete statistics file: %s", resourceLogPath.utf8().data());
-}
-
-void WebResourceLoadStatisticsStore::startMonitoringStatisticsStorage()
-{
-    ASSERT(!RunLoop::isMain());
-    if (m_statisticsStorageMonitor)
-        return;
-    
-    String resourceLogPath = resourceLogFilePath();
-    if (resourceLogPath.isEmpty())
-        return;
-    
-    m_statisticsStorageMonitor = std::make_unique<FileMonitor>(resourceLogPath, m_statisticsQueue.copyRef(), [this] (FileMonitor::FileChangeType type) {
-        ASSERT(!RunLoop::isMain());
-        switch (type) {
-        case FileMonitor::FileChangeType::Modification:
-            refreshFromDisk();
-            break;
-        case FileMonitor::FileChangeType::Removal:
-            clearInMemory();
-            m_statisticsStorageMonitor = nullptr;
-            break;
-        }
-    });
-}
-
-void WebResourceLoadStatisticsStore::stopMonitoringStatisticsStorage()
-{
-    ASSERT(!RunLoop::isMain());
-    m_statisticsStorageMonitor = nullptr;
-}
-
-void WebResourceLoadStatisticsStore::syncWithExistingStatisticsStorageIfNeeded()
-{
-    ASSERT(!RunLoop::isMain());
-    if (m_statisticsStorageMonitor)
-        return;
-
-    String resourceLog = resourceLogFilePath();
-    if (resourceLog.isEmpty() || !fileExists(resourceLog))
-        return;
-
-    refreshFromDisk();
-}
-
-#if !PLATFORM(COCOA)
-void WebResourceLoadStatisticsStore::platformExcludeFromBackup() const
-{
-}
-#endif
-
-std::unique_ptr<KeyedDecoder> WebResourceLoadStatisticsStore::createDecoderFromDisk(const String& path) const
-{
-    ASSERT(!RunLoop::isMain());
-    auto handle = openAndLockFile(path, OpenForRead);
-    if (handle == invalidPlatformFileHandle)
-        return nullptr;
-    
-    long long fileSize = 0;
-    if (!getFileSize(handle, fileSize)) {
-        unlockAndCloseFile(handle);
-        return nullptr;
-    }
-    
-    size_t bytesToRead;
-    if (!WTF::convertSafely(fileSize, bytesToRead)) {
-        unlockAndCloseFile(handle);
-        return nullptr;
-    }
-
-    Vector<char> buffer(bytesToRead);
-    size_t totalBytesRead = readFromFile(handle, buffer.data(), buffer.size());
-
-    unlockAndCloseFile(handle);
-
-    if (totalBytesRead != bytesToRead)
-        return nullptr;
-
-    return KeyedDecoder::decoder(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
+    m_persistentStorage.finishAllPendingWorkSynchronously();
 }
 
 void WebResourceLoadStatisticsStore::performDailyTasks()
@@ -653,7 +401,7 @@ void WebResourceLoadStatisticsStore::scheduleClearInMemoryAndPersistent()
     ASSERT(RunLoop::isMain());
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
         clearInMemory();
-        deleteStoreFromDisk();
+        m_persistentStorage.clear();
         grandfatherExistingWebsiteData();
     });
 }
@@ -732,11 +480,11 @@ std::unique_ptr<KeyedEncoder> WebResourceLoadStatisticsStore::createEncoderFromD
     return encoder;
 }
 
-void WebResourceLoadStatisticsStore::populateFromDecoder(KeyedDecoder& decoder)
+void WebResourceLoadStatisticsStore::resetDataFromDecoder(KeyedDecoder& decoder)
 {
     ASSERT(!RunLoop::isMain());
-    if (!m_resourceStatisticsMap.isEmpty())
-        return;
+
+    clearInMemory();
 
     unsigned versionOnDisk;
     if (!decoder.decodeUInt32("version", versionOnDisk))
index aad9e6e..e4c62ae 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "Connection.h"
 #include "ResourceLoadStatisticsClassifier.h"
+#include "ResourceLoadStatisticsPersistentStorage.h"
 #include <wtf/MonotonicTime.h>
 #include <wtf/RunLoop.h>
 #include <wtf/Vector.h>
@@ -42,7 +43,6 @@ class WorkQueue;
 }
 
 namespace WebCore {
-class FileMonitor;
 class KeyedDecoder;
 class KeyedEncoder;
 class URL;
@@ -55,7 +55,6 @@ class WebProcessProxy;
 
 enum class ShouldClearFirst;
 
-// FIXME: We should consider moving FileSystem I/O to a separate class.
 class WebResourceLoadStatisticsStore final : public IPC::Connection::WorkQueueMessageReceiver {
 public:
     using UpdateCookiePartitioningForDomainsHandler = WTF::Function<void(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, ShouldClearFirst)>;
@@ -66,6 +65,9 @@ public:
 
     ~WebResourceLoadStatisticsStore();
 
+    bool isEmpty() const { return m_resourceStatisticsMap.isEmpty(); }
+    WorkQueue& statisticsQueue() { return m_statisticsQueue.get(); }
+
     void setNotifyPagesWhenDataRecordsWereScanned(bool value) { m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned = value; }
     void setShouldClassifyResourcesBeforeDataRecordsRemoval(bool value) { m_parameters.shouldClassifyResourcesBeforeDataRecordsRemoval = value; }
     void setShouldSubmitTelemetry(bool value) { m_parameters.shouldSubmitTelemetry = value; }
@@ -109,33 +111,21 @@ public:
     void pruneStatisticsIfNeeded();
 
     void resetParametersToDefaultValues();
+
+    std::unique_ptr<WebCore::KeyedEncoder> createEncoderFromData() const;
+    void resetDataFromDecoder(WebCore::KeyedDecoder&);
+    void clearInMemory();
+    void grandfatherExistingWebsiteData();
+    void includeTodayAsOperatingDateIfNecessary();
     
 private:
     WebResourceLoadStatisticsStore(const String&, UpdateCookiePartitioningForDomainsHandler&&);
 
-    void readDataFromDiskIfNeeded();
-
     void removeDataRecords();
-    void startMonitoringStatisticsStorage();
-    void stopMonitoringStatisticsStorage();
-
-    String statisticsStoragePath() const;
-    String resourceLogFilePath() const;
 
     // IPC::MessageReceiver
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
-    void grandfatherExistingWebsiteData();
-
-    void writeStoreToDisk();
-    void scheduleOrWriteStoreToDisk();
-    std::unique_ptr<WebCore::KeyedDecoder> createDecoderFromDisk(const String& path) const;
-    WallTime statisticsFileModificationTime(const String& label) const;
-    void platformExcludeFromBackup() const;
-    void deleteStoreFromDisk();
-    void syncWithExistingStatisticsStorageIfNeeded();
-    void refreshFromDisk();
-    bool hasStatisticsFileChangedSinceLastSync(const String& path) const;
     void performDailyTasks();
     bool shouldRemoveDataRecords() const;
     void setDataRecordsBeingRemoved(bool);
@@ -143,15 +133,11 @@ private:
     bool shouldPartitionCookies(const WebCore::ResourceLoadStatistics&) const;
     bool hasStatisticsExpired(const WebCore::ResourceLoadStatistics&) const;
     bool hasHadUnexpiredRecentUserInteraction(WebCore::ResourceLoadStatistics&) const;
-    void includeTodayAsOperatingDateIfNecessary();
     Vector<String> topPrivatelyControlledDomainsToRemoveWebsiteDataFor();
     void updateCookiePartitioning();
     void updateCookiePartitioningForDomains(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, ShouldClearFirst);
     void mergeStatistics(Vector<WebCore::ResourceLoadStatistics>&&);
     WebCore::ResourceLoadStatistics& ensureResourceStatisticsForPrimaryDomain(const String&);
-    std::unique_ptr<WebCore::KeyedEncoder> createEncoderFromData() const;
-    void populateFromDecoder(WebCore::KeyedDecoder&);
-    void clearInMemory();
 
     void resetCookiePartitioningState();
 
@@ -178,22 +164,18 @@ private:
     ResourceLoadStatisticsClassifier m_resourceLoadStatisticsClassifier;
 #endif
     Ref<WTF::WorkQueue> m_statisticsQueue;
-    std::unique_ptr<WebCore::FileMonitor> m_statisticsStorageMonitor;
+    ResourceLoadStatisticsPersistentStorage m_persistentStorage;
     Deque<WTF::WallTime> m_operatingDates;
 
     UpdateCookiePartitioningForDomainsHandler m_updateCookiePartitioningForDomainsHandler;
 
     WallTime m_endOfGrandfatheringTimestamp;
-    const String m_statisticsStoragePath;
-    WallTime m_lastStatisticsFileSyncTime;
-    MonotonicTime m_lastStatisticsWriteTime;
     RunLoop::Timer<WebResourceLoadStatisticsStore> m_dailyTasksTimer;
     MonotonicTime m_lastTimeDataRecordsWereRemoved;
 
     Parameters m_parameters;
 
     bool m_dataRecordsBeingRemoved { false };
-    bool m_didScheduleWrite { false };
 };
 
 } // namespace WebKit
index b06fe37..4eaf46f 100644 (file)
                8310428C1BD6B66F00A715E4 /* NetworkCacheSubresourcesEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8310428A1BD6B66F00A715E4 /* NetworkCacheSubresourcesEntry.cpp */; };
                831EEBBD1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = 831EEBBB1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.h */; };
                831EEBBE1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 831EEBBC1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.cpp */; };
+               8324355C1F1714670035AA3A /* ResourceLoadStatisticsPersistentStorageIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8324355B1F1714610035AA3A /* ResourceLoadStatisticsPersistentStorageIOS.mm */; };
                832AE2521BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 832AE2501BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.h */; };
                832AE2531BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832AE2511BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.cpp */; };
                832ED18B1E2FE157006BA64A /* PerActivityStateCPUUsageSampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832ED1891E2FE13B006BA64A /* PerActivityStateCPUUsageSampler.cpp */; };
                8372DB281A67562800C697C5 /* WebPageDiagnosticLoggingClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8372DB261A67562800C697C5 /* WebPageDiagnosticLoggingClient.cpp */; };
                8372DB291A67562800C697C5 /* WebPageDiagnosticLoggingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 8372DB271A67562800C697C5 /* WebPageDiagnosticLoggingClient.h */; };
                8372DB2F1A677D4A00C697C5 /* WKDiagnosticLoggingResultType.h in Headers */ = {isa = PBXBuildFile; fileRef = 8372DB2E1A677D4A00C697C5 /* WKDiagnosticLoggingResultType.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               83850C0C1F16BA9000C15E52 /* ResourceLoadStatisticsPersistentStorage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83850C0A1F16BA6C00C15E52 /* ResourceLoadStatisticsPersistentStorage.cpp */; };
+               83850C0D1F16BA9000C15E52 /* ResourceLoadStatisticsPersistentStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 83850C0B1F16BA6C00C15E52 /* ResourceLoadStatisticsPersistentStorage.h */; };
                83891B631A68B3420030F386 /* APIDiagnosticLoggingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 83891B621A68B3420030F386 /* APIDiagnosticLoggingClient.h */; };
                83891B691A68BEBC0030F386 /* _WKDiagnosticLoggingDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 83891B681A68BEBC0030F386 /* _WKDiagnosticLoggingDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                83891B6C1A68C30B0030F386 /* DiagnosticLoggingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 83891B6A1A68C30B0030F386 /* DiagnosticLoggingClient.h */; };
                8310428A1BD6B66F00A715E4 /* NetworkCacheSubresourcesEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkCacheSubresourcesEntry.cpp; sourceTree = "<group>"; };
                831EEBBB1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkCacheSpeculativeLoad.h; sourceTree = "<group>"; };
                831EEBBC1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkCacheSpeculativeLoad.cpp; sourceTree = "<group>"; };
+               8324355B1F1714610035AA3A /* ResourceLoadStatisticsPersistentStorageIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResourceLoadStatisticsPersistentStorageIOS.mm; sourceTree = "<group>"; };
                832AE2501BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkCacheSpeculativeLoadManager.h; sourceTree = "<group>"; };
                832AE2511BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkCacheSpeculativeLoadManager.cpp; sourceTree = "<group>"; };
                832ED1891E2FE13B006BA64A /* PerActivityStateCPUUsageSampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerActivityStateCPUUsageSampler.cpp; sourceTree = "<group>"; };
                8372DB261A67562800C697C5 /* WebPageDiagnosticLoggingClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebPageDiagnosticLoggingClient.cpp; sourceTree = "<group>"; };
                8372DB271A67562800C697C5 /* WebPageDiagnosticLoggingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebPageDiagnosticLoggingClient.h; sourceTree = "<group>"; };
                8372DB2E1A677D4A00C697C5 /* WKDiagnosticLoggingResultType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDiagnosticLoggingResultType.h; sourceTree = "<group>"; };
+               83850C0A1F16BA6C00C15E52 /* ResourceLoadStatisticsPersistentStorage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResourceLoadStatisticsPersistentStorage.cpp; sourceTree = "<group>"; };
+               83850C0B1F16BA6C00C15E52 /* ResourceLoadStatisticsPersistentStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceLoadStatisticsPersistentStorage.h; sourceTree = "<group>"; };
                83891B621A68B3420030F386 /* APIDiagnosticLoggingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIDiagnosticLoggingClient.h; sourceTree = "<group>"; };
                83891B681A68BEBC0030F386 /* _WKDiagnosticLoggingDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKDiagnosticLoggingDelegate.h; sourceTree = "<group>"; };
                83891B6A1A68C30B0030F386 /* DiagnosticLoggingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiagnosticLoggingClient.h; sourceTree = "<group>"; };
                                1A1D8BA01731A36300141DA4 /* LocalStorageDatabase.h */,
                                1A8C728A1738477C000A6554 /* LocalStorageDatabaseTracker.cpp */,
                                1A8C728B1738477C000A6554 /* LocalStorageDatabaseTracker.h */,
+                               83850C0A1F16BA6C00C15E52 /* ResourceLoadStatisticsPersistentStorage.cpp */,
+                               83850C0B1F16BA6C00C15E52 /* ResourceLoadStatisticsPersistentStorage.h */,
                                1A44B95916B73F9F00B7BBD8 /* StorageManager.cpp */,
                                1A44B95A16B73F9F00B7BBD8 /* StorageManager.h */,
                                1AB31A9316BC65AB00F6DBC9 /* StorageManager.messages.in */,
                        isa = PBXGroup;
                        children = (
                                5120C8301E54E2650025B250 /* LocalStorageDatabaseTrackerIOS.mm */,
+                               8324355B1F1714610035AA3A /* ResourceLoadStatisticsPersistentStorageIOS.mm */,
                        );
                        path = ios;
                        sourceTree = "<group>";
                                1AD4C1931B39F33200ABC28E /* ApplicationStateTracker.h in Headers */,
                                1AEFD27911D16C81008219D3 /* ArgumentCoder.h in Headers */,
                                1AEFD2F711D1807B008219D3 /* ArgumentCoders.h in Headers */,
+                               83850C0D1F16BA9000C15E52 /* ResourceLoadStatisticsPersistentStorage.h in Headers */,
                                1AAF0C4A12B16334008E49E2 /* ArgumentCodersCF.h in Headers */,
                                E179FD9C134D38060015B883 /* ArgumentCodersMac.h in Headers */,
                                CE1A0BD21A48E6C60054EF74 /* AssertionServicesSPI.h in Headers */,
                                E4436ECD1A0D040B00EAD204 /* NetworkCacheKey.cpp in Sources */,
                                831EEBBE1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.cpp in Sources */,
                                832AE2531BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.cpp in Sources */,
+                               83850C0C1F16BA9000C15E52 /* ResourceLoadStatisticsPersistentStorage.cpp in Sources */,
                                83BDCCB91AC5FDB6003F6441 /* NetworkCacheStatistics.cpp in Sources */,
                                E4436ED01A0D040B00EAD204 /* NetworkCacheStorage.cpp in Sources */,
                                8310428C1BD6B66F00A715E4 /* NetworkCacheSubresourcesEntry.cpp in Sources */,
                                1ACC50F11CBC381D003C7D03 /* WKOpenPanelParameters.mm in Sources */,
                                BC85806312B8505700EDEB2E /* WKOpenPanelParametersRef.cpp in Sources */,
                                BC85806212B8505700EDEB2E /* WKOpenPanelResultListener.cpp in Sources */,
+                               8324355C1F1714670035AA3A /* ResourceLoadStatisticsPersistentStorageIOS.mm in Sources */,
                                BCD597D6112B56DC00EC8C23 /* WKPage.cpp in Sources */,
                                7C89D29B1A67837B003A5FDE /* WKPageConfigurationRef.cpp in Sources */,
                                BC7B633812A45ABA00D174A4 /* WKPageGroup.cpp in Sources */,