/src/3rdparty/webkit/Source/WebCore/loader/icon/IconDatabase.cpp
https://bitbucket.org/ultra_iter/qt-vtl · C++ · 2250 lines · 1502 code · 470 blank · 278 comment · 293 complexity · ab102563e33e56369c45e7a1b2a9e9d7 MD5 · raw file
Large files are truncated click here to view the full file
- /*
- * Copyright (C) 2006, 2007, 2008, 2009, 2011 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 COMPUTER, 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 COMPUTER, 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.
- */
- #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>
- // For methods that are meant to support API from the main thread - should not be called internally
- #define ASSERT_NOT_SYNC_THREAD() ASSERT(!m_syncThreadRunning || !IS_ICON_SYNC_THREAD())
- // For methods that are meant to support the sync thread ONLY
- #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;
- // This version number is in the DB and marks the current generation of the schema
- // Currently, a mismatched schema causes the DB to be wiped and reset. This isn't
- // so bad during development but in the future, we would need to write a conversion
- // function to advance older released schemas to "current"
- static const int currentDatabaseVersion = 6;
- // Icons expire once every 4 days
- 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);
- }
- // ************************
- // *** Main Thread Only ***
- // ************************
- void IconDatabase::setClient(IconDatabaseClient* client)
- {
- // We don't allow a null client, because we never null check it anywhere in this code
- // Also don't allow a client change after the thread has already began
- // (setting the client should occur before the database is opened)
- 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.crossThreadString();
- // Formulate the full path for the database file
- m_completeDatabasePath = pathByAppendingComponent(m_databaseDirectory, filename);
- // Lock here as well as first thing in the thread so the thread doesn't actually commence until the createThread() call
- // completes and m_syncThreadRunning is properly set
- 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) {
- // Set the flag to tell the sync thread to wrap it up
- m_threadTerminationRequested = true;
- // Wake up the sync thread if it's waiting
- wakeSyncThread();
-
- // Wait for the sync thread to terminate
- 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");
-
- // Clear the in-memory record of every IconRecord, anything waiting to be read from disk, and anything waiting to be written to disk
- {
- MutexLocker locker(m_urlAndIconLock);
-
- // Clear the IconRecords for every page URL - RefCounting will cause the IconRecords themselves to be deleted
- // We don't delete the actual PageRecords because we have the "retain icon for url" count to keep track of
- HashMap<String, PageURLRecord*>::iterator iter = m_pageURLToRecordMap.begin();
- HashMap<String, PageURLRecord*>::iterator end = m_pageURLToRecordMap.end();
- for (; iter != end; ++iter)
- (*iter).second->setIconRecord(0);
-
- // Clear the iconURL -> IconRecord map
- m_iconURLToRecordMap.clear();
-
- // Clear all in-memory records of things that need to be synced out to disk
- {
- MutexLocker locker(m_pendingSyncLock);
- m_pageURLsPendingSync.clear();
- m_iconsPendingSync.clear();
- }
-
- // Clear all in-memory records of things that need to be read in from disk
- {
- 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();
- // pageURLOriginal cannot be stored without being deep copied first.
- // We should go our of our way to only copy it if we have to store it
-
- if (!isOpen() || !pageCanHaveIcon(pageURLOriginal))
- return defaultIcon(size);
- MutexLocker locker(m_urlAndIconLock);
-
- String pageURLCopy; // Creates a null string for easy testing
-
- PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
- if (!pageRecord) {
- pageURLCopy = pageURLOriginal.crossThreadString();
- pageRecord = getOrCreatePageURLRecord(pageURLCopy);
- }
-
- // If pageRecord is NULL, one of two things is true -
- // 1 - The initial url import is incomplete and this pageURL was marked to be notified once it is complete if an iconURL exists
- // 2 - The initial url import IS complete and this pageURL has no icon
- if (!pageRecord) {
- MutexLocker locker(m_pendingReadingLock);
-
- // Import is ongoing, there might be an icon. In this case, register to be notified when the icon comes in
- // If we ever reach this condition, we know we've already made the pageURL copy
- if (!m_iconURLImportComplete)
- m_pageURLsInterestedInIcons.add(pageURLCopy);
-
- return 0;
- }
- IconRecord* iconRecord = pageRecord->iconRecord();
-
- // If the initial URL import isn't complete, it's possible to have a PageURL record without an associated icon
- // In this case, the pageURL is already in the set to alert the client when the iconURL mapping is complete so
- // we can just bail now
- if (!m_iconURLImportComplete && !iconRecord)
- return 0;
-
- // The only way we should *not* have an icon record is if this pageURL is retained but has no icon yet - make sure of that
- ASSERT(iconRecord || m_retainedPageURLs.contains(pageURLOriginal));
-
- if (!iconRecord)
- return 0;
-
- // If it's a new IconRecord object that doesn't have its imageData set yet,
- // mark it to be read by the background thread
- if (iconRecord->imageDataStatus() == ImageDataStatusUnknown) {
- if (pageURLCopy.isNull())
- pageURLCopy = pageURLOriginal.crossThreadString();
-
- MutexLocker locker(m_pendingReadingLock);
- m_pageURLsInterestedInIcons.add(pageURLCopy);
- m_iconsPendingReading.add(iconRecord);
- wakeSyncThread();
- return 0;
- }
-
- // If the size parameter was (0, 0) that means the caller of this method just wanted the read from disk to be kicked off
- // and isn't actually interested in the image return value
- if (size == IntSize(0, 0))
- return 0;
-
- // PARANOID DISCUSSION: This method makes some assumptions. It returns a WebCore::image which the icon database might dispose of at anytime in the future,
- // and Images aren't ref counted. So there is no way for the client to guarantee continued existence of the image.
- // This has *always* been the case, but in practice clients would always create some other platform specific representation of the image
- // and drop the raw Image*. On Mac an NSImage, and on windows drawing into an HBITMAP.
- // The async aspect adds a huge question - what if the image is deleted before the platform specific API has a chance to create its own
- // representation out of it?
- // If an image is read in from the icondatabase, we do *not* overwrite any image data that exists in the in-memory cache.
- // This is because we make the assumption that anything in memory is newer than whatever is in the database.
- // So the only time the data will be set from the second thread is when it is INITIALLY being read in from the database, but we would never
- // delete the image on the secondary thread if the image already exists.
- return iconRecord->image(size);
- }
- void IconDatabase::readIconForPageURLFromDisk(const String& pageURL)
- {
- // The effect of asking for an Icon for a pageURL automatically queues it to be read from disk
- // if it hasn't already been set in memory. The special IntSize (0, 0) is a special way of telling
- // that method "I don't care about the actual Image, i just want you to make sure you're getting it from disk.
- synchronousIconForPageURL(pageURL, IntSize(0, 0));
- }
- String IconDatabase::synchronousIconURLForPageURL(const String& pageURLOriginal)
- {
- ASSERT_NOT_SYNC_THREAD();
-
- // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
- // Also, in the case we have a real answer for the caller, we must deep copy that as well
-
- if (!isOpen() || !pageCanHaveIcon(pageURLOriginal))
- return String();
-
- MutexLocker locker(m_urlAndIconLock);
-
- PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
- if (!pageRecord)
- pageRecord = getOrCreatePageURLRecord(pageURLOriginal.crossThreadString());
-
- // If pageRecord is NULL, one of two things is true -
- // 1 - The initial url import is incomplete and this pageURL has already been marked to be notified once it is complete if an iconURL exists
- // 2 - The initial url import IS complete and this pageURL has no icon
- if (!pageRecord)
- return String();
-
- // Possible the pageRecord is around because it's a retained pageURL with no iconURL, so we have to check
- return pageRecord->iconRecord() ? pageRecord->iconRecord()->iconURL().threadsafeCopy() : 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();
-
- // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
-
- if (!isEnabled() || !pageCanHaveIcon(pageURLOriginal))
- return;
-
- MutexLocker locker(m_urlAndIconLock);
- PageURLRecord* record = m_pageURLToRecordMap.get(pageURLOriginal);
-
- String pageURL;
-
- if (!record) {
- pageURL = pageURLOriginal.crossThreadString();
- record = new PageURLRecord(pageURL);
- m_pageURLToRecordMap.set(pageURL, record);
- }
-
- if (!record->retain()) {
- if (pageURL.isNull())
- pageURL = pageURLOriginal.crossThreadString();
- // This page just had its retain count bumped from 0 to 1 - Record that fact
- m_retainedPageURLs.add(pageURL);
- // If we read the iconURLs yet, we want to avoid any pageURL->iconURL lookups and the pageURLsPendingDeletion is moot,
- // so we bail here and skip those steps
- if (!m_iconURLImportComplete)
- return;
- MutexLocker locker(m_pendingSyncLock);
- // If this pageURL waiting to be sync'ed, update the sync record
- // This saves us in the case where a page was ready to be deleted from the database but was just retained - so theres no need to delete it!
- 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();
-
- // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
-
- if (!isEnabled() || !pageCanHaveIcon(pageURLOriginal))
- return;
-
- MutexLocker locker(m_urlAndIconLock);
- // Check if this pageURL is actually retained
- if (!m_retainedPageURLs.contains(pageURLOriginal)) {
- LOG_ERROR("Attempting to release icon for URL %s which is not retained", urlForLogging(pageURLOriginal).ascii().data());
- return;
- }
-
- // Get its retain count - if it's retained, we'd better have a PageURLRecord for it
- 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 it still has a positive retain count, store the new count and bail
- if (pageRecord->release())
- return;
-
- // This pageRecord has now been fully released. Do the appropriate cleanup
- LOG(IconDatabase, "No more retainers for PageURL %s", urlForLogging(pageURLOriginal).ascii().data());
- m_pageURLToRecordMap.remove(pageURLOriginal);
- m_retainedPageURLs.remove(pageURLOriginal);
-
- // Grab the iconRecord for later use (and do a sanity check on it for kicks)
- IconRecord* iconRecord = pageRecord->iconRecord();
-
- ASSERT(!iconRecord || (iconRecord && m_iconURLToRecordMap.get(iconRecord->iconURL()) == iconRecord));
- {
- MutexLocker locker(m_pendingReadingLock);
-
- // Since this pageURL is going away, there's no reason anyone would ever be interested in its read results
- if (!m_iconURLImportComplete)
- m_pageURLsPendingImport.remove(pageURLOriginal);
- m_pageURLsInterestedInIcons.remove(pageURLOriginal);
-
- // If this icon is down to it's last retainer, we don't care about reading it in from disk anymore
- if (iconRecord && iconRecord->hasOneRef()) {
- m_iconURLToRecordMap.remove(iconRecord->iconURL());
- m_iconsPendingReading.remove(iconRecord);
- }
- }
-
- // Mark stuff for deletion from the database only if we're not in private browsing
- if (!m_privateBrowsingEnabled) {
- MutexLocker locker(m_pendingSyncLock);
- m_pageURLsPendingSync.set(pageURLOriginal.crossThreadString(), pageRecord->snapshot(true));
-
- // If this page is the last page to refer to a particular IconRecord, that IconRecord needs to
- // be marked for deletion
- 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();
-
- // Cannot do anything with dataOriginal or iconURLOriginal that would end up storing them without deep copying first
-
- if (!isOpen() || iconURLOriginal.isEmpty())
- return;
-
- RefPtr<SharedBuffer> data = dataOriginal ? dataOriginal->copy() : PassRefPtr<SharedBuffer>(0);
- String iconURL = iconURLOriginal.crossThreadString();
-
- Vector<String> pageURLs;
- {
- MutexLocker locker(m_urlAndIconLock);
-
- // If this icon was pending a read, remove it from that set because this new data should override what is on disk
- RefPtr<IconRecord> icon = m_iconURLToRecordMap.get(iconURL);
- if (icon) {
- MutexLocker locker(m_pendingReadingLock);
- m_iconsPendingReading.remove(icon.get());
- } else
- icon = getOrCreateIconRecord(iconURL);
-
- // Update the data and set the time stamp
- icon->setImageData(data.release());
- icon->setTimestamp((int)currentTime());
-
- // Copy the current retaining pageURLs - if any - to notify them of the change
- pageURLs.appendRange(icon->retainingPageURLs().begin(), icon->retainingPageURLs().end());
-
- // Mark the IconRecord as requiring an update to the database only if private browsing is disabled
- 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());
- }
- }
- // Send notification out regarding all PageURLs that retain this icon
- // But not if we're on the sync thread because that implies this mapping
- // comes from the initial import which we don't want notifications for
- if (!IS_ICON_SYNC_THREAD()) {
- // Start the timer to commit this change - or further delay the timer if it was already started
- scheduleOrDeferSyncTimer();
- // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
- // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up
- 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();
- // Cannot do anything with iconURLOriginal or pageURLOriginal that would end up storing them without deep copying first
-
- ASSERT(!iconURLOriginal.isEmpty());
-
- if (!isOpen() || !pageCanHaveIcon(pageURLOriginal))
- return;
-
- String iconURL, pageURL;
-
- {
- MutexLocker locker(m_urlAndIconLock);
- PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
-
- // If the urls already map to each other, bail.
- // This happens surprisingly often, and seems to cream iBench performance
- if (pageRecord && pageRecord->iconRecord() && pageRecord->iconRecord()->iconURL() == iconURLOriginal)
- return;
-
- pageURL = pageURLOriginal.crossThreadString();
- iconURL = iconURLOriginal.crossThreadString();
- if (!pageRecord) {
- pageRecord = new PageURLRecord(pageURL);
- m_pageURLToRecordMap.set(pageURL, pageRecord);
- }
- RefPtr<IconRecord> iconRecord = pageRecord->iconRecord();
- // Otherwise, set the new icon record for this page
- pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
- // If the current icon has only a single ref left, it is about to get wiped out.
- // Remove it from the in-memory records and don't bother reading it in from disk anymore
- 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());
- }
-
- // And mark this mapping to be added to the database
- if (!m_privateBrowsingEnabled) {
- MutexLocker locker(m_pendingSyncLock);
- m_pageURLsPendingSync.set(pageURL, pageRecord->snapshot());
-
- // If the icon is on its last ref, mark it for deletion
- if (iconRecord && iconRecord->hasOneRef())
- m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
- }
- }
- // Since this mapping is new, send the notification out - but not if we're on the sync thread because that implies this mapping
- // comes from the initial import which we don't want notifications for
- if (!IS_ICON_SYNC_THREAD()) {
- // Start the timer to commit this change - or further delay the timer if it was already started
- 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;
-
- // If we have a IconRecord, it should also have its timeStamp marked because there is only two times when we create the IconRecord:
- // 1 - When we read the icon urls from disk, getting the timeStamp at the same time
- // 2 - When we get a new icon from the loader, in which case the timestamp is set at that time
- {
- 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;
- }
- }
-
- // If we don't have a record for it, but we *have* imported all iconURLs from disk, then we should load it now
- MutexLocker readingLocker(m_pendingReadingLock);
- if (m_iconURLImportComplete)
- return IconLoadYes;
-
- // Otherwise - since we refuse to perform I/O on the main thread to find out for sure - we return the answer that says
- // "You might be asked to load this later, so flag that"
- 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();
-
- // This method should only be called upon completion of the initial url import from the database
- 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;
- // The following is balanced by the call to enableSuddenTermination in the
- // syncThreadMainLoop function.
- // FIXME: It would be better to only disable sudden termination if we have
- // something to write, not just if we have something to read.
- disableSuddenTermination();
- }
- m_syncCondition.signal();
- }
- void IconDatabase::scheduleOrDeferSyncTimer()
- {
- ASSERT_NOT_SYNC_THREAD();
- if (!m_syncTimer.isActive()) {
- // The following is balanced by the call to enableSuddenTermination in the
- // syncTimerFired function.
- disableSuddenTermination();
- }
- m_syncTimer.startOneShot(updateTimerDelay);
- }
- void IconDatabase::syncTimerFired(Timer<IconDatabase>*)
- {
- ASSERT_NOT_SYNC_THREAD();
- wakeSyncThread();
- // The following is balanced by the call to disableSuddenTermination in the
- // scheduleOrDeferSyncTimer function.
- enableSuddenTermination();
- }
- // ******************
- // *** Any Thread ***
- // ******************
- bool IconDatabase::isOpen() const
- {
- MutexLocker locker(m_syncLock);
- return m_syncDB.isOpen();
- }
- String IconDatabase::databasePath() const
- {
- MutexLocker locker(m_syncLock);
- return m_completeDatabasePath.threadsafeCopy();
- }
- String IconDatabase::defaultDatabaseFilename()
- {
- DEFINE_STATIC_LOCAL(String, defaultDatabaseFilename, ("WebpageIcons.db"));
- return defaultDatabaseFilename.threadsafeCopy();
- }
- // Unlike getOrCreatePageURLRecord(), getOrCreateIconRecord() does not mark the icon as "interested in import"
- PassRefPtr<IconRecord> IconDatabase::getOrCreateIconRecord(const String& iconURL)
- {
- // Clients of getOrCreateIconRecord() are required to acquire the m_urlAndIconLock before calling this method
- 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();
- }
- // This method retrieves the existing PageURLRecord, or creates a new one and marks it as "interested in the import" for later notification
- PageURLRecord* IconDatabase::getOrCreatePageURLRecord(const String& pageURL)
- {
- // Clients of getOrCreatePageURLRecord() are required to acquire the m_urlAndIconLock before calling this method
- ASSERT(!m_urlAndIconLock.tryLock());
- if (!pageCanHaveIcon(pageURL))
- return 0;
- PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
-
- MutexLocker locker(m_pendingReadingLock);
- if (!m_iconURLImportComplete) {
- // If the initial import of all URLs hasn't completed and we have no page record, we assume we *might* know about this later and create a record for it
- if (!pageRecord) {
- LOG(IconDatabase, "Creating new PageURLRecord for pageURL %s", urlForLogging(pageURL).ascii().data());
- pageRecord = new PageURLRecord(pageURL);
- m_pageURLToRecordMap.set(pageURL, pageRecord);
- }
- // If the pageRecord for this page does not have an iconRecord attached to it, then it is a new pageRecord still awaiting the initial import
- // Mark the URL as "interested in the result of the import" then bail
- if (!pageRecord->iconRecord()) {
- m_pageURLsPendingImport.add(pageURL);
- return 0;
- }
- }
- // We've done the initial import of all URLs known in the database. If this record doesn't exist now, it never will
- return pageRecord;
- }
- // ************************
- // *** Sync Thread Only ***
- // ************************
- void IconDatabase::importIconURLForPageURL(const String& iconURL, const String& pageURL)
- {
- ASSERT_ICON_SYNC_THREAD();
-
- // This function is only for setting actual existing url mappings so assert that neither of these URLs are empty
- 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()
- {
- // The call to create this thread might not complete before the thread actually starts, so we might fail this ASSERT_ICON_SYNC_THREAD() because the pointer
- // to our thread structure hasn't been filled in yet.
- // To fix this, the main thread acquires this lock before creating us, then releases the lock after creation is complete. A quick lock/unlock cycle here will
- // prevent us from running before that call completes
- m_syncLock.lock();
- m_syncLock.unlock();
- ASSERT_ICON_SYNC_THREAD();
-
- LOG(IconDatabase, "(THREAD) IconDatabase sync thread started");
- #ifndef NDEBUG
- double startTime = currentTime();
- #endif
- // Need to create the database path if it doesn't already exist
- makeAllDirectories(m_databaseDirectory);
- // Existence of a journal file is evidence of a previous crash/force quit and automatically qualifies
- // us to do an integrity check
- 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();
-
- // Commit the transaction only if the import completes (the import should be atomic)
- 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
- }
-
- // Uncomment the following line to simulate a long lasting URL import (*HUGE* icon databases, or network home directories)
- // while (currentTime() - timeStamp < 10);
- // Read in URL mappings from the database
- 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)
- {
- // These four tables should always exist in a valid 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);
- // Should've been consumed by SQLite, delete just to make sure we don't see it again in the future;
- deleteFile(m_completeDatabasePath + "-journal");
- deleteFile(m_completeDatabasePath);
- }
-
- // Reopen the write database, creating it from scratch
- 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);
- }
- // Reduce sqlite RAM cache size from default 2000 pages (~1.5kB per page). 3MB of cache for icon database is overkill
- if (!SQLiteStatement(m_syncDB, "PRAGMA cache_size = 200;").executeCommand())
- LOG_ERROR("SQLite database could not set cache_size");
- // Tell backup software (i.e., Time Machine) to never back up the icon database, because
- // it's a large file that changes frequently, thus using a lot of backup disk space, and
- // it's unlikely that many users would be upset about it not being backed up. We could
- // make this configurable on a per-client basis some day if some clients don't want this.
- if (canExcludeFromBackup() && !wasExcludedFromBackup() && excludeFromBackup(m_completeDatabasePath))
- setWas…