blob: babd8a9829fb80d19852ec974aeea70cee0b9158 [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008, 2009, 2014 Apple Inc. All rights reserved.
* Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
*
* 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. ``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
* 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 <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;
// This allows us to cache whether or not a SiteIcon has had its data set yet
// This helps the IconDatabase know if it has to set the data on a new object or not,
// and also to determine if the icon is missing data or if it just hasn't been brought
// in from the DB yet
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;
// Returns false if the page wasn't retained beforehand, true if the retain count was already 1 or higher.
bool retain(int count)
{
bool wasRetained = m_retainCount > 0;
m_retainCount += count;
return wasRetained;
}
// Returns true if the page is still retained after the call. False if the retain count just dropped to 0.
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;
};
// *** Main Thread Only ***
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 };
// *** Any Thread ***
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;
// Holding m_syncLock is required when accessing m_completeDatabasePath
String m_completeDatabasePath;
bool m_threadTerminationRequested { false };
bool m_removeIconsRequested { false };
bool m_iconURLImportComplete { false };
bool m_syncThreadHasWorkToDo { false };
Lock m_urlAndIconLock;
// Holding m_urlAndIconLock is required when accessing any of the following data structures or the objects they contain
HashMap<String, IconRecord*> m_iconURLToRecordMap;
HashMap<String, PageURLRecord*> m_pageURLToRecordMap;
HashSet<String> m_retainedPageURLs;
Lock m_pendingSyncLock;
// Holding m_pendingSyncLock is required when accessing any of the following data structures
HashMap<String, PageURLSnapshot> m_pageURLsPendingSync;
HashMap<String, IconSnapshot> m_iconsPendingSync;
Lock m_pendingReadingLock;
// Holding m_pendingSyncLock is required when accessing any of the following data structures - when dealing with IconRecord*s, holding m_urlAndIconLock is also required
HashSet<String> m_pageURLsPendingImport;
HashSet<String> m_pageURLsInterestedInIcons;
HashSet<IconRecord*> m_iconsPendingReading;
Lock m_urlsToRetainOrReleaseLock;
// Holding m_urlsToRetainOrReleaseLock is required when accessing any of the following data structures.
HashCountedSet<String> m_urlsToRetain;
HashCountedSet<String> m_urlsToRelease;
bool m_retainOrReleaseIconRequested { false };
// *** Sync Thread Only ***
public:
bool shouldStopThreadActivity() const;
private:
void iconDatabaseSyncThread();
// The following block of methods are called exclusively by the sync thread to manage i/o to and from the database
// Each method should periodically monitor m_threadTerminationRequested when it makes sense to return early on shutdown
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();
// Methods to dispatch client callbacks on the main thread
void dispatchDidImportIconURLForPageURLOnMainThread(const String&);
void dispatchDidImportIconDataForPageURLOnMainThread(const String&);
void dispatchDidFinishURLImportOnMainThread();
// The client is set by the main thread before the thread starts, and from then on is only used by the sync thread
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;
};
} // namespace WebKit