#pragma once
#include <WebCore/NativeImage.h>
#include <WebCore/SQLiteDatabase.h>
#include <wtf/Condition.h>
#include <wtf/HashCountedSet.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
#include <wtf/RunLoop.h>
#include <wtf/glib/RunLoopSourcePriority.h>
#include <wtf/text/StringHash.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
class SharedBuffer;
}
namespace WebKit {
class IconDatabaseClient {
public:
virtual ~IconDatabaseClient() = default;
virtual void didImportIconURLForPageURL(const String&) { }
virtual void didImportIconDataForPageURL(const String&) { }
virtual void didChangeIconForPageURL(const String&) { }
virtual void didFinishURLImport() { }
};
class IconDatabase {
WTF_MAKE_FAST_ALLOCATED;
private:
class IconSnapshot {
public:
IconSnapshot() = default;
IconSnapshot(const String& iconURL, int timestamp, WebCore::SharedBuffer* data)
: m_iconURL(iconURL)
, m_timestamp(timestamp)
, m_data(data)
{ }
const String& iconURL() const { return m_iconURL; }
int timestamp() const { return m_timestamp; }
WebCore::SharedBuffer* data() const { return m_data.get(); }
private:
String m_iconURL;
int m_timestamp { 0 };
RefPtr<WebCore::SharedBuffer> m_data;
};
class IconRecord : public RefCounted<IconRecord> {
public:
static Ref<IconRecord> create(const String& url)
{
return adoptRef(*new IconRecord(url));
}
~IconRecord();
time_t getTimestamp() { return m_stamp; }
void setTimestamp(time_t stamp) { m_stamp = stamp; }
void setImageData(RefPtr<WebCore::SharedBuffer>&&);
WebCore::NativeImagePtr image(const WebCore::IntSize&);
String iconURL() { return m_iconURL; }
enum class ImageDataStatus { Present, Missing, Unknown };
ImageDataStatus imageDataStatus();
HashSet<String>& retainingPageURLs() { return m_retainingPageURLs; }
IconSnapshot snapshot(bool forDeletion = false) const;
private:
IconRecord(const String& url);
String m_iconURL;
time_t m_stamp { 0 };
RefPtr<WebCore::SharedBuffer> m_imageData;
WebCore::NativeImagePtr m_image;
HashSet<String> m_retainingPageURLs;
bool m_dataSet { false };
};
class PageURLSnapshot {
public:
PageURLSnapshot() = default;
PageURLSnapshot(const String& pageURL, const String& iconURL)
: m_pageURL(pageURL)
, m_iconURL(iconURL)
{ }
const String& pageURL() const { return m_pageURL; }
const String& iconURL() const { return m_iconURL; }
private:
String m_pageURL;
String m_iconURL;
};
class PageURLRecord {
WTF_MAKE_NONCOPYABLE(PageURLRecord); WTF_MAKE_FAST_ALLOCATED;
public:
PageURLRecord(const String& pageURL);
~PageURLRecord();
inline String url() const { return m_pageURL; }
void setIconRecord(RefPtr<IconRecord>&&);
IconRecord* iconRecord() { return m_iconRecord.get(); }
PageURLSnapshot snapshot(bool forDeletion = false) const;
bool retain(int count)
{
bool wasRetained = m_retainCount > 0;
m_retainCount += count;
return wasRetained;
}
bool release(int count)
{
ASSERT(m_retainCount >= count);
m_retainCount -= count;
return m_retainCount > 0;
}
int retainCount() const { return m_retainCount; }
private:
String m_pageURL;
RefPtr<IconRecord> m_iconRecord;
int m_retainCount { 0 };
};
class MainThreadNotifier {
public:
MainThreadNotifier()
: m_timer(RunLoop::main(), this, &MainThreadNotifier::timerFired)
{
m_timer.setPriority(RunLoopSourcePriority::MainThreadDispatcherTimer);
}
void setActive(bool active)
{
m_isActive.store(active);
}
void notify(Function<void()>&& notification)
{
if (!m_isActive.load())
return;
{
LockHolder locker(m_notificationQueueLock);
m_notificationQueue.append(WTFMove(notification));
}
if (!m_timer.isActive())
m_timer.startOneShot(0_s);
}
void stop()
{
setActive(false);
m_timer.stop();
LockHolder locker(m_notificationQueueLock);
m_notificationQueue.clear();
}
private:
void timerFired()
{
Deque<Function<void()>> notificationQueue;
{
LockHolder locker(m_notificationQueueLock);
notificationQueue = WTFMove(m_notificationQueue);
}
if (!m_isActive.load())
return;
while (!notificationQueue.isEmpty()) {
auto function = notificationQueue.takeFirst();
function();
}
}
Deque<Function<void()>> m_notificationQueue;
Lock m_notificationQueueLock;
Atomic<bool> m_isActive;
RunLoop::Timer<MainThreadNotifier> m_timer;
};
public:
IconDatabase();
~IconDatabase();
enum class IconLoadDecision { Yes, No, Unknown };
void setClient(std::unique_ptr<IconDatabaseClient>&&);
bool open(const String& directory, const String& filename);
void close();
void removeAllIcons();
void retainIconForPageURL(const String&);
void releaseIconForPageURL(const String&);
void setIconDataForIconURL(RefPtr<WebCore::SharedBuffer>&&, const String& iconURL);
void setIconURLForPageURL(const String& iconURL, const String& pageURL);
enum class IsKnownIcon { No, Yes };
std::pair<WebCore::NativeImagePtr, IsKnownIcon> synchronousIconForPageURL(const String&, const WebCore::IntSize&);
String synchronousIconURLForPageURL(const String&);
bool synchronousIconDataKnownForIconURL(const String&);
IconLoadDecision synchronousLoadDecisionForIconURL(const String&);
void setEnabled(bool);
bool isEnabled() const;
void setPrivateBrowsingEnabled(bool flag);
bool isPrivateBrowsingEnabled() const;
static void delayDatabaseCleanup();
static void allowDatabaseCleanup();
static void checkIntegrityBeforeOpening();
private:
void wakeSyncThread();
void scheduleOrDeferSyncTimer();
void syncTimerFired();
RunLoop::Timer<IconDatabase> m_syncTimer;
RefPtr<Thread> m_syncThread;
bool m_syncThreadRunning { false };
bool m_scheduleOrDeferSyncTimerRequested { false };
public:
bool isOpen() const;
String databasePath() const;
static String defaultDatabaseFilename();
private:
Ref<IconRecord> getOrCreateIconRecord(const String& iconURL);
PageURLRecord* getOrCreatePageURLRecord(const String& pageURL);
bool m_isEnabled { false };
bool m_privateBrowsingEnabled { false };
mutable Lock m_syncLock;
Condition m_syncCondition;
String m_databaseDirectory;
String m_completeDatabasePath;
bool m_threadTerminationRequested { false };
bool m_removeIconsRequested { false };
bool m_iconURLImportComplete { false };
bool m_syncThreadHasWorkToDo { false };
Lock m_urlAndIconLock;
HashMap<String, IconRecord*> m_iconURLToRecordMap;
HashMap<String, PageURLRecord*> m_pageURLToRecordMap;
HashSet<String> m_retainedPageURLs;
Lock m_pendingSyncLock;
HashMap<String, PageURLSnapshot> m_pageURLsPendingSync;
HashMap<String, IconSnapshot> m_iconsPendingSync;
Lock m_pendingReadingLock;
HashSet<String> m_pageURLsPendingImport;
HashSet<String> m_pageURLsInterestedInIcons;
HashSet<IconRecord*> m_iconsPendingReading;
Lock m_urlsToRetainOrReleaseLock;
HashCountedSet<String> m_urlsToRetain;
HashCountedSet<String> m_urlsToRelease;
bool m_retainOrReleaseIconRequested { false };
public:
bool shouldStopThreadActivity() const;
private:
void iconDatabaseSyncThread();
void performOpenInitialization();
bool checkIntegrity();
void performURLImport();
void syncThreadMainLoop();
bool readFromDatabase();
bool writeToDatabase();
void pruneUnretainedIcons();
void checkForDanglingPageURLs(bool pruneIfFound);
void removeAllIconsOnThread();
void deleteAllPreparedStatements();
void* cleanupSyncThread();
void performRetainIconForPageURL(const String&, int retainCount);
void performReleaseIconForPageURL(const String&, int releaseCount);
bool m_initialPruningComplete { false };
void setIconURLForPageURLInSQLDatabase(const String&, const String&);
void setIconIDForPageURLInSQLDatabase(int64_t, const String&);
void removePageURLFromSQLDatabase(const String& pageURL);
int64_t getIconIDForIconURLFromSQLDatabase(const String& iconURL);
int64_t addIconURLToSQLDatabase(const String&);
RefPtr<WebCore::SharedBuffer> getImageDataForIconURLFromSQLDatabase(const String& iconURL);
void removeIconFromSQLDatabase(const String& iconURL);
void writeIconSnapshotToSQLDatabase(const IconSnapshot&);
void performPendingRetainAndReleaseOperations();
void dispatchDidImportIconURLForPageURLOnMainThread(const String&);
void dispatchDidImportIconDataForPageURLOnMainThread(const String&);
void dispatchDidFinishURLImportOnMainThread();
std::unique_ptr<IconDatabaseClient> m_client;
WebCore::SQLiteDatabase m_syncDB;
std::unique_ptr<WebCore::SQLiteStatement> m_setIconIDForPageURLStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_removePageURLStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_getIconIDForIconURLStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_getImageDataForIconURLStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_addIconToIconInfoStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_addIconToIconDataStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_getImageDataStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_deletePageURLsForIconURLStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_deleteIconFromIconInfoStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_deleteIconFromIconDataStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_updateIconInfoStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_updateIconDataStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_setIconInfoStatement;
std::unique_ptr<WebCore::SQLiteStatement> m_setIconDataStatement;
MainThreadNotifier m_mainThreadNotifier;
};
}