#include "config.h"
#include "IconDatabase.h"
#if ENABLE(ICONDATABASE)
#include "AutodrainedPool.h"
#include "DocumentLoader.h"
#include "FileSystem.h"
#include "IconDatabaseClient.h"
#include "IconRecord.h"
#include "IntSize.h"
#include "Logging.h"
#include "SQLiteStatement.h"
#include "SQLiteTransaction.h"
#include "SuddenTermination.h"
#include <wtf/CurrentTime.h>
#include <wtf/MainThread.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
#define ASSERT_NOT_SYNC_THREAD() ASSERT(!m_syncThreadRunning || !IS_ICON_SYNC_THREAD())
#define IS_ICON_SYNC_THREAD() (m_syncThread == currentThread())
#define ASSERT_ICON_SYNC_THREAD() ASSERT(IS_ICON_SYNC_THREAD())
#if PLATFORM(QT) || PLATFORM(GTK)
#define CAN_THEME_URL_ICON
#endif
namespace WebCore {
static int databaseCleanupCounter = 0;
static const int currentDatabaseVersion = 6;
static const int iconExpirationTime = 60*60*24*4;
static const int updateTimerDelay = 5;
static bool checkIntegrityOnOpen = false;
#ifndef NDEBUG
static String urlForLogging(const String& url)
{
static unsigned urlTruncationLength = 120;
if (url.length() < urlTruncationLength)
return url;
return url.substring(0, urlTruncationLength) + "...";
}
#endif
class DefaultIconDatabaseClient : public IconDatabaseClient {
public:
virtual bool performImport() { return true; }
virtual void didImportIconURLForPageURL(const String&) { }
virtual void didImportIconDataForPageURL(const String&) { }
virtual void didChangeIconForPageURL(const String&) { }
virtual void didRemoveAllIcons() { }
virtual void didFinishURLImport() { }
};
static IconDatabaseClient* defaultClient()
{
static IconDatabaseClient* defaultClient = new DefaultIconDatabaseClient();
return defaultClient;
}
static inline bool pageCanHaveIcon(const String& pageURL)
{
return protocolIsInHTTPFamily(pageURL);
}
void IconDatabase::setClient(IconDatabaseClient* client)
{
ASSERT(client);
ASSERT(!m_syncThreadRunning);
if (!client || m_syncThreadRunning)
return;
m_client = client;
}
bool IconDatabase::open(const String& directory, const String& filename)
{
ASSERT_NOT_SYNC_THREAD();
if (!m_isEnabled)
return false;
if (isOpen()) {
LOG_ERROR("Attempt to reopen the IconDatabase which is already open. Must close it first.");
return false;
}
m_databaseDirectory = directory.isolatedCopy();
m_completeDatabasePath = pathByAppendingComponent(m_databaseDirectory, filename);
m_syncLock.lock();
m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore: IconDatabase");
m_syncThreadRunning = m_syncThread;
m_syncLock.unlock();
if (!m_syncThread)
return false;
return true;
}
void IconDatabase::close()
{
ASSERT_NOT_SYNC_THREAD();
if (m_syncThreadRunning) {
m_threadTerminationRequested = true;
wakeSyncThread();
waitForThreadCompletion(m_syncThread, 0);
}
m_syncThreadRunning = false;
m_threadTerminationRequested = false;
m_removeIconsRequested = false;
m_syncDB.close();
ASSERT(!isOpen());
}
void IconDatabase::removeAllIcons()
{
ASSERT_NOT_SYNC_THREAD();
if (!isOpen())
return;
LOG(IconDatabase, "Requesting background thread to remove all icons");
{
MutexLocker locker(m_urlAndIconLock);
HashMap<String, PageURLRecord*>::iterator iter = m_pageURLToRecordMap.begin();
HashMap<String, PageURLRecord*>::iterator end = m_pageURLToRecordMap.end();
for (; iter != end; ++iter)
(*iter).second->setIconRecord(0);
m_iconURLToRecordMap.clear();
{
MutexLocker locker(m_pendingSyncLock);
m_pageURLsPendingSync.clear();
m_iconsPendingSync.clear();
}
{
MutexLocker locker(m_pendingReadingLock);
m_pageURLsPendingImport.clear();
m_pageURLsInterestedInIcons.clear();
m_iconsPendingReading.clear();
m_loadersPendingDecision.clear();
}
}
m_removeIconsRequested = true;
wakeSyncThread();
}
Image* IconDatabase::synchronousIconForPageURL(const String& pageURLOriginal, const IntSize& size)
{
ASSERT_NOT_SYNC_THREAD();
if (!isOpen() || !pageCanHaveIcon(pageURLOriginal))
return 0;
MutexLocker locker(m_urlAndIconLock);
String pageURLCopy;
PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
if (!pageRecord) {
pageURLCopy = pageURLOriginal.isolatedCopy();
pageRecord = getOrCreatePageURLRecord(pageURLCopy);
}
if (!pageRecord) {
MutexLocker locker(m_pendingReadingLock);
if (!m_iconURLImportComplete)
m_pageURLsInterestedInIcons.add(pageURLCopy);
return 0;
}
IconRecord* iconRecord = pageRecord->iconRecord();
if (!m_iconURLImportComplete && !iconRecord)
return 0;
ASSERT(iconRecord || m_retainedPageURLs.contains(pageURLOriginal));
if (!iconRecord)
return 0;
if (iconRecord->imageDataStatus() == ImageDataStatusUnknown) {
if (pageURLCopy.isNull())
pageURLCopy = pageURLOriginal.isolatedCopy();
MutexLocker locker(m_pendingReadingLock);
m_pageURLsInterestedInIcons.add(pageURLCopy);
m_iconsPendingReading.add(iconRecord);
wakeSyncThread();
return 0;
}
if (size == IntSize(0, 0))
return 0;
return iconRecord->image(size);
}
void IconDatabase::readIconForPageURLFromDisk(const String& pageURL)
{
synchronousIconForPageURL(pageURL, IntSize(0, 0));
}
String IconDatabase::synchronousIconURLForPageURL(const String& pageURLOriginal)
{
ASSERT_NOT_SYNC_THREAD();
if (!isOpen() || !pageCanHaveIcon(pageURLOriginal))
return String();
MutexLocker locker(m_urlAndIconLock);
PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
if (!pageRecord)
pageRecord = getOrCreatePageURLRecord(pageURLOriginal.isolatedCopy());
if (!pageRecord)
return String();
return pageRecord->iconRecord() ? pageRecord->iconRecord()->iconURL().isolatedCopy() : String();
}
#ifdef CAN_THEME_URL_ICON
static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord)
{
defaultIconRecord->loadImageFromResource("urlIcon");
}
#else
static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord)
{
static const unsigned char defaultIconData[] = { 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x03, 0x32, 0x80, 0x00, 0x20, 0x50, 0x38, 0x24, 0x16, 0x0D, 0x07, 0x84, 0x42, 0x61, 0x50, 0xB8,
0x64, 0x08, 0x18, 0x0D, 0x0A, 0x0B, 0x84, 0xA2, 0xA1, 0xE2, 0x08, 0x5E, 0x39, 0x28, 0xAF, 0x48, 0x24, 0xD3, 0x53, 0x9A, 0x37, 0x1D, 0x18, 0x0E, 0x8A, 0x4B, 0xD1, 0x38,
0xB0, 0x7C, 0x82, 0x07, 0x03, 0x82, 0xA2, 0xE8, 0x6C, 0x2C, 0x03, 0x2F, 0x02, 0x82, 0x41, 0xA1, 0xE2, 0xF8, 0xC8, 0x84, 0x68, 0x6D, 0x1C, 0x11, 0x0A, 0xB7, 0xFA, 0x91,
0x6E, 0xD1, 0x7F, 0xAF, 0x9A, 0x4E, 0x87, 0xFB, 0x19, 0xB0, 0xEA, 0x7F, 0xA4, 0x95, 0x8C, 0xB7, 0xF9, 0xA9, 0x0A, 0xA9, 0x7F, 0x8C, 0x88, 0x66, 0x96, 0xD4, 0xCA, 0x69,
0x2F, 0x00, 0x81, 0x65, 0xB0, 0x29, 0x90, 0x7C, 0xBA, 0x2B, 0x21, 0x1E, 0x5C, 0xE6, 0xB4, 0xBD, 0x31, 0xB6, 0xE7, 0x7A, 0xBF, 0xDD, 0x6F, 0x37, 0xD3, 0xFD, 0xD8, 0xF2,
0xB6, 0xDB, 0xED, 0xAC, 0xF7, 0x03, 0xC5, 0xFE, 0x77, 0x53, 0xB6, 0x1F, 0xE6, 0x24, 0x8B, 0x1D, 0xFE, 0x26, 0x20, 0x9E, 0x1C, 0xE0, 0x80, 0x65, 0x7A, 0x18, 0x02, 0x01,
0x82, 0xC5, 0xA0, 0xC0, 0xF1, 0x89, 0xBA, 0x23, 0x30, 0xAD, 0x1F, 0xE7, 0xE5, 0x5B, 0x6D, 0xFE, 0xE7, 0x78, 0x3E, 0x1F, 0xEE, 0x97, 0x8B, 0xE7, 0x37, 0x9D, 0xCF, 0xE7,
0x92, 0x8B, 0x87, 0x0B, 0xFC, 0xA0, 0x8E, 0x68, 0x3F, 0xC6, 0x27, 0xA6, 0x33, 0xFC, 0x36, 0x5B, 0x59, 0x3F, 0xC1, 0x02, 0x63, 0x3B, 0x74, 0x00, 0x03, 0x07, 0x0B, 0x61,
0x00, 0x20, 0x60, 0xC9, 0x08, 0x00, 0x1C, 0x25, 0x9F, 0xE0, 0x12, 0x8A, 0xD5, 0xFE, 0x6B, 0x4F, 0x35, 0x9F, 0xED, 0xD7, 0x4B, 0xD9, 0xFE, 0x8A, 0x59, 0xB8, 0x1F, 0xEC,
0x56, 0xD3, 0xC1, 0xFE, 0x63, 0x4D, 0xF2, 0x83, 0xC6, 0xB6, 0x1B, 0xFC, 0x34, 0x68, 0x61, 0x3F, 0xC1, 0xA6, 0x25, 0xEB, 0xFC, 0x06, 0x58, 0x5C, 0x3F, 0xC0, 0x03, 0xE4,
0xC3, 0xFC, 0x04, 0x0F, 0x1A, 0x6F, 0xE0, 0xE0, 0x20, 0xF9, 0x61, 0x7A, 0x02, 0x28, 0x2B, 0xBC, 0x46, 0x25, 0xF3, 0xFC, 0x66, 0x3D, 0x99, 0x27, 0xF9, 0x7E, 0x6B, 0x1D,
0xC7, 0xF9, 0x2C, 0x5E, 0x1C, 0x87, 0xF8, 0xC0, 0x4D, 0x9A, 0xE7, 0xF8, 0xDA, 0x51, 0xB2, 0xC1, 0x68, 0xF2, 0x64, 0x1F, 0xE1, 0x50, 0xED, 0x0A, 0x04, 0x23, 0x79, 0x8A,
0x7F, 0x82, 0xA3, 0x39, 0x80, 0x7F, 0x80, 0xC2, 0xB1, 0x5E, 0xF7, 0x04, 0x2F, 0xB2, 0x10, 0x02, 0x86, 0x63, 0xC9, 0xCC, 0x07, 0xBF, 0x87, 0xF8, 0x4A, 0x38, 0xAF, 0xC1,
0x88, 0xF8, 0x66, 0x1F, 0xE1, 0xD9, 0x08, 0xD4, 0x8F, 0x25, 0x5B, 0x4A, 0x49, 0x97, 0x87, 0x39, 0xFE, 0x25, 0x12, 0x10, 0x68, 0xAA, 0x4A, 0x2F, 0x42, 0x29, 0x12, 0x69,
0x9F, 0xE1, 0xC1, 0x00, 0x67, 0x1F, 0xE1, 0x58, 0xED, 0x00, 0x83, 0x23, 0x49, 0x82, 0x7F, 0x81, 0x21, 0xE0, 0xFC, 0x73, 0x21, 0x00, 0x50, 0x7D, 0x2B, 0x84, 0x03, 0x83,
0xC2, 0x1B, 0x90, 0x06, 0x69, 0xFE, 0x23, 0x91, 0xAE, 0x50, 0x9A, 0x49, 0x32, 0xC2, 0x89, 0x30, 0xE9, 0x0A, 0xC4, 0xD9, 0xC4, 0x7F, 0x94, 0xA6, 0x51, 0xDE, 0x7F, 0x9D,
0x07, 0x89, 0xF6, 0x7F, 0x91, 0x85, 0xCA, 0x88, 0x25, 0x11, 0xEE, 0x50, 0x7C, 0x43, 0x35, 0x21, 0x60, 0xF1, 0x0D, 0x82, 0x62, 0x39, 0x07, 0x2C, 0x20, 0xE0, 0x80, 0x72,
0x34, 0x17, 0xA1, 0x80, 0xEE, 0xF0, 0x89, 0x24, 0x74, 0x1A, 0x2C, 0x93, 0xB3, 0x78, 0xCC, 0x52, 0x9D, 0x6A, 0x69, 0x56, 0xBB, 0x0D, 0x85, 0x69, 0xE6, 0x7F, 0x9E, 0x27,
0xB9, 0xFD, 0x50, 0x54, 0x47, 0xF9, 0xCC, 0x78, 0x9F, 0x87, 0xF9, 0x98, 0x70, 0xB9, 0xC2, 0x91, 0x2C, 0x6D, 0x1F, 0xE1, 0xE1, 0x00, 0xBF, 0x02, 0xC1, 0xF5, 0x18, 0x84,
0x01, 0xE1, 0x48, 0x8C, 0x42, 0x07, 0x43, 0xC9, 0x76, 0x7F, 0x8B, 0x04, 0xE4, 0xDE, 0x35, 0x95, 0xAB, 0xB0, 0xF0, 0x5C, 0x55, 0x23, 0xF9, 0x7E, 0x7E, 0x9F, 0xE4, 0x0C,
0xA7, 0x55, 0x47, 0xC7, 0xF9, 0xE6, 0xCF, 0x1F, 0xE7, 0x93, 0x35, 0x52, 0x54, 0x63, 0x19, 0x46, 0x73, 0x1F, 0xE2, 0x61, 0x08, 0xF0, 0x82, 0xE1, 0x80, 0x92, 0xF9, 0x20,
0xC0, 0x28, 0x18, 0x0A, 0x05, 0xA1, 0xA2, 0xF8, 0x6E, 0xDB, 0x47, 0x49, 0xFE, 0x3E, 0x17, 0xB6, 0x61, 0x13, 0x1A, 0x29, 0x26, 0xA9, 0xFE, 0x7F, 0x92, 0x70, 0x69, 0xFE,
0x4C, 0x2F, 0x55, 0x01, 0xF1, 0x54, 0xD4, 0x35, 0x49, 0x4A, 0x69, 0x59, 0x83, 0x81, 0x58, 0x76, 0x9F, 0xE2, 0x20, 0xD6, 0x4C, 0x9B, 0xA0, 0x48, 0x1E, 0x0B, 0xB7, 0x48,
0x58, 0x26, 0x11, 0x06, 0x42, 0xE8, 0xA4, 0x40, 0x17, 0x27, 0x39, 0x00, 0x60, 0x2D, 0xA4, 0xC3, 0x2C, 0x7F, 0x94, 0x56, 0xE4, 0xE1, 0x77, 0x1F, 0xE5, 0xB9, 0xD7, 0x66,
0x1E, 0x07, 0xB3, 0x3C, 0x63, 0x1D, 0x35, 0x49, 0x0E, 0x63, 0x2D, 0xA2, 0xF1, 0x12, 0x60, 0x1C, 0xE0, 0xE0, 0x52, 0x1B, 0x8B, 0xAC, 0x38, 0x0E, 0x07, 0x03, 0x60, 0x28,
0x1C, 0x0E, 0x87, 0x00, 0xF0, 0x66, 0x27, 0x11, 0xA2, 0xC1, 0x02, 0x5A, 0x1C, 0xE4, 0x21, 0x83, 0x1F, 0x13, 0x86, 0xFA, 0xD2, 0x55, 0x1D, 0xD6, 0x61, 0xBC, 0x77, 0xD3,
0xE6, 0x91, 0xCB, 0x4C, 0x90, 0xA6, 0x25, 0xB8, 0x2F, 0x90, 0xC5, 0xA9, 0xCE, 0x12, 0x07, 0x02, 0x91, 0x1B, 0x9F, 0x68, 0x00, 0x16, 0x76, 0x0D, 0xA1, 0x00, 0x08, 0x06,
0x03, 0x81, 0xA0, 0x20, 0x1A, 0x0D, 0x06, 0x80, 0x30, 0x24, 0x12, 0x89, 0x20, 0x98, 0x4A, 0x1F, 0x0F, 0x21, 0xA0, 0x9E, 0x36, 0x16, 0xC2, 0x88, 0xE6, 0x48, 0x9B, 0x83,
0x31, 0x1C, 0x55, 0x1E, 0x43, 0x59, 0x1A, 0x56, 0x1E, 0x42, 0xF0, 0xFA, 0x4D, 0x1B, 0x9B, 0x08, 0xDC, 0x5B, 0x02, 0xA1, 0x30, 0x7E, 0x3C, 0xEE, 0x5B, 0xA6, 0xDD, 0xB8,
0x6D, 0x5B, 0x62, 0xB7, 0xCD, 0xF3, 0x9C, 0xEA, 0x04, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x01,
0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0xE0, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00,
0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x01, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x11, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x08, 0x01, 0x15, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x16, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x17,
0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x29, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xE8, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x03, 0xF0, 0x01, 0x1C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
0x00, 0x00, 0x01, 0x52, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0A,
0xFC, 0x80, 0x00, 0x00, 0x27, 0x10, 0x00, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10 };
DEFINE_STATIC_LOCAL(RefPtr<SharedBuffer>, defaultIconBuffer, (SharedBuffer::create(defaultIconData, sizeof(defaultIconData))));
defaultIconRecord->setImageData(defaultIconBuffer);
}
#endif
Image* IconDatabase::defaultIcon(const IntSize& size)
{
ASSERT_NOT_SYNC_THREAD();
if (!m_defaultIconRecord) {
m_defaultIconRecord = IconRecord::create("urlIcon");
loadDefaultIconRecord(m_defaultIconRecord.get());
}
return m_defaultIconRecord->image(size);
}
void IconDatabase::retainIconForPageURL(const String& pageURLOriginal)
{
ASSERT_NOT_SYNC_THREAD();
if (!isEnabled() || !pageCanHaveIcon(pageURLOriginal))
return;
MutexLocker locker(m_urlAndIconLock);
PageURLRecord* record = m_pageURLToRecordMap.get(pageURLOriginal);
String pageURL;
if (!record) {
pageURL = pageURLOriginal.isolatedCopy();
record = new PageURLRecord(pageURL);
m_pageURLToRecordMap.set(pageURL, record);
}
if (!record->retain()) {
if (pageURL.isNull())
pageURL = pageURLOriginal.isolatedCopy();
m_retainedPageURLs.add(pageURL);
if (!m_iconURLImportComplete)
return;
MutexLocker locker(m_pendingSyncLock);
if (!m_privateBrowsingEnabled && m_pageURLsPendingSync.contains(pageURL)) {
LOG(IconDatabase, "Bringing %s back from the brink", pageURL.ascii().data());
m_pageURLsPendingSync.set(pageURL, record->snapshot());
}
}
}
void IconDatabase::releaseIconForPageURL(const String& pageURLOriginal)
{
ASSERT_NOT_SYNC_THREAD();
if (!isEnabled() || !pageCanHaveIcon(pageURLOriginal))
return;
MutexLocker locker(m_urlAndIconLock);
if (!m_retainedPageURLs.contains(pageURLOriginal)) {
LOG_ERROR("Attempting to release icon for URL %s which is not retained", urlForLogging(pageURLOriginal).ascii().data());
return;
}
PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
ASSERT(pageRecord);
LOG(IconDatabase, "Releasing pageURL %s to a retain count of %i", urlForLogging(pageURLOriginal).ascii().data(), pageRecord->retainCount() - 1);
ASSERT(pageRecord->retainCount() > 0);
if (pageRecord->release())
return;
LOG(IconDatabase, "No more retainers for PageURL %s", urlForLogging(pageURLOriginal).ascii().data());
m_pageURLToRecordMap.remove(pageURLOriginal);
m_retainedPageURLs.remove(pageURLOriginal);
IconRecord* iconRecord = pageRecord->iconRecord();
ASSERT(!iconRecord || (iconRecord && m_iconURLToRecordMap.get(iconRecord->iconURL()) == iconRecord));
{
MutexLocker locker(m_pendingReadingLock);
if (!m_iconURLImportComplete)
m_pageURLsPendingImport.remove(pageURLOriginal);
m_pageURLsInterestedInIcons.remove(pageURLOriginal);
if (iconRecord && iconRecord->hasOneRef()) {
m_iconURLToRecordMap.remove(iconRecord->iconURL());
m_iconsPendingReading.remove(iconRecord);
}
}
if (!m_privateBrowsingEnabled) {
MutexLocker locker(m_pendingSyncLock);
m_pageURLsPendingSync.set(pageURLOriginal.isolatedCopy(), pageRecord->snapshot(true));
if (iconRecord && iconRecord->hasOneRef())
m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
}
delete pageRecord;
if (isOpen())
scheduleOrDeferSyncTimer();
}
void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, const String& iconURLOriginal)
{
ASSERT_NOT_SYNC_THREAD();
if (!isOpen() || iconURLOriginal.isEmpty())
return;
RefPtr<SharedBuffer> data = dataOriginal ? dataOriginal->copy() : PassRefPtr<SharedBuffer>(0);
if (data)
data->setMutexForVerifier(m_urlAndIconLock);
String iconURL = iconURLOriginal.isolatedCopy();
Vector<String> pageURLs;
{
MutexLocker locker(m_urlAndIconLock);
RefPtr<IconRecord> icon = m_iconURLToRecordMap.get(iconURL);
if (icon) {
MutexLocker locker(m_pendingReadingLock);
m_iconsPendingReading.remove(icon.get());
} else
icon = getOrCreateIconRecord(iconURL);
icon->setImageData(data.release());
icon->setTimestamp((int)currentTime());
pageURLs.appendRange(icon->retainingPageURLs().begin(), icon->retainingPageURLs().end());
if (!m_privateBrowsingEnabled) {
MutexLocker locker(m_pendingSyncLock);
m_iconsPendingSync.set(iconURL, icon->snapshot());
}
if (icon->hasOneRef()) {
ASSERT(icon->retainingPageURLs().isEmpty());
LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(icon->iconURL()).ascii().data());
m_iconURLToRecordMap.remove(icon->iconURL());
}
}
if (!IS_ICON_SYNC_THREAD()) {
scheduleOrDeferSyncTimer();
AutodrainedPool pool(25);
for (unsigned i = 0; i < pageURLs.size(); ++i) {
LOG(IconDatabase, "Dispatching notification that retaining pageURL %s has a new icon", urlForLogging(pageURLs[i]).ascii().data());
m_client->didChangeIconForPageURL(pageURLs[i]);
pool.cycle();
}
}
}
void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const String& pageURLOriginal)
{
ASSERT_NOT_SYNC_THREAD();
ASSERT(!iconURLOriginal.isEmpty());
if (!isOpen() || !pageCanHaveIcon(pageURLOriginal))
return;
String iconURL, pageURL;
{
MutexLocker locker(m_urlAndIconLock);
PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
if (pageRecord && pageRecord->iconRecord() && pageRecord->iconRecord()->iconURL() == iconURLOriginal)
return;
pageURL = pageURLOriginal.isolatedCopy();
iconURL = iconURLOriginal.isolatedCopy();
if (!pageRecord) {
pageRecord = new PageURLRecord(pageURL);
m_pageURLToRecordMap.set(pageURL, pageRecord);
}
RefPtr<IconRecord> iconRecord = pageRecord->iconRecord();
pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
if (iconRecord && iconRecord->hasOneRef()) {
ASSERT(iconRecord->retainingPageURLs().size() == 0);
LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(iconRecord->iconURL()).ascii().data());
m_iconURLToRecordMap.remove(iconRecord->iconURL());
MutexLocker locker(m_pendingReadingLock);
m_iconsPendingReading.remove(iconRecord.get());
}
if (!m_privateBrowsingEnabled) {
MutexLocker locker(m_pendingSyncLock);
m_pageURLsPendingSync.set(pageURL, pageRecord->snapshot());
if (iconRecord && iconRecord->hasOneRef())
m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
}
}
if (!IS_ICON_SYNC_THREAD()) {
scheduleOrDeferSyncTimer();
LOG(IconDatabase, "Dispatching notification that we changed an icon mapping for url %s", urlForLogging(pageURL).ascii().data());
AutodrainedPool pool;
m_client->didChangeIconForPageURL(pageURL);
}
}
IconLoadDecision IconDatabase::synchronousLoadDecisionForIconURL(const String& iconURL, DocumentLoader* notificationDocumentLoader)
{
ASSERT_NOT_SYNC_THREAD();
if (!isOpen() || iconURL.isEmpty())
return IconLoadNo;
{
MutexLocker locker(m_urlAndIconLock);
if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL)) {
LOG(IconDatabase, "Found expiration time on a present icon based on existing IconRecord");
return (int)currentTime() - icon->getTimestamp() > iconExpirationTime ? IconLoadYes : IconLoadNo;
}
}
MutexLocker readingLocker(m_pendingReadingLock);
if (m_iconURLImportComplete)
return IconLoadYes;
LOG(IconDatabase, "Don't know if we should load %s or not - adding %p to the set of document loaders waiting on a decision", iconURL.ascii().data(), notificationDocumentLoader);
if (notificationDocumentLoader)
m_loadersPendingDecision.add(notificationDocumentLoader);
return IconLoadUnknown;
}
bool IconDatabase::synchronousIconDataKnownForIconURL(const String& iconURL)
{
ASSERT_NOT_SYNC_THREAD();
MutexLocker locker(m_urlAndIconLock);
if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
return icon->imageDataStatus() != ImageDataStatusUnknown;
return false;
}
void IconDatabase::setEnabled(bool enabled)
{
ASSERT_NOT_SYNC_THREAD();
if (!enabled && isOpen())
close();
m_isEnabled = enabled;
}
bool IconDatabase::isEnabled() const
{
ASSERT_NOT_SYNC_THREAD();
return m_isEnabled;
}
void IconDatabase::setPrivateBrowsingEnabled(bool flag)
{
m_privateBrowsingEnabled = flag;
}
bool IconDatabase::isPrivateBrowsingEnabled() const
{
return m_privateBrowsingEnabled;
}
void IconDatabase::delayDatabaseCleanup()
{
++databaseCleanupCounter;
if (databaseCleanupCounter == 1)
LOG(IconDatabase, "Database cleanup is now DISABLED");
}
void IconDatabase::allowDatabaseCleanup()
{
if (--databaseCleanupCounter < 0)
databaseCleanupCounter = 0;
if (databaseCleanupCounter == 0)
LOG(IconDatabase, "Database cleanup is now ENABLED");
}
void IconDatabase::checkIntegrityBeforeOpening()
{
checkIntegrityOnOpen = true;
}
size_t IconDatabase::pageURLMappingCount()
{
MutexLocker locker(m_urlAndIconLock);
return m_pageURLToRecordMap.size();
}
size_t IconDatabase::retainedPageURLCount()
{
MutexLocker locker(m_urlAndIconLock);
return m_retainedPageURLs.size();
}
size_t IconDatabase::iconRecordCount()
{
MutexLocker locker(m_urlAndIconLock);
return m_iconURLToRecordMap.size();
}
size_t IconDatabase::iconRecordCountWithData()
{
MutexLocker locker(m_urlAndIconLock);
size_t result = 0;
HashMap<String, IconRecord*>::iterator i = m_iconURLToRecordMap.begin();
HashMap<String, IconRecord*>::iterator end = m_iconURLToRecordMap.end();
for (; i != end; ++i)
result += ((*i).second->imageDataStatus() == ImageDataStatusPresent);
return result;
}
IconDatabase::IconDatabase()
: m_syncTimer(this, &IconDatabase::syncTimerFired)
, m_syncThreadRunning(false)
, m_isEnabled(false)
, m_privateBrowsingEnabled(false)
, m_threadTerminationRequested(false)
, m_removeIconsRequested(false)
, m_iconURLImportComplete(false)
, m_disabledSuddenTerminationForSyncThread(false)
, m_initialPruningComplete(false)
, m_client(defaultClient())
, m_imported(false)
, m_isImportedSet(false)
{
LOG(IconDatabase, "Creating IconDatabase %p", this);
ASSERT(isMainThread());
}
IconDatabase::~IconDatabase()
{
ASSERT_NOT_REACHED();
}
void IconDatabase::notifyPendingLoadDecisionsOnMainThread(void* context)
{
static_cast<IconDatabase*>(context)->notifyPendingLoadDecisions();
}
void IconDatabase::notifyPendingLoadDecisions()
{
ASSERT_NOT_SYNC_THREAD();
ASSERT(m_iconURLImportComplete);
LOG(IconDatabase, "Notifying all DocumentLoaders that were waiting on a load decision for thier icons");
HashSet<RefPtr<DocumentLoader> >::iterator i = m_loadersPendingDecision.begin();
HashSet<RefPtr<DocumentLoader> >::iterator end = m_loadersPendingDecision.end();
for (; i != end; ++i)
if ((*i)->refCount() > 1)
(*i)->iconLoadDecisionAvailable();
m_loadersPendingDecision.clear();
}
void IconDatabase::wakeSyncThread()
{
MutexLocker locker(m_syncLock);
if (!m_disabledSuddenTerminationForSyncThread) {
m_disabledSuddenTerminationForSyncThread = true;
disableSuddenTermination();
}
m_syncCondition.signal();
}
void IconDatabase::scheduleOrDeferSyncTimer()
{
ASSERT_NOT_SYNC_THREAD();
if (!m_syncTimer.isActive()) {
disableSuddenTermination();
}
m_syncTimer.startOneShot(updateTimerDelay);
}
void IconDatabase::syncTimerFired(Timer<IconDatabase>*)
{
ASSERT_NOT_SYNC_THREAD();
wakeSyncThread();
enableSuddenTermination();
}
bool IconDatabase::isOpen() const
{
MutexLocker locker(m_syncLock);
return m_syncDB.isOpen();
}
String IconDatabase::databasePath() const
{
MutexLocker locker(m_syncLock);
return m_completeDatabasePath.isolatedCopy();
}
String IconDatabase::defaultDatabaseFilename()
{
DEFINE_STATIC_LOCAL(String, defaultDatabaseFilename, ("WebpageIcons.db"));
return defaultDatabaseFilename.isolatedCopy();
}
PassRefPtr<IconRecord> IconDatabase::getOrCreateIconRecord(const String& iconURL)
{
ASSERT(!m_urlAndIconLock.tryLock());
if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
return icon;
RefPtr<IconRecord> newIcon = IconRecord::create(iconURL);
m_iconURLToRecordMap.set(iconURL, newIcon.get());
return newIcon.release();
}
PageURLRecord* IconDatabase::getOrCreatePageURLRecord(const String& pageURL)
{
ASSERT(!m_urlAndIconLock.tryLock());
if (!pageCanHaveIcon(pageURL))
return 0;
PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
MutexLocker locker(m_pendingReadingLock);
if (!m_iconURLImportComplete) {
if (!pageRecord) {
LOG(IconDatabase, "Creating new PageURLRecord for pageURL %s", urlForLogging(pageURL).ascii().data());
pageRecord = new PageURLRecord(pageURL);
m_pageURLToRecordMap.set(pageURL, pageRecord);
}
if (!pageRecord->iconRecord()) {
m_pageURLsPendingImport.add(pageURL);
return 0;
}
}
return pageRecord;
}
void IconDatabase::importIconURLForPageURL(const String& iconURL, const String& pageURL)
{
ASSERT_ICON_SYNC_THREAD();
ASSERT(!iconURL.isEmpty());
ASSERT(!pageURL.isEmpty());
ASSERT(pageCanHaveIcon(pageURL));
setIconURLForPageURLInSQLDatabase(iconURL, pageURL);
}
void IconDatabase::importIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String& iconURL)
{
ASSERT_ICON_SYNC_THREAD();
ASSERT(!iconURL.isEmpty());
writeIconSnapshotToSQLDatabase(IconSnapshot(iconURL, (int)currentTime(), data.get()));
}
bool IconDatabase::shouldStopThreadActivity() const
{
ASSERT_ICON_SYNC_THREAD();
return m_threadTerminationRequested || m_removeIconsRequested;
}
void* IconDatabase::iconDatabaseSyncThreadStart(void* vIconDatabase)
{
IconDatabase* iconDB = static_cast<IconDatabase*>(vIconDatabase);
return iconDB->iconDatabaseSyncThread();
}
void* IconDatabase::iconDatabaseSyncThread()
{
m_syncLock.lock();
m_syncLock.unlock();
ASSERT_ICON_SYNC_THREAD();
LOG(IconDatabase, "(THREAD) IconDatabase sync thread started");
#ifndef NDEBUG
double startTime = currentTime();
#endif
makeAllDirectories(m_databaseDirectory);
String journalFilename = m_completeDatabasePath + "-journal";
if (!checkIntegrityOnOpen) {
AutodrainedPool pool;
checkIntegrityOnOpen = fileExists(journalFilename);
}
{
MutexLocker locker(m_syncLock);
if (!m_syncDB.open(m_completeDatabasePath)) {
LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg());
return 0;
}
}
if (shouldStopThreadActivity())
return syncThreadMainLoop();
#ifndef NDEBUG
double timeStamp = currentTime();
LOG(IconDatabase, "(THREAD) Open took %.4f seconds", timeStamp - startTime);
#endif
performOpenInitialization();
if (shouldStopThreadActivity())
return syncThreadMainLoop();
#ifndef NDEBUG
double newStamp = currentTime();
LOG(IconDatabase, "(THREAD) performOpenInitialization() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
timeStamp = newStamp;
#endif
if (!imported()) {
LOG(IconDatabase, "(THREAD) Performing Safari2 import procedure");
SQLiteTransaction importTransaction(m_syncDB);
importTransaction.begin();
if (m_client->performImport()) {
setImported(true);
importTransaction.commit();
} else {
LOG(IconDatabase, "(THREAD) Safari 2 import was cancelled");
importTransaction.rollback();
}
if (shouldStopThreadActivity())
return syncThreadMainLoop();
#ifndef NDEBUG
newStamp = currentTime();
LOG(IconDatabase, "(THREAD) performImport() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
timeStamp = newStamp;
#endif
}
LOG(IconDatabase, "(THREAD) Starting iconURL import");
performURLImport();
if (shouldStopThreadActivity())
return syncThreadMainLoop();
#ifndef NDEBUG
newStamp = currentTime();
LOG(IconDatabase, "(THREAD) performURLImport() took %.4f seconds. Entering main loop %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
#endif
LOG(IconDatabase, "(THREAD) Beginning sync");
return syncThreadMainLoop();
}
static int databaseVersionNumber(SQLiteDatabase& db)
{
return SQLiteStatement(db, "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';").getColumnInt(0);
}
static bool isValidDatabase(SQLiteDatabase& db)
{
if (!db.tableExists("IconInfo") || !db.tableExists("IconData") || !db.tableExists("PageURL") || !db.tableExists("IconDatabaseInfo"))
return false;
if (databaseVersionNumber(db) < currentDatabaseVersion) {
LOG(IconDatabase, "DB version is not found or below expected valid version");
return false;
}
return true;
}
static void createDatabaseTables(SQLiteDatabase& db)
{
if (!db.executeCommand("CREATE TABLE PageURL (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID INTEGER NOT NULL ON CONFLICT FAIL);")) {
LOG_ERROR("Could not create PageURL table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
db.close();
return;
}
if (!db.executeCommand("CREATE INDEX PageURLIndex ON PageURL (url);")) {
LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
db.close();
return;
}
if (!db.executeCommand("CREATE TABLE IconInfo (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, stamp INTEGER);")) {
LOG_ERROR("Could not create IconInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
db.close();
return;
}
if (!db.executeCommand("CREATE INDEX IconInfoIndex ON IconInfo (url, iconID);")) {
LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
db.close();
return;
}
if (!db.executeCommand("CREATE TABLE IconData (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, data BLOB);")) {
LOG_ERROR("Could not create IconData table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
db.close();
return;
}
if (!db.executeCommand("CREATE INDEX IconDataIndex ON IconData (iconID);")) {
LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
db.close();
return;
}
if (!db.executeCommand("CREATE TABLE IconDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
LOG_ERROR("Could not create IconDatabaseInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
db.close();
return;
}
if (!db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) {
LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", db.lastError(), db.lastErrorMsg());
db.close();
return;
}
}
void IconDatabase::performOpenInitialization()
{
ASSERT_ICON_SYNC_THREAD();
if (!isOpen())
return;
if (checkIntegrityOnOpen) {
checkIntegrityOnOpen = false;
if (!checkIntegrity()) {
LOG(IconDatabase, "Integrity check was bad - dumping IconDatabase");
m_syncDB.close();
{
MutexLocker locker(m_syncLock);
deleteFile(m_completeDatabasePath + "-journal");
deleteFile(m_completeDatabasePath);
}
if (!m_syncDB.open(m_completeDatabasePath)) {
LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg());
return;
}
}
}
int version = databaseVersionNumber(m_syncDB);
if (version > currentDatabaseVersion) {
LOG(IconDatabase, "Database version number %i is greater than our current version number %i - closing the database to prevent overwriting newer versions", version, currentDatabaseVersion);
m_syncDB.close();
m_threadTerminationRequested = true;
return;
}
if (!isValidDatabase(m_syncDB)) {
LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", m_completeDatabasePath.ascii().data());
m_syncDB.clearAllTables();
createDatabaseTables(m_syncDB);
}
if (!SQLiteStatement(m_syncDB, "PRAGMA cache_size = 200;").executeCommand())
LOG_ERROR("SQLite database could not set cache_size");
if (canExcludeFromBackup() && !wasExcludedFromBackup() && excludeFromBackup(m_completeDatabasePath))
setWasExcludedFromBackup();
}
bool IconDatabase::checkIntegrity()
{
ASSERT_ICON_SYNC_THREAD();
SQLiteStatement integrity(m_syncDB, "PRAGMA integrity_check;");
if (integrity.prepare() != SQLResultOk) {
LOG_ERROR("checkIntegrity failed to execute");
return false;
}
int resultCode = integrity.step();
if (resultCode == SQLResultOk)
return true;
if (resultCode != SQLResultRow)
return false;
int columns = integrity.columnCount();
if (columns != 1) {
LOG_ERROR("Received %i columns performing integrity check, should be 1", columns);
return false;
}
String resultText = integrity.getColumnText(0);
if (resultText == "ok")
return true;
LOG_ERROR("Icon database integrity check failed - \n%s", resultText.ascii().data());
return false;
}
void IconDatabase::performURLImport()
{
ASSERT_ICON_SYNC_THREAD();
SQLiteStatement query(m_syncDB, "SELECT PageURL.url, IconInfo.url, IconInfo.stamp FROM PageURL INNER JOIN IconInfo ON PageURL.iconID=IconInfo.iconID;");
if (query.prepare() != SQLResultOk) {
LOG_ERROR("Unable to prepare icon url import query");
return;
}
AutodrainedPool pool(25);
int result = query.step();
while (result == SQLResultRow) {
String pageURL = query.getColumnText(0);
String iconURL = query.getColumnText(1);
{
MutexLocker locker(m_urlAndIconLock);
PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
if (!pageRecord && databaseCleanupCounter && pageCanHaveIcon(pageURL)) {
pageRecord = new PageURLRecord(pageURL);
m_pageURLToRecordMap.set(pageURL, pageRecord);
}
if (pageRecord) {
IconRecord* currentIcon = pageRecord->iconRecord();
if (!currentIcon || currentIcon->iconURL() != iconURL) {
pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
currentIcon = pageRecord->iconRecord();
}
currentIcon->setTimestamp(query.getColumnInt(2));
}
}
{
MutexLocker locker(m_pendingReadingLock);
if (m_pageURLsPendingImport.contains(pageURL)) {
dispatchDidImportIconURLForPageURLOnMainThread(pageURL);
m_pageURLsPendingImport.remove(pageURL);
pool.cycle();
}
}
if (shouldStopThreadActivity()) {
LOG(IconDatabase, "IconDatabase asked to terminate during performURLImport()");
return;
}
result = query.step();
}
if (result != SQLResultDone)
LOG(IconDatabase, "Error reading page->icon url mappings from database");
Vector<String> urls;
{
MutexLocker locker(m_pendingReadingLock);
urls.appendRange(m_pageURLsPendingImport.begin(), m_pageURLsPendingImport.end());
m_pageURLsPendingImport.clear();
m_iconURLImportComplete = true;
}
Vector<String> urlsToNotify;
{
MutexLocker locker(m_urlAndIconLock);
for (unsigned i = 0; i < urls.size(); ++i) {
if (!m_retainedPageURLs.contains(urls[i])) {
PageURLRecord* record = m_pageURLToRecordMap.get(urls[i]);
if (record && !databaseCleanupCounter) {
m_pageURLToRecordMap.remove(urls[i]);
IconRecord* iconRecord = record->iconRecord();
if (iconRecord && iconRecord->hasOneRef()) {
m_iconURLToRecordMap.remove(iconRecord->iconURL());
{
MutexLocker locker(m_pendingReadingLock);
m_pageURLsInterestedInIcons.remove(urls[i]);
m_iconsPendingReading.remove(iconRecord);
}
{
MutexLocker locker(m_pendingSyncLock);
m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
}
}
delete record;
}
} else {
urlsToNotify.append(urls[i]);
}
}
}
LOG(IconDatabase, "Notifying %lu interested page URLs that their icon URL is known due to the import", static_cast<unsigned long>(urlsToNotify.size()));
for (unsigned i = 0; i < urlsToNotify.size(); ++i) {
LOG(IconDatabase, "Notifying icon info known for pageURL %s", urlsToNotify[i].ascii().data());
dispatchDidImportIconURLForPageURLOnMainThread(urlsToNotify[i]);
if (shouldStopThreadActivity())
return;
pool.cycle();
}
dispatchDidFinishURLImportOnMainThread();
callOnMainThread(notifyPendingLoadDecisionsOnMainThread, this);
}
void* IconDatabase::syncThreadMainLoop()
{
ASSERT_ICON_SYNC_THREAD();
bool shouldReenableSuddenTermination = false;
m_syncLock.lock();
while (!m_threadTerminationRequested) {
m_syncLock.unlock();
#ifndef NDEBUG
double timeStamp = currentTime();
#endif
LOG(IconDatabase, "(THREAD) Main work loop starting");
if (m_removeIconsRequested) {
removeAllIconsOnThread();
m_removeIconsRequested = false;
}
if (m_threadTerminationRequested)
break;
bool didAnyWork = true;
while (didAnyWork) {
bool didWrite = writeToDatabase();
if (shouldStopThreadActivity())
break;
didAnyWork = readFromDatabase();
if (shouldStopThreadActivity())
break;
static bool prunedUnretainedIcons = false;
if (didWrite && !m_privateBrowsingEnabled && !prunedUnretainedIcons && !databaseCleanupCounter) {
#ifndef NDEBUG
double time = currentTime();
#endif
LOG(IconDatabase, "(THREAD) Starting pruneUnretainedIcons()");
pruneUnretainedIcons();
LOG(IconDatabase, "(THREAD) pruneUnretainedIcons() took %.4f seconds", currentTime() - time);
prunedUnretainedIcons = true;
}
didAnyWork = didAnyWork || didWrite;
if (shouldStopThreadActivity())
break;
}
#ifndef NDEBUG
double newstamp = currentTime();
LOG(IconDatabase, "(THREAD) Main work loop ran for %.4f seconds, %s requested to terminate", newstamp - timeStamp, shouldStopThreadActivity() ? "was" : "was not");
#endif
m_syncLock.lock();
if (shouldStopThreadActivity())
continue;
if (shouldReenableSuddenTermination) {
ASSERT(m_disabledSuddenTerminationForSyncThread);
enableSuddenTermination();
m_disabledSuddenTerminationForSyncThread = false;
}
m_syncCondition.wait(m_syncLock);
shouldReenableSuddenTermination = true;
}
m_syncLock.unlock();
cleanupSyncThread();
if (shouldReenableSuddenTermination) {
ASSERT(m_disabledSuddenTerminationForSyncThread);
enableSuddenTermination();
m_disabledSuddenTerminationForSyncThread = false;
}
return 0;
}
bool IconDatabase::readFromDatabase()
{
ASSERT_ICON_SYNC_THREAD();
#ifndef NDEBUG
double timeStamp = currentTime();
#endif
bool didAnyWork = false;
Vector<IconRecord*> icons;
{
MutexLocker locker(m_pendingReadingLock);
icons.appendRange(m_iconsPendingReading.begin(), m_iconsPendingReading.end());
}
HashSet<String> urlsToNotify;
for (unsigned i = 0; i < icons.size(); ++i) {
didAnyWork = true;
RefPtr<SharedBuffer> imageData = getImageDataForIconURLFromSQLDatabase(icons[i]->iconURL());
{
MutexLocker urlLocker(m_urlAndIconLock);
{
MutexLocker readLocker(m_pendingReadingLock);
if (m_iconsPendingReading.contains(icons[i])) {
icons[i]->setImageData(imageData.release());
m_iconsPendingReading.remove(icons[i]);
const HashSet<String>* outerHash;
const HashSet<String>* innerHash;
if (icons[i]->retainingPageURLs().size() > m_pageURLsInterestedInIcons.size()) {
outerHash = &m_pageURLsInterestedInIcons;
innerHash = &(icons[i]->retainingPageURLs());
} else {
innerHash = &m_pageURLsInterestedInIcons;
outerHash = &(icons[i]->retainingPageURLs());
}
HashSet<String>::const_iterator iter = outerHash->begin();
HashSet<String>::const_iterator end = outerHash->end();
for (; iter != end; ++iter) {
if (innerHash->contains(*iter)) {
LOG(IconDatabase, "%s is interesting in the icon we just read. Adding it to the list and removing it from the interested set", urlForLogging(*iter).ascii().data());
urlsToNotify.add(*iter);
}
if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size())
break;
}
if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size())
m_pageURLsInterestedInIcons.clear();
else {
iter = urlsToNotify.begin();
end = urlsToNotify.end();
for (; iter != end; ++iter)
m_pageURLsInterestedInIcons.remove(*iter);
}
}
}
}
if (shouldStopThreadActivity())
return didAnyWork;
AutodrainedPool pool(25);
HashSet<String>::iterator iter = urlsToNotify.begin();
HashSet<String>::iterator end = urlsToNotify.end();
for (unsigned iteration = 0; iter != end; ++iter, ++iteration) {
LOG(IconDatabase, "Notifying icon received for pageURL %s", urlForLogging(*iter).ascii().data());
dispatchDidImportIconDataForPageURLOnMainThread(*iter);
if (shouldStopThreadActivity())
return didAnyWork;
pool.cycle();
}
LOG(IconDatabase, "Done notifying %i pageURLs who just received their icons", urlsToNotify.size());
urlsToNotify.clear();
if (shouldStopThreadActivity())
return didAnyWork;
}
LOG(IconDatabase, "Reading from database took %.4f seconds", currentTime() - timeStamp);
return didAnyWork;
}
bool IconDatabase::writeToDatabase()
{
ASSERT_ICON_SYNC_THREAD();
#ifndef NDEBUG
double timeStamp = currentTime();
#endif
bool didAnyWork = false;
{
MutexLocker locker(m_urlAndIconLock);
Vector<IconSnapshot> iconSnapshots;
Vector<PageURLSnapshot> pageSnapshots;
{
MutexLocker locker(m_pendingSyncLock);
iconSnapshots.appendRange(m_iconsPendingSync.begin().values(), m_iconsPendingSync.end().values());
m_iconsPendingSync.clear();
pageSnapshots.appendRange(m_pageURLsPendingSync.begin().values(), m_pageURLsPendingSync.end().values());
m_pageURLsPendingSync.clear();
}
if (iconSnapshots.size() || pageSnapshots.size())
didAnyWork = true;
SQLiteTransaction syncTransaction(m_syncDB);
syncTransaction.begin();
for (unsigned i = 0; i < iconSnapshots.size(); ++i) {
writeIconSnapshotToSQLDatabase(iconSnapshots[i]);
LOG(IconDatabase, "Wrote IconRecord for IconURL %s with timeStamp of %i to the DB", urlForLogging(iconSnapshots[i].iconURL()).ascii().data(), iconSnapshots[i].timestamp());
}
for (unsigned i = 0; i < pageSnapshots.size(); ++i) {
if (pageSnapshots[i].iconURL().isEmpty())
removePageURLFromSQLDatabase(pageSnapshots[i].pageURL());
else
setIconURLForPageURLInSQLDatabase(pageSnapshots[i].iconURL(), pageSnapshots[i].pageURL());
LOG(IconDatabase, "Committed IconURL for PageURL %s to database", urlForLogging(pageSnapshots[i].pageURL()).ascii().data());
}
syncTransaction.commit();
}
if (didAnyWork)
checkForDanglingPageURLs(false);
LOG(IconDatabase, "Updating the database took %.4f seconds", currentTime() - timeStamp);
return didAnyWork;
}
void IconDatabase::pruneUnretainedIcons()
{
ASSERT_ICON_SYNC_THREAD();
if (!isOpen())
return;
ASSERT(!m_initialPruningComplete);
ASSERT(m_iconURLImportComplete);
Vector<int64_t> pageIDsToDelete;
SQLiteStatement pageSQL(m_syncDB, "SELECT rowid, url FROM PageURL;");
pageSQL.prepare();
int result;
while ((result = pageSQL.step()) == SQLResultRow) {
MutexLocker locker(m_urlAndIconLock);
if (!m_pageURLToRecordMap.contains(pageSQL.getColumnText(1)))
pageIDsToDelete.append(pageSQL.getColumnInt64(0));
}
if (result != SQLResultDone)
LOG_ERROR("Error reading PageURL table from on-disk DB");
pageSQL.finalize();
size_t numToDelete = pageIDsToDelete.size();
if (numToDelete) {
SQLiteTransaction pruningTransaction(m_syncDB);
pruningTransaction.begin();
SQLiteStatement pageDeleteSQL(m_syncDB, "DELETE FROM PageURL WHERE rowid = (?);");
pageDeleteSQL.prepare();
for (size_t i = 0; i < numToDelete; ++i) {
#if OS(WINDOWS)
LOG(IconDatabase, "Pruning page with rowid %I64i from disk", static_cast<long long>(pageIDsToDelete[i]));
#else
LOG(IconDatabase, "Pruning page with rowid %lli from disk", static_cast<long long>(pageIDsToDelete[i]));
#endif
pageDeleteSQL.bindInt64(1, pageIDsToDelete[i]);
int result = pageDeleteSQL.step();
if (result != SQLResultDone)
#if OS(WINDOWS)
LOG_ERROR("Unabled to delete page with id %I64i from disk", static_cast<long long>(pageIDsToDelete[i]));
#else
LOG_ERROR("Unabled to delete page with id %lli from disk", static_cast<long long>(pageIDsToDelete[i]));
#endif
pageDeleteSQL.reset();
if (shouldStopThreadActivity()) {
pruningTransaction.commit();
return;
}
}
pruningTransaction.commit();
pageDeleteSQL.finalize();
}
SQLiteTransaction pruningTransaction(m_syncDB);
pruningTransaction.begin();
if (!m_syncDB.executeCommand("DELETE FROM IconData WHERE iconID NOT IN (SELECT iconID FROM PageURL);"))
LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk IconData table");
if (!m_syncDB.executeCommand("DELETE FROM IconInfo WHERE iconID NOT IN (SELECT iconID FROM PageURL);"))
LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk IconInfo table");
pruningTransaction.commit();
checkForDanglingPageURLs(true);
m_initialPruningComplete = true;
}
void IconDatabase::checkForDanglingPageURLs(bool pruneIfFound)
{
ASSERT_ICON_SYNC_THREAD();
#ifndef NDEBUG
static bool danglersFound = true;
#else
static bool danglersFound = false;
#endif
if ((pruneIfFound || !danglersFound) && SQLiteStatement(m_syncDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM IconInfo) LIMIT 1;").returnsAtLeastOneResult()) {
danglersFound = true;
LOG(IconDatabase, "Dangling PageURL entries found");
if (pruneIfFound && !m_syncDB.executeCommand("DELETE FROM PageURL WHERE iconID NOT IN (SELECT iconID FROM IconInfo);"))
LOG(IconDatabase, "Unable to prune dangling PageURLs");
}
}
void IconDatabase::removeAllIconsOnThread()
{
ASSERT_ICON_SYNC_THREAD();
LOG(IconDatabase, "Removing all icons on the sync thread");
deleteAllPreparedStatements();
m_syncDB.clearAllTables();
m_syncDB.runVacuumCommand();
createDatabaseTables(m_syncDB);
LOG(IconDatabase, "Dispatching notification that we removed all icons");
dispatchDidRemoveAllIconsOnMainThread();
}
void IconDatabase::deleteAllPreparedStatements()
{
ASSERT_ICON_SYNC_THREAD();
m_setIconIDForPageURLStatement.clear();
m_removePageURLStatement.clear();
m_getIconIDForIconURLStatement.clear();
m_getImageDataForIconURLStatement.clear();
m_addIconToIconInfoStatement.clear();
m_addIconToIconDataStatement.clear();
m_getImageDataStatement.clear();
m_deletePageURLsForIconURLStatement.clear();
m_deleteIconFromIconInfoStatement.clear();
m_deleteIconFromIconDataStatement.clear();
m_updateIconInfoStatement.clear();
m_updateIconDataStatement.clear();
m_setIconInfoStatement.clear();
m_setIconDataStatement.clear();
}
void* IconDatabase::cleanupSyncThread()
{
ASSERT_ICON_SYNC_THREAD();
#ifndef NDEBUG
double timeStamp = currentTime();
#endif
if (m_removeIconsRequested)
removeAllIconsOnThread();
LOG(IconDatabase, "(THREAD) Doing final writeout and closure of sync thread");
writeToDatabase();
MutexLocker locker(m_syncLock);
m_databaseDirectory = String();
m_completeDatabasePath = String();
deleteAllPreparedStatements();
m_syncDB.close();
#ifndef NDEBUG
LOG(IconDatabase, "(THREAD) Final closure took %.4f seconds", currentTime() - timeStamp);
#endif
m_syncThreadRunning = false;
return 0;
}
bool IconDatabase::imported()
{
ASSERT_ICON_SYNC_THREAD();
if (m_isImportedSet)
return m_imported;
SQLiteStatement query(m_syncDB, "SELECT IconDatabaseInfo.value FROM IconDatabaseInfo WHERE IconDatabaseInfo.key = \"ImportedSafari2Icons\";");
if (query.prepare() != SQLResultOk) {
LOG_ERROR("Unable to prepare imported statement");
return false;
}
int result = query.step();
if (result == SQLResultRow)
result = query.getColumnInt(0);
else {
if (result != SQLResultDone)
LOG_ERROR("imported statement failed");
result = 0;
}
m_isImportedSet = true;
return m_imported = result;
}
void IconDatabase::setImported(bool import)
{
ASSERT_ICON_SYNC_THREAD();
m_imported = import;
m_isImportedSet = true;
String queryString = import ?
"INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 1);" :
"INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 0);";
SQLiteStatement query(m_syncDB, queryString);
if (query.prepare() != SQLResultOk) {
LOG_ERROR("Unable to prepare set imported statement");
return;
}
if (query.step() != SQLResultDone)
LOG_ERROR("set imported statement failed");
}
inline void readySQLiteStatement(OwnPtr<SQLiteStatement>& statement, SQLiteDatabase& db, const String& str)
{
if (statement && (statement->database() != &db || statement->isExpired())) {
if (statement->isExpired())
LOG(IconDatabase, "SQLiteStatement associated with %s is expired", str.ascii().data());
statement.clear();
}
if (!statement) {
statement = adoptPtr(new SQLiteStatement(db, str));
if (statement->prepare() != SQLResultOk)
LOG_ERROR("Preparing statement %s failed", str.ascii().data());
}
}
void IconDatabase::setIconURLForPageURLInSQLDatabase(const String& iconURL, const String& pageURL)
{
ASSERT_ICON_SYNC_THREAD();
int64_t iconID = getIconIDForIconURLFromSQLDatabase(iconURL);
if (!iconID)
iconID = addIconURLToSQLDatabase(iconURL);
if (!iconID) {
LOG_ERROR("Failed to establish an ID for iconURL %s", urlForLogging(iconURL).ascii().data());
ASSERT(false);
return;
}
setIconIDForPageURLInSQLDatabase(iconID, pageURL);
}
void IconDatabase::setIconIDForPageURLInSQLDatabase(int64_t iconID, const String& pageURL)
{
ASSERT_ICON_SYNC_THREAD();
readySQLiteStatement(m_setIconIDForPageURLStatement, m_syncDB, "INSERT INTO PageURL (url, iconID) VALUES ((?), ?);");
m_setIconIDForPageURLStatement->bindText(1, pageURL);
m_setIconIDForPageURLStatement->bindInt64(2, iconID);
int result = m_setIconIDForPageURLStatement->step();
if (result != SQLResultDone) {
ASSERT(false);
LOG_ERROR("setIconIDForPageURLQuery failed for url %s", urlForLogging(pageURL).ascii().data());
}
m_setIconIDForPageURLStatement->reset();
}
void IconDatabase::removePageURLFromSQLDatabase(const String& pageURL)
{
ASSERT_ICON_SYNC_THREAD();
readySQLiteStatement(m_removePageURLStatement, m_syncDB, "DELETE FROM PageURL WHERE url = (?);");
m_removePageURLStatement->bindText(1, pageURL);
if (m_removePageURLStatement->step() != SQLResultDone)
LOG_ERROR("removePageURLFromSQLDatabase failed for url %s", urlForLogging(pageURL).ascii().data());
m_removePageURLStatement->reset();
}
int64_t IconDatabase::getIconIDForIconURLFromSQLDatabase(const String& iconURL)
{
ASSERT_ICON_SYNC_THREAD();
readySQLiteStatement(m_getIconIDForIconURLStatement, m_syncDB, "SELECT IconInfo.iconID FROM IconInfo WHERE IconInfo.url = (?);");
m_getIconIDForIconURLStatement->bindText(1, iconURL);
int64_t result = m_getIconIDForIconURLStatement->step();
if (result == SQLResultRow)
result = m_getIconIDForIconURLStatement->getColumnInt64(0);
else {
if (result != SQLResultDone)
LOG_ERROR("getIconIDForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data());
result = 0;
}
m_getIconIDForIconURLStatement->reset();
return result;
}
int64_t IconDatabase::addIconURLToSQLDatabase(const String& iconURL)
{
ASSERT_ICON_SYNC_THREAD();
readySQLiteStatement(m_addIconToIconInfoStatement, m_syncDB, "INSERT INTO IconInfo (url, stamp) VALUES (?, 0);");
m_addIconToIconInfoStatement->bindText(1, iconURL);
int result = m_addIconToIconInfoStatement->step();
m_addIconToIconInfoStatement->reset();
if (result != SQLResultDone) {
LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconInfo", urlForLogging(iconURL).ascii().data());
return 0;
}
int64_t iconID = m_syncDB.lastInsertRowID();
readySQLiteStatement(m_addIconToIconDataStatement, m_syncDB, "INSERT INTO IconData (iconID, data) VALUES (?, ?);");
m_addIconToIconDataStatement->bindInt64(1, iconID);
result = m_addIconToIconDataStatement->step();
m_addIconToIconDataStatement->reset();
if (result != SQLResultDone) {
LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconData", urlForLogging(iconURL).ascii().data());
return 0;
}
return iconID;
}
PassRefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(const String& iconURL)
{
ASSERT_ICON_SYNC_THREAD();
RefPtr<SharedBuffer> imageData;
readySQLiteStatement(m_getImageDataForIconURLStatement, m_syncDB, "SELECT IconData.data FROM IconData WHERE IconData.iconID IN (SELECT iconID FROM IconInfo WHERE IconInfo.url = (?));");
m_getImageDataForIconURLStatement->bindText(1, iconURL);
int result = m_getImageDataForIconURLStatement->step();
if (result == SQLResultRow) {
Vector<char> data;
m_getImageDataForIconURLStatement->getColumnBlobAsVector(0, data);
imageData = SharedBuffer::create(data.data(), data.size());
} else if (result != SQLResultDone)
LOG_ERROR("getImageDataForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data());
m_getImageDataForIconURLStatement->reset();
return imageData.release();
}
void IconDatabase::removeIconFromSQLDatabase(const String& iconURL)
{
ASSERT_ICON_SYNC_THREAD();
if (iconURL.isEmpty())
return;
int64_t iconID = getIconIDForIconURLFromSQLDatabase(iconURL);
if (!iconID)
return;
readySQLiteStatement(m_deletePageURLsForIconURLStatement, m_syncDB, "DELETE FROM PageURL WHERE PageURL.iconID = (?);");
m_deletePageURLsForIconURLStatement->bindInt64(1, iconID);
if (m_deletePageURLsForIconURLStatement->step() != SQLResultDone)
LOG_ERROR("m_deletePageURLsForIconURLStatement failed for url %s", urlForLogging(iconURL).ascii().data());
readySQLiteStatement(m_deleteIconFromIconInfoStatement, m_syncDB, "DELETE FROM IconInfo WHERE IconInfo.iconID = (?);");
m_deleteIconFromIconInfoStatement->bindInt64(1, iconID);
if (m_deleteIconFromIconInfoStatement->step() != SQLResultDone)
LOG_ERROR("m_deleteIconFromIconInfoStatement failed for url %s", urlForLogging(iconURL).ascii().data());
readySQLiteStatement(m_deleteIconFromIconDataStatement, m_syncDB, "DELETE FROM IconData WHERE IconData.iconID = (?);");
m_deleteIconFromIconDataStatement->bindInt64(1, iconID);
if (m_deleteIconFromIconDataStatement->step() != SQLResultDone)
LOG_ERROR("m_deleteIconFromIconDataStatement failed for url %s", urlForLogging(iconURL).ascii().data());
m_deletePageURLsForIconURLStatement->reset();
m_deleteIconFromIconInfoStatement->reset();
m_deleteIconFromIconDataStatement->reset();
}
void IconDatabase::writeIconSnapshotToSQLDatabase(const IconSnapshot& snapshot)
{
ASSERT_ICON_SYNC_THREAD();
if (snapshot.iconURL().isEmpty())
return;
if (!snapshot.timestamp() && !snapshot.data()) {
LOG(IconDatabase, "Removing %s from on-disk database", urlForLogging(snapshot.iconURL()).ascii().data());
removeIconFromSQLDatabase(snapshot.iconURL());
return;
}
int64_t iconID = getIconIDForIconURLFromSQLDatabase(snapshot.iconURL());
if (iconID) {
readySQLiteStatement(m_updateIconInfoStatement, m_syncDB, "UPDATE IconInfo SET stamp = ?, url = ? WHERE iconID = ?;");
m_updateIconInfoStatement->bindInt64(1, snapshot.timestamp());
m_updateIconInfoStatement->bindText(2, snapshot.iconURL());
m_updateIconInfoStatement->bindInt64(3, iconID);
if (m_updateIconInfoStatement->step() != SQLResultDone)
LOG_ERROR("Failed to update icon info for url %s", urlForLogging(snapshot.iconURL()).ascii().data());
m_updateIconInfoStatement->reset();
readySQLiteStatement(m_updateIconDataStatement, m_syncDB, "UPDATE IconData SET data = ? WHERE iconID = ?;");
m_updateIconDataStatement->bindInt64(2, iconID);
if (snapshot.data() && snapshot.data()->size())
m_updateIconDataStatement->bindBlob(1, snapshot.data()->data(), snapshot.data()->size());
else
m_updateIconDataStatement->bindNull(1);
if (m_updateIconDataStatement->step() != SQLResultDone)
LOG_ERROR("Failed to update icon data for url %s", urlForLogging(snapshot.iconURL()).ascii().data());
m_updateIconDataStatement->reset();
} else {
readySQLiteStatement(m_setIconInfoStatement, m_syncDB, "INSERT INTO IconInfo (url,stamp) VALUES (?, ?);");
m_setIconInfoStatement->bindText(1, snapshot.iconURL());
m_setIconInfoStatement->bindInt64(2, snapshot.timestamp());
if (m_setIconInfoStatement->step() != SQLResultDone)
LOG_ERROR("Failed to set icon info for url %s", urlForLogging(snapshot.iconURL()).ascii().data());
m_setIconInfoStatement->reset();
int64_t iconID = m_syncDB.lastInsertRowID();
readySQLiteStatement(m_setIconDataStatement, m_syncDB, "INSERT INTO IconData (iconID, data) VALUES (?, ?);");
m_setIconDataStatement->bindInt64(1, iconID);
if (snapshot.data() && snapshot.data()->size())
m_setIconDataStatement->bindBlob(2, snapshot.data()->data(), snapshot.data()->size());
else
m_setIconDataStatement->bindNull(2);
if (m_setIconDataStatement->step() != SQLResultDone)
LOG_ERROR("Failed to set icon data for url %s", urlForLogging(snapshot.iconURL()).ascii().data());
m_setIconDataStatement->reset();
}
}
bool IconDatabase::wasExcludedFromBackup()
{
ASSERT_ICON_SYNC_THREAD();
return SQLiteStatement(m_syncDB, "SELECT value FROM IconDatabaseInfo WHERE key = 'ExcludedFromBackup';").getColumnInt(0);
}
void IconDatabase::setWasExcludedFromBackup()
{
ASSERT_ICON_SYNC_THREAD();
SQLiteStatement(m_syncDB, "INSERT INTO IconDatabaseInfo (key, value) VALUES ('ExcludedFromBackup', 1)").executeCommand();
}
class ClientWorkItem {
public:
ClientWorkItem(IconDatabaseClient* client)
: m_client(client)
{ }
virtual void performWork() = 0;
virtual ~ClientWorkItem() { }
protected:
IconDatabaseClient* m_client;
};
class ImportedIconURLForPageURLWorkItem : public ClientWorkItem {
public:
ImportedIconURLForPageURLWorkItem(IconDatabaseClient* client, const String& pageURL)
: ClientWorkItem(client)
, m_pageURL(new String(pageURL.isolatedCopy()))
{ }
virtual ~ImportedIconURLForPageURLWorkItem()
{
delete m_pageURL;
}
virtual void performWork()
{
ASSERT(m_client);
m_client->didImportIconURLForPageURL(*m_pageURL);
m_client = 0;
}
private:
String* m_pageURL;
};
class ImportedIconDataForPageURLWorkItem : public ClientWorkItem {
public:
ImportedIconDataForPageURLWorkItem(IconDatabaseClient* client, const String& pageURL)
: ClientWorkItem(client)
, m_pageURL(new String(pageURL.isolatedCopy()))
{ }
virtual ~ImportedIconDataForPageURLWorkItem()
{
delete m_pageURL;
}
virtual void performWork()
{
ASSERT(m_client);
m_client->didImportIconDataForPageURL(*m_pageURL);
m_client = 0;
}
private:
String* m_pageURL;
};
class RemovedAllIconsWorkItem : public ClientWorkItem {
public:
RemovedAllIconsWorkItem(IconDatabaseClient* client)
: ClientWorkItem(client)
{ }
virtual void performWork()
{
ASSERT(m_client);
m_client->didRemoveAllIcons();
m_client = 0;
}
};
class FinishedURLImport : public ClientWorkItem {
public:
FinishedURLImport(IconDatabaseClient* client)
: ClientWorkItem(client)
{ }
virtual void performWork()
{
ASSERT(m_client);
m_client->didFinishURLImport();
m_client = 0;
}
};
static void performWorkItem(void* context)
{
ClientWorkItem* item = static_cast<ClientWorkItem*>(context);
item->performWork();
delete item;
}
void IconDatabase::dispatchDidImportIconURLForPageURLOnMainThread(const String& pageURL)
{
ASSERT_ICON_SYNC_THREAD();
ImportedIconURLForPageURLWorkItem* work = new ImportedIconURLForPageURLWorkItem(m_client, pageURL);
callOnMainThread(performWorkItem, work);
}
void IconDatabase::dispatchDidImportIconDataForPageURLOnMainThread(const String& pageURL)
{
ASSERT_ICON_SYNC_THREAD();
ImportedIconDataForPageURLWorkItem* work = new ImportedIconDataForPageURLWorkItem(m_client, pageURL);
callOnMainThread(performWorkItem, work);
}
void IconDatabase::dispatchDidRemoveAllIconsOnMainThread()
{
ASSERT_ICON_SYNC_THREAD();
RemovedAllIconsWorkItem* work = new RemovedAllIconsWorkItem(m_client);
callOnMainThread(performWorkItem, work);
}
void IconDatabase::dispatchDidFinishURLImportOnMainThread()
{
ASSERT_ICON_SYNC_THREAD();
FinishedURLImport* work = new FinishedURLImport(m_client);
callOnMainThread(performWorkItem, work);
}
}
#endif // ENABLE(ICONDATABASE)