a8507a97d089ad49401ab2cf4fd97f90f7934dbd
[WebKit-https.git] / Source / WebCore / loader / icon / IconDatabase.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "IconDatabase.h"
29
30 #if ENABLE(ICONDATABASE)
31
32 #include "AutodrainedPool.h"
33 #include "DocumentLoader.h"
34 #include "FileSystem.h"
35 #include "IconDatabaseClient.h"
36 #include "IconRecord.h"
37 #include "IntSize.h"
38 #include "Logging.h"
39 #include "SQLiteStatement.h"
40 #include "SQLiteTransaction.h"
41 #include "SuddenTermination.h"
42 #include <wtf/CurrentTime.h>
43 #include <wtf/MainThread.h>
44 #include <wtf/StdLibExtras.h>
45 #include <wtf/text/CString.h>
46
47 // For methods that are meant to support API from the main thread - should not be called internally
48 #define ASSERT_NOT_SYNC_THREAD() ASSERT(!m_syncThreadRunning || !IS_ICON_SYNC_THREAD())
49
50 // For methods that are meant to support the sync thread ONLY
51 #define IS_ICON_SYNC_THREAD() (m_syncThread == currentThread())
52 #define ASSERT_ICON_SYNC_THREAD() ASSERT(IS_ICON_SYNC_THREAD())
53
54 #if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(WIN_CAIRO)
55 #define CAN_THEME_URL_ICON
56 #endif
57
58 namespace WebCore {
59
60 static int databaseCleanupCounter = 0;
61
62 // This version number is in the DB and marks the current generation of the schema
63 // Currently, a mismatched schema causes the DB to be wiped and reset.  This isn't 
64 // so bad during development but in the future, we would need to write a conversion
65 // function to advance older released schemas to "current"
66 static const int currentDatabaseVersion = 6;
67
68 // Icons expire once every 4 days
69 static const int iconExpirationTime = 60*60*24*4; 
70
71 static const int updateTimerDelay = 5; 
72
73 static bool checkIntegrityOnOpen = false;
74
75 #if !LOG_DISABLED || !ERROR_DISABLED
76 static String urlForLogging(const String& url)
77 {
78     static unsigned urlTruncationLength = 120;
79
80     if (url.length() < urlTruncationLength)
81         return url;
82     return url.substring(0, urlTruncationLength) + "...";
83 }
84 #endif
85
86 class DefaultIconDatabaseClient : public IconDatabaseClient {
87 public:
88     virtual bool performImport() { return true; }
89     virtual void didImportIconURLForPageURL(const String&) { } 
90     virtual void didImportIconDataForPageURL(const String&) { }
91     virtual void didChangeIconForPageURL(const String&) { }
92     virtual void didRemoveAllIcons() { }
93     virtual void didFinishURLImport() { }
94 };
95
96 static IconDatabaseClient* defaultClient() 
97 {
98     static IconDatabaseClient* defaultClient = new DefaultIconDatabaseClient();
99     return defaultClient;
100 }
101
102 // ************************
103 // *** Main Thread Only ***
104 // ************************
105
106 void IconDatabase::setClient(IconDatabaseClient* client)
107 {
108     // We don't allow a null client, because we never null check it anywhere in this code
109     // Also don't allow a client change after the thread has already began 
110     // (setting the client should occur before the database is opened)
111     ASSERT(client);
112     ASSERT(!m_syncThreadRunning);
113     if (!client || m_syncThreadRunning)
114         return;
115         
116     m_client = client;
117 }
118
119 bool IconDatabase::open(const String& directory, const String& filename)
120 {
121     ASSERT_NOT_SYNC_THREAD();
122
123     if (!m_isEnabled)
124         return false;
125
126     if (isOpen()) {
127         LOG_ERROR("Attempt to reopen the IconDatabase which is already open.  Must close it first.");
128         return false;
129     }
130
131     m_databaseDirectory = directory.isolatedCopy();
132
133     // Formulate the full path for the database file
134     m_completeDatabasePath = pathByAppendingComponent(m_databaseDirectory, filename);
135
136     // Lock here as well as first thing in the thread so the thread doesn't actually commence until the createThread() call 
137     // completes and m_syncThreadRunning is properly set
138     m_syncLock.lock();
139     m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore: IconDatabase");
140     m_syncThreadRunning = m_syncThread;
141     m_syncLock.unlock();
142     if (!m_syncThread)
143         return false;
144     return true;
145 }
146
147 void IconDatabase::close()
148 {
149     ASSERT_NOT_SYNC_THREAD();
150     
151     if (m_syncThreadRunning) {
152         // Set the flag to tell the sync thread to wrap it up
153         m_threadTerminationRequested = true;
154
155         // Wake up the sync thread if it's waiting
156         wakeSyncThread();
157         
158         // Wait for the sync thread to terminate
159         waitForThreadCompletion(m_syncThread, 0);
160     }
161
162     m_syncThreadRunning = false;    
163     m_threadTerminationRequested = false;
164     m_removeIconsRequested = false;
165
166     m_syncDB.close();
167     ASSERT(!isOpen());
168 }
169
170 void IconDatabase::removeAllIcons()
171 {
172     ASSERT_NOT_SYNC_THREAD();
173     
174     if (!isOpen())
175         return;
176
177     LOG(IconDatabase, "Requesting background thread to remove all icons");
178     
179     // Clear the in-memory record of every IconRecord, anything waiting to be read from disk, and anything waiting to be written to disk
180     {
181         MutexLocker locker(m_urlAndIconLock);
182         
183         // Clear the IconRecords for every page URL - RefCounting will cause the IconRecords themselves to be deleted
184         // We don't delete the actual PageRecords because we have the "retain icon for url" count to keep track of
185         HashMap<String, PageURLRecord*>::iterator iter = m_pageURLToRecordMap.begin();
186         HashMap<String, PageURLRecord*>::iterator end = m_pageURLToRecordMap.end();
187         for (; iter != end; ++iter)
188             (*iter).second->setIconRecord(0);
189             
190         // Clear the iconURL -> IconRecord map
191         m_iconURLToRecordMap.clear();
192                     
193         // Clear all in-memory records of things that need to be synced out to disk
194         {
195             MutexLocker locker(m_pendingSyncLock);
196             m_pageURLsPendingSync.clear();
197             m_iconsPendingSync.clear();
198         }
199         
200         // Clear all in-memory records of things that need to be read in from disk
201         {
202             MutexLocker locker(m_pendingReadingLock);
203             m_pageURLsPendingImport.clear();
204             m_pageURLsInterestedInIcons.clear();
205             m_iconsPendingReading.clear();
206             m_loadersPendingDecision.clear();
207         }
208     }
209     
210     m_removeIconsRequested = true;
211     wakeSyncThread();
212 }
213
214 Image* IconDatabase::synchronousIconForPageURL(const String& pageURLOriginal, const IntSize& size)
215 {   
216     ASSERT_NOT_SYNC_THREAD();
217
218     // pageURLOriginal cannot be stored without being deep copied first.  
219     // We should go our of our way to only copy it if we have to store it
220     
221     if (!isOpen() || !documentCanHaveIcon(pageURLOriginal))
222         return 0;
223
224     MutexLocker locker(m_urlAndIconLock);
225     
226     String pageURLCopy; // Creates a null string for easy testing
227     
228     PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
229     if (!pageRecord) {
230         pageURLCopy = pageURLOriginal.isolatedCopy();
231         pageRecord = getOrCreatePageURLRecord(pageURLCopy);
232     }
233     
234     // If pageRecord is NULL, one of two things is true -
235     // 1 - The initial url import is incomplete and this pageURL was marked to be notified once it is complete if an iconURL exists
236     // 2 - The initial url import IS complete and this pageURL has no icon
237     if (!pageRecord) {
238         MutexLocker locker(m_pendingReadingLock);
239         
240         // Import is ongoing, there might be an icon.  In this case, register to be notified when the icon comes in
241         // If we ever reach this condition, we know we've already made the pageURL copy
242         if (!m_iconURLImportComplete)
243             m_pageURLsInterestedInIcons.add(pageURLCopy);
244         
245         return 0;
246     }
247
248     IconRecord* iconRecord = pageRecord->iconRecord();
249     
250     // If the initial URL import isn't complete, it's possible to have a PageURL record without an associated icon
251     // In this case, the pageURL is already in the set to alert the client when the iconURL mapping is complete so
252     // we can just bail now
253     if (!m_iconURLImportComplete && !iconRecord)
254         return 0;
255     
256     // 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
257     ASSERT(iconRecord || m_retainedPageURLs.contains(pageURLOriginal));
258     
259     if (!iconRecord)
260         return 0;
261         
262     // If it's a new IconRecord object that doesn't have its imageData set yet,
263     // mark it to be read by the background thread
264     if (iconRecord->imageDataStatus() == ImageDataStatusUnknown) {
265         if (pageURLCopy.isNull())
266             pageURLCopy = pageURLOriginal.isolatedCopy();
267     
268         MutexLocker locker(m_pendingReadingLock);
269         m_pageURLsInterestedInIcons.add(pageURLCopy);
270         m_iconsPendingReading.add(iconRecord);
271         wakeSyncThread();
272         return 0;
273     }
274     
275     // If the size parameter was (0, 0) that means the caller of this method just wanted the read from disk to be kicked off
276     // and isn't actually interested in the image return value
277     if (size == IntSize(0, 0))
278         return 0;
279         
280     // PARANOID DISCUSSION: This method makes some assumptions.  It returns a WebCore::image which the icon database might dispose of at anytime in the future,
281     // and Images aren't ref counted.  So there is no way for the client to guarantee continued existence of the image.
282     // This has *always* been the case, but in practice clients would always create some other platform specific representation of the image
283     // and drop the raw Image*.  On Mac an NSImage, and on windows drawing into an HBITMAP.
284     // 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
285     // representation out of it?
286     // If an image is read in from the icondatabase, we do *not* overwrite any image data that exists in the in-memory cache.  
287     // This is because we make the assumption that anything in memory is newer than whatever is in the database.
288     // 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 
289     // delete the image on the secondary thread if the image already exists.
290     return iconRecord->image(size);
291 }
292
293 void IconDatabase::readIconForPageURLFromDisk(const String& pageURL)
294 {
295     // The effect of asking for an Icon for a pageURL automatically queues it to be read from disk
296     // if it hasn't already been set in memory.  The special IntSize (0, 0) is a special way of telling 
297     // that method "I don't care about the actual Image, i just want you to make sure you're getting it from disk.
298     synchronousIconForPageURL(pageURL, IntSize(0, 0));
299 }
300
301 String IconDatabase::synchronousIconURLForPageURL(const String& pageURLOriginal)
302 {    
303     ASSERT_NOT_SYNC_THREAD(); 
304         
305     // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
306     // Also, in the case we have a real answer for the caller, we must deep copy that as well
307     
308     if (!isOpen() || !documentCanHaveIcon(pageURLOriginal))
309         return String();
310         
311     MutexLocker locker(m_urlAndIconLock);
312     
313     PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
314     if (!pageRecord)
315         pageRecord = getOrCreatePageURLRecord(pageURLOriginal.isolatedCopy());
316     
317     // If pageRecord is NULL, one of two things is true -
318     // 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
319     // 2 - The initial url import IS complete and this pageURL has no icon
320     if (!pageRecord)
321         return String();
322     
323     // Possible the pageRecord is around because it's a retained pageURL with no iconURL, so we have to check
324     return pageRecord->iconRecord() ? pageRecord->iconRecord()->iconURL().isolatedCopy() : String();
325 }
326
327 #ifdef CAN_THEME_URL_ICON
328 static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord)
329 {
330      defaultIconRecord->loadImageFromResource("urlIcon");
331 }
332 #else
333 static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord)
334 {
335     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, 
336         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, 
337         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, 
338         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, 
339         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, 
340         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, 
341         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, 
342         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, 
343         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, 
344         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, 
345         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, 
346         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, 
347         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, 
348         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, 
349         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, 
350         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, 
351         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, 
352         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, 
353         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,
354         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, 
355         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, 
356         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, 
357         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, 
358         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, 
359         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, 
360         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, 
361         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, 
362         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, 
363         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, 
364         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, 
365         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, 
366         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, 
367         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, 
368         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, 
369         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, 
370         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, 
371         0xFC, 0x80, 0x00, 0x00, 0x27, 0x10, 0x00, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10 };
372         
373     DEFINE_STATIC_LOCAL(RefPtr<SharedBuffer>, defaultIconBuffer, (SharedBuffer::create(defaultIconData, sizeof(defaultIconData))));
374     defaultIconRecord->setImageData(defaultIconBuffer);
375 }
376 #endif
377
378 Image* IconDatabase::defaultIcon(const IntSize& size)
379 {
380     ASSERT_NOT_SYNC_THREAD();
381
382     
383     if (!m_defaultIconRecord) {
384         m_defaultIconRecord = IconRecord::create("urlIcon");
385         m_defaultIconRecord->setMutexForVerifier(m_urlAndIconLock);
386         loadDefaultIconRecord(m_defaultIconRecord.get());
387     }
388     
389     return m_defaultIconRecord->image(size);
390 }
391
392
393 void IconDatabase::retainIconForPageURL(const String& pageURLOriginal)
394 {    
395     ASSERT_NOT_SYNC_THREAD();
396     
397     // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
398     
399     if (!isEnabled() || !documentCanHaveIcon(pageURLOriginal))
400         return;
401        
402     MutexLocker locker(m_urlAndIconLock);
403
404     PageURLRecord* record = m_pageURLToRecordMap.get(pageURLOriginal);
405     
406     String pageURL;
407     
408     if (!record) {
409         pageURL = pageURLOriginal.isolatedCopy();
410
411         record = new PageURLRecord(pageURL);
412         m_pageURLToRecordMap.set(pageURL, record);
413     }
414     
415     if (!record->retain()) {
416         if (pageURL.isNull())
417             pageURL = pageURLOriginal.isolatedCopy();
418
419         // This page just had its retain count bumped from 0 to 1 - Record that fact
420         m_retainedPageURLs.add(pageURL);
421
422         // If we read the iconURLs yet, we want to avoid any pageURL->iconURL lookups and the pageURLsPendingDeletion is moot, 
423         // so we bail here and skip those steps
424         if (!m_iconURLImportComplete)
425             return;
426
427         MutexLocker locker(m_pendingSyncLock);
428         // If this pageURL waiting to be sync'ed, update the sync record
429         // 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!
430         if (!m_privateBrowsingEnabled && m_pageURLsPendingSync.contains(pageURL)) {
431             LOG(IconDatabase, "Bringing %s back from the brink", pageURL.ascii().data());
432             m_pageURLsPendingSync.set(pageURL, record->snapshot());
433         }
434     }
435 }
436
437 void IconDatabase::releaseIconForPageURL(const String& pageURLOriginal)
438 {
439     ASSERT_NOT_SYNC_THREAD();
440         
441     // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
442     
443     if (!isEnabled() || !documentCanHaveIcon(pageURLOriginal))
444         return;
445     
446     MutexLocker locker(m_urlAndIconLock);
447
448     // Check if this pageURL is actually retained
449     if (!m_retainedPageURLs.contains(pageURLOriginal)) {
450         LOG_ERROR("Attempting to release icon for URL %s which is not retained", urlForLogging(pageURLOriginal).ascii().data());
451         return;
452     }
453     
454     // Get its retain count - if it's retained, we'd better have a PageURLRecord for it
455     PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
456     ASSERT(pageRecord);
457     LOG(IconDatabase, "Releasing pageURL %s to a retain count of %i", urlForLogging(pageURLOriginal).ascii().data(), pageRecord->retainCount() - 1);
458     ASSERT(pageRecord->retainCount() > 0);
459         
460     // If it still has a positive retain count, store the new count and bail
461     if (pageRecord->release())
462         return;
463         
464     // This pageRecord has now been fully released.  Do the appropriate cleanup
465     LOG(IconDatabase, "No more retainers for PageURL %s", urlForLogging(pageURLOriginal).ascii().data());
466     m_pageURLToRecordMap.remove(pageURLOriginal);
467     m_retainedPageURLs.remove(pageURLOriginal);       
468     
469     // Grab the iconRecord for later use (and do a sanity check on it for kicks)
470     IconRecord* iconRecord = pageRecord->iconRecord();
471     
472     ASSERT(!iconRecord || (iconRecord && m_iconURLToRecordMap.get(iconRecord->iconURL()) == iconRecord));
473
474     {
475         MutexLocker locker(m_pendingReadingLock);
476         
477         // Since this pageURL is going away, there's no reason anyone would ever be interested in its read results    
478         if (!m_iconURLImportComplete)
479             m_pageURLsPendingImport.remove(pageURLOriginal);
480         m_pageURLsInterestedInIcons.remove(pageURLOriginal);
481         
482         // If this icon is down to it's last retainer, we don't care about reading it in from disk anymore
483         if (iconRecord && iconRecord->hasOneRef()) {
484             m_iconURLToRecordMap.remove(iconRecord->iconURL());
485             m_iconsPendingReading.remove(iconRecord);
486         }
487     }
488     
489     // Mark stuff for deletion from the database only if we're not in private browsing
490     if (!m_privateBrowsingEnabled) {
491         MutexLocker locker(m_pendingSyncLock);
492         m_pageURLsPendingSync.set(pageURLOriginal.isolatedCopy(), pageRecord->snapshot(true));
493     
494         // If this page is the last page to refer to a particular IconRecord, that IconRecord needs to
495         // be marked for deletion
496         if (iconRecord && iconRecord->hasOneRef())
497             m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
498     }
499     
500     delete pageRecord;
501
502     if (isOpen())
503         scheduleOrDeferSyncTimer();
504 }
505
506 void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, const String& iconURLOriginal)
507 {    
508     ASSERT_NOT_SYNC_THREAD();
509     
510     // Cannot do anything with dataOriginal or iconURLOriginal that would end up storing them without deep copying first
511     
512     if (!isOpen() || iconURLOriginal.isEmpty())
513         return;
514     
515     RefPtr<SharedBuffer> data = dataOriginal ? dataOriginal->copy() : PassRefPtr<SharedBuffer>(0);
516     if (data)
517         data->setMutexForVerifier(m_urlAndIconLock);
518     String iconURL = iconURLOriginal.isolatedCopy();
519     
520     Vector<String> pageURLs;
521     {
522         MutexLocker locker(m_urlAndIconLock);
523     
524         // If this icon was pending a read, remove it from that set because this new data should override what is on disk
525         RefPtr<IconRecord> icon = m_iconURLToRecordMap.get(iconURL);
526         if (icon) {
527             MutexLocker locker(m_pendingReadingLock);
528             m_iconsPendingReading.remove(icon.get());
529         } else
530             icon = getOrCreateIconRecord(iconURL);
531     
532         // Update the data and set the time stamp
533         icon->setImageData(data.release());
534         icon->setTimestamp((int)currentTime());
535         
536         // Copy the current retaining pageURLs - if any - to notify them of the change
537         pageURLs.appendRange(icon->retainingPageURLs().begin(), icon->retainingPageURLs().end());
538         
539         // Mark the IconRecord as requiring an update to the database only if private browsing is disabled
540         if (!m_privateBrowsingEnabled) {
541             MutexLocker locker(m_pendingSyncLock);
542             m_iconsPendingSync.set(iconURL, icon->snapshot());
543         }
544
545         if (icon->hasOneRef()) {
546             ASSERT(icon->retainingPageURLs().isEmpty());
547             LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(icon->iconURL()).ascii().data());
548             m_iconURLToRecordMap.remove(icon->iconURL());
549         }
550     }
551
552     // Send notification out regarding all PageURLs that retain this icon
553     // But not if we're on the sync thread because that implies this mapping
554     // comes from the initial import which we don't want notifications for
555     if (!IS_ICON_SYNC_THREAD()) {
556         // Start the timer to commit this change - or further delay the timer if it was already started
557         scheduleOrDeferSyncTimer();
558
559         // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
560         // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up        
561         AutodrainedPool pool(25);
562
563         for (unsigned i = 0; i < pageURLs.size(); ++i) {
564             LOG(IconDatabase, "Dispatching notification that retaining pageURL %s has a new icon", urlForLogging(pageURLs[i]).ascii().data());
565             m_client->didChangeIconForPageURL(pageURLs[i]);
566
567             pool.cycle();
568         }
569     }
570 }
571
572 void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const String& pageURLOriginal)
573 {    
574     ASSERT_NOT_SYNC_THREAD();
575
576     // Cannot do anything with iconURLOriginal or pageURLOriginal that would end up storing them without deep copying first
577     
578     ASSERT(!iconURLOriginal.isEmpty());
579         
580     if (!isOpen() || !documentCanHaveIcon(pageURLOriginal))
581         return;
582     
583     String iconURL, pageURL;
584     
585     {
586         MutexLocker locker(m_urlAndIconLock);
587
588         PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
589         
590         // If the urls already map to each other, bail.
591         // This happens surprisingly often, and seems to cream iBench performance
592         if (pageRecord && pageRecord->iconRecord() && pageRecord->iconRecord()->iconURL() == iconURLOriginal)
593             return;
594             
595         pageURL = pageURLOriginal.isolatedCopy();
596         iconURL = iconURLOriginal.isolatedCopy();
597
598         if (!pageRecord) {
599             pageRecord = new PageURLRecord(pageURL);
600             m_pageURLToRecordMap.set(pageURL, pageRecord);
601         }
602
603         RefPtr<IconRecord> iconRecord = pageRecord->iconRecord();
604
605         // Otherwise, set the new icon record for this page
606         pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
607
608         // If the current icon has only a single ref left, it is about to get wiped out. 
609         // Remove it from the in-memory records and don't bother reading it in from disk anymore
610         if (iconRecord && iconRecord->hasOneRef()) {
611             ASSERT(iconRecord->retainingPageURLs().size() == 0);
612             LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(iconRecord->iconURL()).ascii().data());
613             m_iconURLToRecordMap.remove(iconRecord->iconURL());
614             MutexLocker locker(m_pendingReadingLock);
615             m_iconsPendingReading.remove(iconRecord.get());
616         }
617         
618         // And mark this mapping to be added to the database
619         if (!m_privateBrowsingEnabled) {
620             MutexLocker locker(m_pendingSyncLock);
621             m_pageURLsPendingSync.set(pageURL, pageRecord->snapshot());
622             
623             // If the icon is on its last ref, mark it for deletion
624             if (iconRecord && iconRecord->hasOneRef())
625                 m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
626         }
627     }
628
629     // Since this mapping is new, send the notification out - but not if we're on the sync thread because that implies this mapping
630     // comes from the initial import which we don't want notifications for
631     if (!IS_ICON_SYNC_THREAD()) {
632         // Start the timer to commit this change - or further delay the timer if it was already started
633         scheduleOrDeferSyncTimer();
634         
635         LOG(IconDatabase, "Dispatching notification that we changed an icon mapping for url %s", urlForLogging(pageURL).ascii().data());
636         AutodrainedPool pool;
637         m_client->didChangeIconForPageURL(pageURL);
638     }
639 }
640
641 IconLoadDecision IconDatabase::synchronousLoadDecisionForIconURL(const String& iconURL, DocumentLoader* notificationDocumentLoader)
642 {
643     ASSERT_NOT_SYNC_THREAD();
644
645     if (!isOpen() || iconURL.isEmpty())
646         return IconLoadNo;
647     
648     // If we have a IconRecord, it should also have its timeStamp marked because there is only two times when we create the IconRecord:
649     // 1 - When we read the icon urls from disk, getting the timeStamp at the same time
650     // 2 - When we get a new icon from the loader, in which case the timestamp is set at that time
651     {
652         MutexLocker locker(m_urlAndIconLock);
653         if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL)) {
654             LOG(IconDatabase, "Found expiration time on a present icon based on existing IconRecord");
655             return (int)currentTime() - icon->getTimestamp() > iconExpirationTime ? IconLoadYes : IconLoadNo;
656         }
657     }
658     
659     // If we don't have a record for it, but we *have* imported all iconURLs from disk, then we should load it now
660     MutexLocker readingLocker(m_pendingReadingLock);
661     if (m_iconURLImportComplete)
662         return IconLoadYes;
663         
664     // Otherwise - since we refuse to perform I/O on the main thread to find out for sure - we return the answer that says
665     // "You might be asked to load this later, so flag that"
666     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);
667     if (notificationDocumentLoader)
668         m_loadersPendingDecision.add(notificationDocumentLoader);    
669
670     return IconLoadUnknown;
671 }
672
673 bool IconDatabase::synchronousIconDataKnownForIconURL(const String& iconURL)
674 {
675     ASSERT_NOT_SYNC_THREAD();
676     
677     MutexLocker locker(m_urlAndIconLock);
678     if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
679         return icon->imageDataStatus() != ImageDataStatusUnknown;
680
681     return false;
682 }
683
684 void IconDatabase::setEnabled(bool enabled)
685 {
686     ASSERT_NOT_SYNC_THREAD();
687     
688     if (!enabled && isOpen())
689         close();
690     m_isEnabled = enabled;
691 }
692
693 bool IconDatabase::isEnabled() const
694 {
695     ASSERT_NOT_SYNC_THREAD();
696     
697      return m_isEnabled;
698 }
699
700 void IconDatabase::setPrivateBrowsingEnabled(bool flag)
701 {
702     m_privateBrowsingEnabled = flag;
703 }
704
705 bool IconDatabase::isPrivateBrowsingEnabled() const
706 {
707     return m_privateBrowsingEnabled;
708 }
709
710 void IconDatabase::delayDatabaseCleanup()
711 {
712     ++databaseCleanupCounter;
713     if (databaseCleanupCounter == 1)
714         LOG(IconDatabase, "Database cleanup is now DISABLED");
715 }
716
717 void IconDatabase::allowDatabaseCleanup()
718 {
719     if (--databaseCleanupCounter < 0)
720         databaseCleanupCounter = 0;
721     if (databaseCleanupCounter == 0)
722         LOG(IconDatabase, "Database cleanup is now ENABLED");
723 }
724
725 void IconDatabase::checkIntegrityBeforeOpening()
726 {
727     checkIntegrityOnOpen = true;
728 }
729
730 size_t IconDatabase::pageURLMappingCount()
731 {
732     MutexLocker locker(m_urlAndIconLock);
733     return m_pageURLToRecordMap.size();
734 }
735
736 size_t IconDatabase::retainedPageURLCount()
737 {
738     MutexLocker locker(m_urlAndIconLock);
739     return m_retainedPageURLs.size();
740 }
741
742 size_t IconDatabase::iconRecordCount()
743 {
744     MutexLocker locker(m_urlAndIconLock);
745     return m_iconURLToRecordMap.size();
746 }
747
748 size_t IconDatabase::iconRecordCountWithData()
749 {
750     MutexLocker locker(m_urlAndIconLock);
751     size_t result = 0;
752     
753     HashMap<String, IconRecord*>::iterator i = m_iconURLToRecordMap.begin();
754     HashMap<String, IconRecord*>::iterator end = m_iconURLToRecordMap.end();
755     
756     for (; i != end; ++i)
757         result += ((*i).second->imageDataStatus() == ImageDataStatusPresent);
758             
759     return result;
760 }
761
762 IconDatabase::IconDatabase()
763     : m_syncTimer(this, &IconDatabase::syncTimerFired)
764     , m_syncThreadRunning(false)
765     , m_isEnabled(false)
766     , m_privateBrowsingEnabled(false)
767     , m_threadTerminationRequested(false)
768     , m_removeIconsRequested(false)
769     , m_iconURLImportComplete(false)
770     , m_syncThreadHasWorkToDo(false)
771     , m_disabledSuddenTerminationForSyncThread(false)
772     , m_initialPruningComplete(false)
773     , m_client(defaultClient())
774     , m_imported(false)
775     , m_isImportedSet(false)
776 {
777     LOG(IconDatabase, "Creating IconDatabase %p", this);
778     ASSERT(isMainThread());
779 }
780
781 IconDatabase::~IconDatabase()
782 {
783     ASSERT_NOT_REACHED();
784 }
785
786 void IconDatabase::notifyPendingLoadDecisionsOnMainThread(void* context)
787 {
788     static_cast<IconDatabase*>(context)->notifyPendingLoadDecisions();
789 }
790
791 void IconDatabase::notifyPendingLoadDecisions()
792 {
793     ASSERT_NOT_SYNC_THREAD();
794     
795     // This method should only be called upon completion of the initial url import from the database
796     ASSERT(m_iconURLImportComplete);
797     LOG(IconDatabase, "Notifying all DocumentLoaders that were waiting on a load decision for thier icons");
798     
799     HashSet<RefPtr<DocumentLoader> >::iterator i = m_loadersPendingDecision.begin();
800     HashSet<RefPtr<DocumentLoader> >::iterator end = m_loadersPendingDecision.end();
801     
802     for (; i != end; ++i)
803         if ((*i)->refCount() > 1)
804             (*i)->iconLoadDecisionAvailable();
805             
806     m_loadersPendingDecision.clear();
807 }
808
809 void IconDatabase::wakeSyncThread()
810 {
811     MutexLocker locker(m_syncLock);
812
813     if (!m_disabledSuddenTerminationForSyncThread) {
814         m_disabledSuddenTerminationForSyncThread = true;
815         // The following is balanced by the call to enableSuddenTermination in the
816         // syncThreadMainLoop function.
817         // FIXME: It would be better to only disable sudden termination if we have
818         // something to write, not just if we have something to read.
819         disableSuddenTermination();
820     }
821
822     m_syncThreadHasWorkToDo = true;
823     m_syncCondition.signal();
824 }
825
826 void IconDatabase::scheduleOrDeferSyncTimer()
827 {
828     ASSERT_NOT_SYNC_THREAD();
829
830     if (!m_syncTimer.isActive()) {
831         // The following is balanced by the call to enableSuddenTermination in the
832         // syncTimerFired function.
833         disableSuddenTermination();
834     }
835
836     m_syncTimer.startOneShot(updateTimerDelay);
837 }
838
839 void IconDatabase::syncTimerFired(Timer<IconDatabase>*)
840 {
841     ASSERT_NOT_SYNC_THREAD();
842     wakeSyncThread();
843
844     // The following is balanced by the call to disableSuddenTermination in the
845     // scheduleOrDeferSyncTimer function.
846     enableSuddenTermination();
847 }
848
849 // ******************
850 // *** Any Thread ***
851 // ******************
852
853 bool IconDatabase::isOpen() const
854 {
855     MutexLocker locker(m_syncLock);
856     return m_syncDB.isOpen();
857 }
858
859 String IconDatabase::databasePath() const
860 {
861     MutexLocker locker(m_syncLock);
862     return m_completeDatabasePath.isolatedCopy();
863 }
864
865 String IconDatabase::defaultDatabaseFilename()
866 {
867     DEFINE_STATIC_LOCAL(String, defaultDatabaseFilename, ("WebpageIcons.db"));
868     return defaultDatabaseFilename.isolatedCopy();
869 }
870
871 // Unlike getOrCreatePageURLRecord(), getOrCreateIconRecord() does not mark the icon as "interested in import"
872 PassRefPtr<IconRecord> IconDatabase::getOrCreateIconRecord(const String& iconURL)
873 {
874     // Clients of getOrCreateIconRecord() are required to acquire the m_urlAndIconLock before calling this method
875     ASSERT(!m_urlAndIconLock.tryLock());
876
877     if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
878         return icon;
879
880     RefPtr<IconRecord> newIcon = IconRecord::create(iconURL);
881     newIcon->setMutexForVerifier(m_urlAndIconLock);
882     m_iconURLToRecordMap.set(iconURL, newIcon.get());
883
884     return newIcon.release();
885 }
886
887 // This method retrieves the existing PageURLRecord, or creates a new one and marks it as "interested in the import" for later notification
888 PageURLRecord* IconDatabase::getOrCreatePageURLRecord(const String& pageURL)
889 {
890     // Clients of getOrCreatePageURLRecord() are required to acquire the m_urlAndIconLock before calling this method
891     ASSERT(!m_urlAndIconLock.tryLock());
892
893     if (!documentCanHaveIcon(pageURL))
894         return 0;
895
896     PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
897     
898     MutexLocker locker(m_pendingReadingLock);
899     if (!m_iconURLImportComplete) {
900         // 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
901         if (!pageRecord) {
902             LOG(IconDatabase, "Creating new PageURLRecord for pageURL %s", urlForLogging(pageURL).ascii().data());
903             pageRecord = new PageURLRecord(pageURL);
904             m_pageURLToRecordMap.set(pageURL, pageRecord);
905         }
906
907         // 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
908         // Mark the URL as "interested in the result of the import" then bail
909         if (!pageRecord->iconRecord()) {
910             m_pageURLsPendingImport.add(pageURL);
911             return 0;
912         }
913     }
914
915     // We've done the initial import of all URLs known in the database.  If this record doesn't exist now, it never will    
916      return pageRecord;
917 }
918
919
920 // ************************
921 // *** Sync Thread Only ***
922 // ************************
923
924 void IconDatabase::importIconURLForPageURL(const String& iconURL, const String& pageURL)
925 {
926     ASSERT_ICON_SYNC_THREAD();
927     
928     // This function is only for setting actual existing url mappings so assert that neither of these URLs are empty
929     ASSERT(!iconURL.isEmpty());
930     ASSERT(!pageURL.isEmpty());
931     ASSERT(documentCanHaveIcon(pageURL));
932     
933     setIconURLForPageURLInSQLDatabase(iconURL, pageURL);    
934 }
935
936 void IconDatabase::importIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String& iconURL)
937 {
938     ASSERT_ICON_SYNC_THREAD();
939     
940     ASSERT(!iconURL.isEmpty());
941
942     writeIconSnapshotToSQLDatabase(IconSnapshot(iconURL, (int)currentTime(), data.get()));
943 }
944
945 bool IconDatabase::shouldStopThreadActivity() const
946 {
947     ASSERT_ICON_SYNC_THREAD();
948     
949     return m_threadTerminationRequested || m_removeIconsRequested;
950 }
951
952 void* IconDatabase::iconDatabaseSyncThreadStart(void* vIconDatabase)
953 {    
954     IconDatabase* iconDB = static_cast<IconDatabase*>(vIconDatabase);
955     
956     return iconDB->iconDatabaseSyncThread();
957 }
958
959 void* IconDatabase::iconDatabaseSyncThread()
960 {
961     // 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 
962     // to our thread structure hasn't been filled in yet.
963     // 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 
964     // prevent us from running before that call completes
965     m_syncLock.lock();
966     m_syncLock.unlock();
967
968     ASSERT_ICON_SYNC_THREAD();
969     
970     LOG(IconDatabase, "(THREAD) IconDatabase sync thread started");
971
972 #if !LOG_DISABLED
973     double startTime = currentTime();
974 #endif
975
976     // Need to create the database path if it doesn't already exist
977     makeAllDirectories(m_databaseDirectory);
978
979     // Existence of a journal file is evidence of a previous crash/force quit and automatically qualifies
980     // us to do an integrity check
981     String journalFilename = m_completeDatabasePath + "-journal";
982     if (!checkIntegrityOnOpen) {
983         AutodrainedPool pool;
984         checkIntegrityOnOpen = fileExists(journalFilename);
985     }
986     
987     {
988         MutexLocker locker(m_syncLock);
989         if (!m_syncDB.open(m_completeDatabasePath)) {
990             LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg());
991             return 0;
992         }
993     }
994     
995     if (shouldStopThreadActivity())
996         return syncThreadMainLoop();
997         
998 #if !LOG_DISABLED
999     double timeStamp = currentTime();
1000     LOG(IconDatabase, "(THREAD) Open took %.4f seconds", timeStamp - startTime);
1001 #endif    
1002
1003     performOpenInitialization();
1004     if (shouldStopThreadActivity())
1005         return syncThreadMainLoop();
1006         
1007 #if !LOG_DISABLED
1008     double newStamp = currentTime();
1009     LOG(IconDatabase, "(THREAD) performOpenInitialization() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
1010     timeStamp = newStamp;
1011 #endif 
1012
1013     if (!imported()) {
1014         LOG(IconDatabase, "(THREAD) Performing Safari2 import procedure");
1015         SQLiteTransaction importTransaction(m_syncDB);
1016         importTransaction.begin();
1017         
1018         // Commit the transaction only if the import completes (the import should be atomic)
1019         if (m_client->performImport()) {
1020             setImported(true);
1021             importTransaction.commit();
1022         } else {
1023             LOG(IconDatabase, "(THREAD) Safari 2 import was cancelled");
1024             importTransaction.rollback();
1025         }
1026         
1027         if (shouldStopThreadActivity())
1028             return syncThreadMainLoop();
1029             
1030 #if !LOG_DISABLED
1031         newStamp = currentTime();
1032         LOG(IconDatabase, "(THREAD) performImport() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
1033         timeStamp = newStamp;
1034 #endif 
1035     }
1036         
1037     // Uncomment the following line to simulate a long lasting URL import (*HUGE* icon databases, or network home directories)
1038     // while (currentTime() - timeStamp < 10);
1039
1040     // Read in URL mappings from the database          
1041     LOG(IconDatabase, "(THREAD) Starting iconURL import");
1042     performURLImport();
1043     
1044     if (shouldStopThreadActivity())
1045         return syncThreadMainLoop();
1046
1047 #if !LOG_DISABLED
1048     newStamp = currentTime();
1049     LOG(IconDatabase, "(THREAD) performURLImport() took %.4f seconds.  Entering main loop %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
1050 #endif 
1051
1052     LOG(IconDatabase, "(THREAD) Beginning sync");
1053     return syncThreadMainLoop();
1054 }
1055
1056 static int databaseVersionNumber(SQLiteDatabase& db)
1057 {
1058     return SQLiteStatement(db, "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';").getColumnInt(0);
1059 }
1060
1061 static bool isValidDatabase(SQLiteDatabase& db)
1062 {
1063     // These four tables should always exist in a valid db
1064     if (!db.tableExists("IconInfo") || !db.tableExists("IconData") || !db.tableExists("PageURL") || !db.tableExists("IconDatabaseInfo"))
1065         return false;
1066     
1067     if (databaseVersionNumber(db) < currentDatabaseVersion) {
1068         LOG(IconDatabase, "DB version is not found or below expected valid version");
1069         return false;
1070     }
1071     
1072     return true;
1073 }
1074
1075 static void createDatabaseTables(SQLiteDatabase& db)
1076 {
1077     if (!db.executeCommand("CREATE TABLE PageURL (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID INTEGER NOT NULL ON CONFLICT FAIL);")) {
1078         LOG_ERROR("Could not create PageURL table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
1079         db.close();
1080         return;
1081     }
1082     if (!db.executeCommand("CREATE INDEX PageURLIndex ON PageURL (url);")) {
1083         LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
1084         db.close();
1085         return;
1086     }
1087     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);")) {
1088         LOG_ERROR("Could not create IconInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
1089         db.close();
1090         return;
1091     }
1092     if (!db.executeCommand("CREATE INDEX IconInfoIndex ON IconInfo (url, iconID);")) {
1093         LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
1094         db.close();
1095         return;
1096     }
1097     if (!db.executeCommand("CREATE TABLE IconData (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, data BLOB);")) {
1098         LOG_ERROR("Could not create IconData table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
1099         db.close();
1100         return;
1101     }
1102     if (!db.executeCommand("CREATE INDEX IconDataIndex ON IconData (iconID);")) {
1103         LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
1104         db.close();
1105         return;
1106     }
1107     if (!db.executeCommand("CREATE TABLE IconDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
1108         LOG_ERROR("Could not create IconDatabaseInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
1109         db.close();
1110         return;
1111     }
1112     if (!db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) {
1113         LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", db.lastError(), db.lastErrorMsg());
1114         db.close();
1115         return;
1116     }
1117 }    
1118
1119 void IconDatabase::performOpenInitialization()
1120 {
1121     ASSERT_ICON_SYNC_THREAD();
1122     
1123     if (!isOpen())
1124         return;
1125     
1126     if (checkIntegrityOnOpen) {
1127         checkIntegrityOnOpen = false;
1128         if (!checkIntegrity()) {
1129             LOG(IconDatabase, "Integrity check was bad - dumping IconDatabase");
1130
1131             m_syncDB.close();
1132             
1133             {
1134                 MutexLocker locker(m_syncLock);
1135                 // Should've been consumed by SQLite, delete just to make sure we don't see it again in the future;
1136                 deleteFile(m_completeDatabasePath + "-journal");
1137                 deleteFile(m_completeDatabasePath);
1138             }
1139             
1140             // Reopen the write database, creating it from scratch
1141             if (!m_syncDB.open(m_completeDatabasePath)) {
1142                 LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg());
1143                 return;
1144             }          
1145         }
1146     }
1147     
1148     int version = databaseVersionNumber(m_syncDB);
1149     
1150     if (version > currentDatabaseVersion) {
1151         LOG(IconDatabase, "Database version number %i is greater than our current version number %i - closing the database to prevent overwriting newer versions", version, currentDatabaseVersion);
1152         m_syncDB.close();
1153         m_threadTerminationRequested = true;
1154         return;
1155     }
1156     
1157     if (!isValidDatabase(m_syncDB)) {
1158         LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", m_completeDatabasePath.ascii().data());
1159         m_syncDB.clearAllTables();
1160         createDatabaseTables(m_syncDB);
1161     }
1162
1163     // Reduce sqlite RAM cache size from default 2000 pages (~1.5kB per page). 3MB of cache for icon database is overkill
1164     if (!SQLiteStatement(m_syncDB, "PRAGMA cache_size = 200;").executeCommand())         
1165         LOG_ERROR("SQLite database could not set cache_size");
1166
1167     // Tell backup software (i.e., Time Machine) to never back up the icon database, because  
1168     // it's a large file that changes frequently, thus using a lot of backup disk space, and 
1169     // it's unlikely that many users would be upset about it not being backed up. We could 
1170     // make this configurable on a per-client basis some day if some clients don't want this.
1171     if (canExcludeFromBackup() && !wasExcludedFromBackup() && excludeFromBackup(m_completeDatabasePath))
1172         setWasExcludedFromBackup();
1173 }
1174
1175 bool IconDatabase::checkIntegrity()
1176 {
1177     ASSERT_ICON_SYNC_THREAD();
1178     
1179     SQLiteStatement integrity(m_syncDB, "PRAGMA integrity_check;");
1180     if (integrity.prepare() != SQLResultOk) {
1181         LOG_ERROR("checkIntegrity failed to execute");
1182         return false;
1183     }
1184     
1185     int resultCode = integrity.step();
1186     if (resultCode == SQLResultOk)
1187         return true;
1188         
1189     if (resultCode != SQLResultRow)
1190         return false;
1191
1192     int columns = integrity.columnCount();
1193     if (columns != 1) {
1194         LOG_ERROR("Received %i columns performing integrity check, should be 1", columns);
1195         return false;
1196     }
1197         
1198     String resultText = integrity.getColumnText(0);
1199         
1200     // A successful, no-error integrity check will be "ok" - all other strings imply failure
1201     if (resultText == "ok")
1202         return true;
1203     
1204     LOG_ERROR("Icon database integrity check failed - \n%s", resultText.ascii().data());
1205     return false;
1206 }
1207
1208 void IconDatabase::performURLImport()
1209 {
1210     ASSERT_ICON_SYNC_THREAD();
1211
1212     SQLiteStatement query(m_syncDB, "SELECT PageURL.url, IconInfo.url, IconInfo.stamp FROM PageURL INNER JOIN IconInfo ON PageURL.iconID=IconInfo.iconID;");
1213     
1214     if (query.prepare() != SQLResultOk) {
1215         LOG_ERROR("Unable to prepare icon url import query");
1216         return;
1217     }
1218     
1219     // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
1220     // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up
1221     AutodrainedPool pool(25);
1222         
1223     int result = query.step();
1224     while (result == SQLResultRow) {
1225         String pageURL = query.getColumnText(0);
1226         String iconURL = query.getColumnText(1);
1227
1228         {
1229             MutexLocker locker(m_urlAndIconLock);
1230             
1231             PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
1232             
1233             // If the pageRecord doesn't exist in this map, then no one has retained this pageURL
1234             // If the s_databaseCleanupCounter count is non-zero, then we're not supposed to be pruning the database in any manner,
1235             // so go ahead and actually create a pageURLRecord for this url even though it's not retained.
1236             // If database cleanup *is* allowed, we don't want to bother pulling in a page url from disk that noone is actually interested
1237             // in - we'll prune it later instead!
1238             if (!pageRecord && databaseCleanupCounter && documentCanHaveIcon(pageURL)) {
1239                 pageRecord = new PageURLRecord(pageURL);
1240                 m_pageURLToRecordMap.set(pageURL, pageRecord);
1241             }
1242             
1243             if (pageRecord) {
1244                 IconRecord* currentIcon = pageRecord->iconRecord();
1245
1246                 if (!currentIcon || currentIcon->iconURL() != iconURL) {
1247                     pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
1248                     currentIcon = pageRecord->iconRecord();
1249                 }
1250             
1251                 // Regardless, the time stamp from disk still takes precedence.  Until we read this icon from disk, we didn't think we'd seen it before
1252                 // so we marked the timestamp as "now", but it's really much older
1253                 currentIcon->setTimestamp(query.getColumnInt(2));
1254             }            
1255         }
1256         
1257         // FIXME: Currently the WebKit API supports 1 type of notification that is sent whenever we get an Icon URL for a Page URL.  We might want to re-purpose it to work for 
1258         // getting the actually icon itself also (so each pageurl would get this notification twice) or we might want to add a second type of notification -
1259         // one for the URL and one for the Image itself
1260         // Note that WebIconDatabase is not neccessarily API so we might be able to make this change
1261         {
1262             MutexLocker locker(m_pendingReadingLock);
1263             if (m_pageURLsPendingImport.contains(pageURL)) {
1264                 dispatchDidImportIconURLForPageURLOnMainThread(pageURL);
1265                 m_pageURLsPendingImport.remove(pageURL);
1266             
1267                 pool.cycle();
1268             }
1269         }
1270         
1271         // Stop the import at any time of the thread has been asked to shutdown
1272         if (shouldStopThreadActivity()) {
1273             LOG(IconDatabase, "IconDatabase asked to terminate during performURLImport()");
1274             return;
1275         }
1276         
1277         result = query.step();
1278     }
1279     
1280     if (result != SQLResultDone)
1281         LOG(IconDatabase, "Error reading page->icon url mappings from database");
1282
1283     // Clear the m_pageURLsPendingImport set - either the page URLs ended up with an iconURL (that we'll notify about) or not, 
1284     // but after m_iconURLImportComplete is set to true, we don't care about this set anymore
1285     Vector<String> urls;
1286     {
1287         MutexLocker locker(m_pendingReadingLock);
1288
1289         urls.appendRange(m_pageURLsPendingImport.begin(), m_pageURLsPendingImport.end());
1290         m_pageURLsPendingImport.clear();        
1291         m_iconURLImportComplete = true;
1292     }
1293     
1294     Vector<String> urlsToNotify;
1295     
1296     // Loop through the urls pending import
1297     // Remove unretained ones if database cleanup is allowed
1298     // Keep a set of ones that are retained and pending notification
1299     {
1300         MutexLocker locker(m_urlAndIconLock);
1301         
1302         for (unsigned i = 0; i < urls.size(); ++i) {
1303             if (!m_retainedPageURLs.contains(urls[i])) {
1304                 PageURLRecord* record = m_pageURLToRecordMap.get(urls[i]);
1305                 if (record && !databaseCleanupCounter) {
1306                     m_pageURLToRecordMap.remove(urls[i]);
1307                     IconRecord* iconRecord = record->iconRecord();
1308                     
1309                     // If this page is the only remaining retainer of its icon, mark that icon for deletion and don't bother
1310                     // reading anything related to it 
1311                     if (iconRecord && iconRecord->hasOneRef()) {
1312                         m_iconURLToRecordMap.remove(iconRecord->iconURL());
1313                         
1314                         {
1315                             MutexLocker locker(m_pendingReadingLock);
1316                             m_pageURLsInterestedInIcons.remove(urls[i]);
1317                             m_iconsPendingReading.remove(iconRecord);
1318                         }
1319                         {
1320                             MutexLocker locker(m_pendingSyncLock);
1321                             m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));                    
1322                         }
1323                     }
1324                     
1325                     delete record;
1326                 }
1327             } else {
1328                 urlsToNotify.append(urls[i]);
1329             }
1330         }
1331     }
1332
1333     LOG(IconDatabase, "Notifying %lu interested page URLs that their icon URL is known due to the import", static_cast<unsigned long>(urlsToNotify.size()));
1334     // Now that we don't hold any locks, perform the actual notifications
1335     for (unsigned i = 0; i < urlsToNotify.size(); ++i) {
1336         LOG(IconDatabase, "Notifying icon info known for pageURL %s", urlsToNotify[i].ascii().data());
1337         dispatchDidImportIconURLForPageURLOnMainThread(urlsToNotify[i]);
1338         if (shouldStopThreadActivity())
1339             return;
1340
1341         pool.cycle();
1342     }
1343     
1344     // Notify the client that the URL import is complete in case it's managing its own pending notifications.
1345     dispatchDidFinishURLImportOnMainThread();
1346     
1347     // Notify all DocumentLoaders that were waiting for an icon load decision on the main thread
1348     callOnMainThread(notifyPendingLoadDecisionsOnMainThread, this);
1349 }
1350
1351 void* IconDatabase::syncThreadMainLoop()
1352 {
1353     ASSERT_ICON_SYNC_THREAD();
1354
1355     m_syncLock.lock();
1356
1357     bool shouldReenableSuddenTermination = m_disabledSuddenTerminationForSyncThread;
1358     m_disabledSuddenTerminationForSyncThread = false;
1359
1360     // We'll either do any pending work on our first pass through the loop, or we'll terminate
1361     // without doing any work. Either way we're dealing with any currently-pending work.
1362     m_syncThreadHasWorkToDo = false;
1363
1364     // It's possible thread termination is requested before the main loop even starts - in that case, just skip straight to cleanup
1365     while (!m_threadTerminationRequested) {
1366         m_syncLock.unlock();
1367
1368 #if !LOG_DISABLED
1369         double timeStamp = currentTime();
1370 #endif
1371         LOG(IconDatabase, "(THREAD) Main work loop starting");
1372
1373         // If we should remove all icons, do it now.  This is an uninteruptible procedure that we will always do before quitting if it is requested
1374         if (m_removeIconsRequested) {
1375             removeAllIconsOnThread();
1376             m_removeIconsRequested = false;
1377         }
1378         
1379         // Then, if the thread should be quitting, quit now!
1380         if (m_threadTerminationRequested)
1381             break;
1382         
1383         bool didAnyWork = true;
1384         while (didAnyWork) {
1385             bool didWrite = writeToDatabase();
1386             if (shouldStopThreadActivity())
1387                 break;
1388                 
1389             didAnyWork = readFromDatabase();
1390             if (shouldStopThreadActivity())
1391                 break;
1392                 
1393             // Prune unretained icons after the first time we sync anything out to the database
1394             // This way, pruning won't be the only operation we perform to the database by itself
1395             // We also don't want to bother doing this if the thread should be terminating (the user is quitting)
1396             // or if private browsing is enabled
1397             // We also don't want to prune if the m_databaseCleanupCounter count is non-zero - that means someone
1398             // has asked to delay pruning
1399             static bool prunedUnretainedIcons = false;
1400             if (didWrite && !m_privateBrowsingEnabled && !prunedUnretainedIcons && !databaseCleanupCounter) {
1401 #if !LOG_DISABLED
1402                 double time = currentTime();
1403 #endif
1404                 LOG(IconDatabase, "(THREAD) Starting pruneUnretainedIcons()");
1405                 
1406                 pruneUnretainedIcons();
1407                 
1408                 LOG(IconDatabase, "(THREAD) pruneUnretainedIcons() took %.4f seconds", currentTime() - time);
1409                 
1410                 // If pruneUnretainedIcons() returned early due to requested thread termination, its still okay
1411                 // to mark prunedUnretainedIcons true because we're about to terminate anyway
1412                 prunedUnretainedIcons = true;
1413             }
1414             
1415             didAnyWork = didAnyWork || didWrite;
1416             if (shouldStopThreadActivity())
1417                 break;
1418         }
1419         
1420 #if !LOG_DISABLED
1421         double newstamp = currentTime();
1422         LOG(IconDatabase, "(THREAD) Main work loop ran for %.4f seconds, %s requested to terminate", newstamp - timeStamp, shouldStopThreadActivity() ? "was" : "was not");
1423 #endif
1424                     
1425         m_syncLock.lock();
1426         
1427         // There is some condition that is asking us to stop what we're doing now and handle a special case
1428         // This is either removing all icons, or shutting down the thread to quit the app
1429         // We handle those at the top of this main loop so continue to jump back up there
1430         if (shouldStopThreadActivity())
1431             continue;
1432
1433         if (shouldReenableSuddenTermination) {
1434             // The following is balanced by the call to disableSuddenTermination in the
1435             // wakeSyncThread function. Any time we wait on the condition, we also have
1436             // to enableSuddenTermation, after doing the next batch of work.
1437             enableSuddenTermination();
1438         }
1439
1440         while (!m_syncThreadHasWorkToDo)
1441             m_syncCondition.wait(m_syncLock);
1442
1443         m_syncThreadHasWorkToDo = false;
1444
1445         ASSERT(m_disabledSuddenTerminationForSyncThread);
1446         shouldReenableSuddenTermination = true;
1447         m_disabledSuddenTerminationForSyncThread = false;
1448     }
1449
1450     m_syncLock.unlock();
1451     
1452     // Thread is terminating at this point
1453     cleanupSyncThread();
1454
1455     if (shouldReenableSuddenTermination) {
1456         // The following is balanced by the call to disableSuddenTermination in the
1457         // wakeSyncThread function. Any time we wait on the condition, we also have
1458         // to enableSuddenTermation, after doing the next batch of work.
1459         enableSuddenTermination();
1460
1461         MutexLocker locker(m_syncLock);
1462         m_disabledSuddenTerminationForSyncThread = false;
1463     }
1464
1465     return 0;
1466 }
1467
1468 bool IconDatabase::readFromDatabase()
1469 {
1470     ASSERT_ICON_SYNC_THREAD();
1471     
1472 #if !LOG_DISABLED
1473     double timeStamp = currentTime();
1474 #endif
1475
1476     bool didAnyWork = false;
1477
1478     // We'll make a copy of the sets of things that need to be read.  Then we'll verify at the time of updating the record that it still wants to be updated
1479     // This way we won't hold the lock for a long period of time
1480     Vector<IconRecord*> icons;
1481     {
1482         MutexLocker locker(m_pendingReadingLock);
1483         icons.appendRange(m_iconsPendingReading.begin(), m_iconsPendingReading.end());
1484     }
1485     
1486     // Keep track of icons we actually read to notify them of the new icon    
1487     HashSet<String> urlsToNotify;
1488     
1489     for (unsigned i = 0; i < icons.size(); ++i) {
1490         didAnyWork = true;
1491         RefPtr<SharedBuffer> imageData = getImageDataForIconURLFromSQLDatabase(icons[i]->iconURL());
1492         imageData->setMutexForVerifier(m_urlAndIconLock);
1493
1494         // Verify this icon still wants to be read from disk
1495         {
1496             MutexLocker urlLocker(m_urlAndIconLock);
1497             {
1498                 MutexLocker readLocker(m_pendingReadingLock);
1499                 
1500                 if (m_iconsPendingReading.contains(icons[i])) {
1501                     // Set the new data
1502                     icons[i]->setImageData(imageData.release());
1503                     
1504                     // Remove this icon from the set that needs to be read
1505                     m_iconsPendingReading.remove(icons[i]);
1506                     
1507                     // We have a set of all Page URLs that retain this icon as well as all PageURLs waiting for an icon
1508                     // We want to find the intersection of these two sets to notify them
1509                     // Check the sizes of these two sets to minimize the number of iterations
1510                     const HashSet<String>* outerHash;
1511                     const HashSet<String>* innerHash;
1512                     
1513                     if (icons[i]->retainingPageURLs().size() > m_pageURLsInterestedInIcons.size()) {
1514                         outerHash = &m_pageURLsInterestedInIcons;
1515                         innerHash = &(icons[i]->retainingPageURLs());
1516                     } else {
1517                         innerHash = &m_pageURLsInterestedInIcons;
1518                         outerHash = &(icons[i]->retainingPageURLs());
1519                     }
1520                     
1521                     HashSet<String>::const_iterator iter = outerHash->begin();
1522                     HashSet<String>::const_iterator end = outerHash->end();
1523                     for (; iter != end; ++iter) {
1524                         if (innerHash->contains(*iter)) {
1525                             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());
1526                             urlsToNotify.add(*iter);
1527                         }
1528                         
1529                         // If we ever get to the point were we've seen every url interested in this icon, break early
1530                         if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size())
1531                             break;
1532                     }
1533                     
1534                     // We don't need to notify a PageURL twice, so all the ones we're about to notify can be removed from the interested set
1535                     if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size())
1536                         m_pageURLsInterestedInIcons.clear();
1537                     else {
1538                         iter = urlsToNotify.begin();
1539                         end = urlsToNotify.end();
1540                         for (; iter != end; ++iter)
1541                             m_pageURLsInterestedInIcons.remove(*iter);
1542                     }
1543                 }
1544             }
1545         }
1546     
1547         if (shouldStopThreadActivity())
1548             return didAnyWork;
1549         
1550         // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
1551         // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up
1552         AutodrainedPool pool(25);
1553
1554         // Now that we don't hold any locks, perform the actual notifications
1555         HashSet<String>::iterator iter = urlsToNotify.begin();
1556         HashSet<String>::iterator end = urlsToNotify.end();
1557         for (unsigned iteration = 0; iter != end; ++iter, ++iteration) {
1558             LOG(IconDatabase, "Notifying icon received for pageURL %s", urlForLogging(*iter).ascii().data());
1559             dispatchDidImportIconDataForPageURLOnMainThread(*iter);
1560             if (shouldStopThreadActivity())
1561                 return didAnyWork;
1562             
1563             pool.cycle();
1564         }
1565
1566         LOG(IconDatabase, "Done notifying %i pageURLs who just received their icons", urlsToNotify.size());
1567         urlsToNotify.clear();
1568         
1569         if (shouldStopThreadActivity())
1570             return didAnyWork;
1571     }
1572
1573     LOG(IconDatabase, "Reading from database took %.4f seconds", currentTime() - timeStamp);
1574
1575     return didAnyWork;
1576 }
1577
1578 bool IconDatabase::writeToDatabase()
1579 {
1580     ASSERT_ICON_SYNC_THREAD();
1581
1582 #if !LOG_DISABLED
1583     double timeStamp = currentTime();
1584 #endif
1585
1586     bool didAnyWork = false;
1587     
1588     // We can copy the current work queue then clear it out - If any new work comes in while we're writing out,
1589     // we'll pick it up on the next pass.  This greatly simplifies the locking strategy for this method and remains cohesive with changes
1590     // asked for by the database on the main thread
1591     {
1592         MutexLocker locker(m_urlAndIconLock);
1593         Vector<IconSnapshot> iconSnapshots;
1594         Vector<PageURLSnapshot> pageSnapshots;
1595         {
1596             MutexLocker locker(m_pendingSyncLock);
1597
1598             iconSnapshots.appendRange(m_iconsPendingSync.begin().values(), m_iconsPendingSync.end().values());
1599             m_iconsPendingSync.clear();
1600
1601             pageSnapshots.appendRange(m_pageURLsPendingSync.begin().values(), m_pageURLsPendingSync.end().values());
1602             m_pageURLsPendingSync.clear();
1603         }
1604
1605         if (iconSnapshots.size() || pageSnapshots.size())
1606             didAnyWork = true;
1607
1608         SQLiteTransaction syncTransaction(m_syncDB);
1609         syncTransaction.begin();
1610
1611         for (unsigned i = 0; i < iconSnapshots.size(); ++i) {
1612             writeIconSnapshotToSQLDatabase(iconSnapshots[i]);
1613             LOG(IconDatabase, "Wrote IconRecord for IconURL %s with timeStamp of %i to the DB", urlForLogging(iconSnapshots[i].iconURL()).ascii().data(), iconSnapshots[i].timestamp());
1614         }
1615
1616         for (unsigned i = 0; i < pageSnapshots.size(); ++i) {
1617             // If the icon URL is empty, this page is meant to be deleted
1618             // ASSERTs are sanity checks to make sure the mappings exist if they should and don't if they shouldn't
1619             if (pageSnapshots[i].iconURL().isEmpty())
1620                 removePageURLFromSQLDatabase(pageSnapshots[i].pageURL());
1621             else
1622                 setIconURLForPageURLInSQLDatabase(pageSnapshots[i].iconURL(), pageSnapshots[i].pageURL());
1623             LOG(IconDatabase, "Committed IconURL for PageURL %s to database", urlForLogging(pageSnapshots[i].pageURL()).ascii().data());
1624         }
1625
1626         syncTransaction.commit();
1627     }
1628
1629     // Check to make sure there are no dangling PageURLs - If there are, we want to output one log message but not spam the console potentially every few seconds
1630     if (didAnyWork)
1631         checkForDanglingPageURLs(false);
1632
1633     LOG(IconDatabase, "Updating the database took %.4f seconds", currentTime() - timeStamp);
1634
1635     return didAnyWork;
1636 }
1637
1638 void IconDatabase::pruneUnretainedIcons()
1639 {
1640     ASSERT_ICON_SYNC_THREAD();
1641
1642     if (!isOpen())
1643         return;        
1644     
1645     // This method should only be called once per run
1646     ASSERT(!m_initialPruningComplete);
1647
1648     // This method relies on having read in all page URLs from the database earlier.
1649     ASSERT(m_iconURLImportComplete);
1650
1651     // Get the known PageURLs from the db, and record the ID of any that are not in the retain count set.
1652     Vector<int64_t> pageIDsToDelete; 
1653
1654     SQLiteStatement pageSQL(m_syncDB, "SELECT rowid, url FROM PageURL;");
1655     pageSQL.prepare();
1656     
1657     int result;
1658     while ((result = pageSQL.step()) == SQLResultRow) {
1659         MutexLocker locker(m_urlAndIconLock);
1660         if (!m_pageURLToRecordMap.contains(pageSQL.getColumnText(1)))
1661             pageIDsToDelete.append(pageSQL.getColumnInt64(0));
1662     }
1663     
1664     if (result != SQLResultDone)
1665         LOG_ERROR("Error reading PageURL table from on-disk DB");
1666     pageSQL.finalize();
1667     
1668     // Delete page URLs that were in the table, but not in our retain count set.
1669     size_t numToDelete = pageIDsToDelete.size();
1670     if (numToDelete) {
1671         SQLiteTransaction pruningTransaction(m_syncDB);
1672         pruningTransaction.begin();
1673         
1674         SQLiteStatement pageDeleteSQL(m_syncDB, "DELETE FROM PageURL WHERE rowid = (?);");
1675         pageDeleteSQL.prepare();
1676         for (size_t i = 0; i < numToDelete; ++i) {
1677 #if OS(WINDOWS)
1678             LOG(IconDatabase, "Pruning page with rowid %I64i from disk", static_cast<long long>(pageIDsToDelete[i]));
1679 #else
1680             LOG(IconDatabase, "Pruning page with rowid %lli from disk", static_cast<long long>(pageIDsToDelete[i]));
1681 #endif
1682             pageDeleteSQL.bindInt64(1, pageIDsToDelete[i]);
1683             int result = pageDeleteSQL.step();
1684             if (result != SQLResultDone)
1685 #if OS(WINDOWS)
1686                 LOG_ERROR("Unabled to delete page with id %I64i from disk", static_cast<long long>(pageIDsToDelete[i]));
1687 #else
1688                 LOG_ERROR("Unabled to delete page with id %lli from disk", static_cast<long long>(pageIDsToDelete[i]));
1689 #endif
1690             pageDeleteSQL.reset();
1691             
1692             // If the thread was asked to terminate, we should commit what pruning we've done so far, figuring we can
1693             // finish the rest later (hopefully)
1694             if (shouldStopThreadActivity()) {
1695                 pruningTransaction.commit();
1696                 return;
1697             }
1698         }
1699         pruningTransaction.commit();
1700         pageDeleteSQL.finalize();
1701     }
1702     
1703     // Deleting unreferenced icons from the Icon tables has to be atomic - 
1704     // If the user quits while these are taking place, they might have to wait.  Thankfully this will rarely be an issue
1705     // A user on a network home directory with a wildly inconsistent database might see quite a pause...
1706
1707     SQLiteTransaction pruningTransaction(m_syncDB);
1708     pruningTransaction.begin();
1709     
1710     // Wipe Icons that aren't retained
1711     if (!m_syncDB.executeCommand("DELETE FROM IconData WHERE iconID NOT IN (SELECT iconID FROM PageURL);"))
1712         LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk IconData table");    
1713     if (!m_syncDB.executeCommand("DELETE FROM IconInfo WHERE iconID NOT IN (SELECT iconID FROM PageURL);"))
1714         LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk IconInfo table");    
1715     
1716     pruningTransaction.commit();
1717         
1718     checkForDanglingPageURLs(true);
1719
1720     m_initialPruningComplete = true;
1721 }
1722
1723 void IconDatabase::checkForDanglingPageURLs(bool pruneIfFound)
1724 {
1725     ASSERT_ICON_SYNC_THREAD();
1726
1727     // This check can be relatively expensive so we don't do it in a release build unless the caller has asked us to prune any dangling
1728     // entries.  We also don't want to keep performing this check and reporting this error if it has already found danglers before so we
1729     // keep track of whether we've found any.  We skip the check in the release build pretending to have already found danglers already.
1730 #ifndef NDEBUG
1731     static bool danglersFound = true;
1732 #else
1733     static bool danglersFound = false;
1734 #endif
1735
1736     if ((pruneIfFound || !danglersFound) && SQLiteStatement(m_syncDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM IconInfo) LIMIT 1;").returnsAtLeastOneResult()) {
1737         danglersFound = true;
1738         LOG(IconDatabase, "Dangling PageURL entries found");
1739         if (pruneIfFound && !m_syncDB.executeCommand("DELETE FROM PageURL WHERE iconID NOT IN (SELECT iconID FROM IconInfo);"))
1740             LOG(IconDatabase, "Unable to prune dangling PageURLs");
1741     }
1742 }
1743
1744 void IconDatabase::removeAllIconsOnThread()
1745 {
1746     ASSERT_ICON_SYNC_THREAD();
1747
1748     LOG(IconDatabase, "Removing all icons on the sync thread");
1749         
1750     // Delete all the prepared statements so they can start over
1751     deleteAllPreparedStatements();    
1752     
1753     // To reset the on-disk database, we'll wipe all its tables then vacuum it
1754     // This is easier and safer than closing it, deleting the file, and recreating from scratch
1755     m_syncDB.clearAllTables();
1756     m_syncDB.runVacuumCommand();
1757     createDatabaseTables(m_syncDB);
1758     
1759     LOG(IconDatabase, "Dispatching notification that we removed all icons");
1760     dispatchDidRemoveAllIconsOnMainThread();    
1761 }
1762
1763 void IconDatabase::deleteAllPreparedStatements()
1764 {
1765     ASSERT_ICON_SYNC_THREAD();
1766     
1767     m_setIconIDForPageURLStatement.clear();
1768     m_removePageURLStatement.clear();
1769     m_getIconIDForIconURLStatement.clear();
1770     m_getImageDataForIconURLStatement.clear();
1771     m_addIconToIconInfoStatement.clear();
1772     m_addIconToIconDataStatement.clear();
1773     m_getImageDataStatement.clear();
1774     m_deletePageURLsForIconURLStatement.clear();
1775     m_deleteIconFromIconInfoStatement.clear();
1776     m_deleteIconFromIconDataStatement.clear();
1777     m_updateIconInfoStatement.clear();
1778     m_updateIconDataStatement.clear();
1779     m_setIconInfoStatement.clear();
1780     m_setIconDataStatement.clear();
1781 }
1782
1783 void* IconDatabase::cleanupSyncThread()
1784 {
1785     ASSERT_ICON_SYNC_THREAD();
1786     
1787 #if !LOG_DISABLED
1788     double timeStamp = currentTime();
1789 #endif 
1790
1791     // If the removeIcons flag is set, remove all icons from the db.
1792     if (m_removeIconsRequested)
1793         removeAllIconsOnThread();
1794
1795     // Sync remaining icons out
1796     LOG(IconDatabase, "(THREAD) Doing final writeout and closure of sync thread");
1797     writeToDatabase();
1798     
1799     // Close the database
1800     MutexLocker locker(m_syncLock);
1801     
1802     m_databaseDirectory = String();
1803     m_completeDatabasePath = String();
1804     deleteAllPreparedStatements();    
1805     m_syncDB.close();
1806     
1807 #if !LOG_DISABLED
1808     LOG(IconDatabase, "(THREAD) Final closure took %.4f seconds", currentTime() - timeStamp);
1809 #endif
1810     
1811     m_syncThreadRunning = false;
1812     return 0;
1813 }
1814
1815 bool IconDatabase::imported()
1816 {
1817     ASSERT_ICON_SYNC_THREAD();
1818     
1819     if (m_isImportedSet)
1820         return m_imported;
1821         
1822     SQLiteStatement query(m_syncDB, "SELECT IconDatabaseInfo.value FROM IconDatabaseInfo WHERE IconDatabaseInfo.key = \"ImportedSafari2Icons\";");
1823     if (query.prepare() != SQLResultOk) {
1824         LOG_ERROR("Unable to prepare imported statement");
1825         return false;
1826     }
1827     
1828     int result = query.step();
1829     if (result == SQLResultRow)
1830         result = query.getColumnInt(0);
1831     else {
1832         if (result != SQLResultDone)
1833             LOG_ERROR("imported statement failed");
1834         result = 0;
1835     }
1836     
1837     m_isImportedSet = true;
1838     return m_imported = result;
1839 }
1840
1841 void IconDatabase::setImported(bool import)
1842 {
1843     ASSERT_ICON_SYNC_THREAD();
1844
1845     m_imported = import;
1846     m_isImportedSet = true;
1847     
1848     String queryString = import ?
1849         "INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 1);" :
1850         "INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 0);";
1851         
1852     SQLiteStatement query(m_syncDB, queryString);
1853     
1854     if (query.prepare() != SQLResultOk) {
1855         LOG_ERROR("Unable to prepare set imported statement");
1856         return;
1857     }    
1858     
1859     if (query.step() != SQLResultDone)
1860         LOG_ERROR("set imported statement failed");
1861 }
1862
1863 // readySQLiteStatement() handles two things
1864 // 1 - If the SQLDatabase& argument is different, the statement must be destroyed and remade.  This happens when the user
1865 //     switches to and from private browsing
1866 // 2 - Lazy construction of the Statement in the first place, in case we've never made this query before
1867 inline void readySQLiteStatement(OwnPtr<SQLiteStatement>& statement, SQLiteDatabase& db, const String& str)
1868 {
1869     if (statement && (statement->database() != &db || statement->isExpired())) {
1870         if (statement->isExpired())
1871             LOG(IconDatabase, "SQLiteStatement associated with %s is expired", str.ascii().data());
1872         statement.clear();
1873     }
1874     if (!statement) {
1875         statement = adoptPtr(new SQLiteStatement(db, str));
1876         if (statement->prepare() != SQLResultOk)
1877             LOG_ERROR("Preparing statement %s failed", str.ascii().data());
1878     }
1879 }
1880
1881 void IconDatabase::setIconURLForPageURLInSQLDatabase(const String& iconURL, const String& pageURL)
1882 {
1883     ASSERT_ICON_SYNC_THREAD();
1884     
1885     int64_t iconID = getIconIDForIconURLFromSQLDatabase(iconURL);
1886
1887     if (!iconID)
1888         iconID = addIconURLToSQLDatabase(iconURL);
1889     
1890     if (!iconID) {
1891         LOG_ERROR("Failed to establish an ID for iconURL %s", urlForLogging(iconURL).ascii().data());
1892         ASSERT(false);
1893         return;
1894     }
1895     
1896     setIconIDForPageURLInSQLDatabase(iconID, pageURL);
1897 }
1898
1899 void IconDatabase::setIconIDForPageURLInSQLDatabase(int64_t iconID, const String& pageURL)
1900 {
1901     ASSERT_ICON_SYNC_THREAD();
1902     
1903     readySQLiteStatement(m_setIconIDForPageURLStatement, m_syncDB, "INSERT INTO PageURL (url, iconID) VALUES ((?), ?);");
1904     m_setIconIDForPageURLStatement->bindText(1, pageURL);
1905     m_setIconIDForPageURLStatement->bindInt64(2, iconID);
1906
1907     int result = m_setIconIDForPageURLStatement->step();
1908     if (result != SQLResultDone) {
1909         ASSERT(false);
1910         LOG_ERROR("setIconIDForPageURLQuery failed for url %s", urlForLogging(pageURL).ascii().data());
1911     }
1912
1913     m_setIconIDForPageURLStatement->reset();
1914 }
1915
1916 void IconDatabase::removePageURLFromSQLDatabase(const String& pageURL)
1917 {
1918     ASSERT_ICON_SYNC_THREAD();
1919     
1920     readySQLiteStatement(m_removePageURLStatement, m_syncDB, "DELETE FROM PageURL WHERE url = (?);");
1921     m_removePageURLStatement->bindText(1, pageURL);
1922
1923     if (m_removePageURLStatement->step() != SQLResultDone)
1924         LOG_ERROR("removePageURLFromSQLDatabase failed for url %s", urlForLogging(pageURL).ascii().data());
1925     
1926     m_removePageURLStatement->reset();
1927 }
1928
1929
1930 int64_t IconDatabase::getIconIDForIconURLFromSQLDatabase(const String& iconURL)
1931 {
1932     ASSERT_ICON_SYNC_THREAD();
1933     
1934     readySQLiteStatement(m_getIconIDForIconURLStatement, m_syncDB, "SELECT IconInfo.iconID FROM IconInfo WHERE IconInfo.url = (?);");
1935     m_getIconIDForIconURLStatement->bindText(1, iconURL);
1936     
1937     int64_t result = m_getIconIDForIconURLStatement->step();
1938     if (result == SQLResultRow)
1939         result = m_getIconIDForIconURLStatement->getColumnInt64(0);
1940     else {
1941         if (result != SQLResultDone)
1942             LOG_ERROR("getIconIDForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data());
1943         result = 0;
1944     }
1945
1946     m_getIconIDForIconURLStatement->reset();
1947     return result;
1948 }
1949
1950 int64_t IconDatabase::addIconURLToSQLDatabase(const String& iconURL)
1951 {
1952     ASSERT_ICON_SYNC_THREAD();
1953     
1954     // There would be a transaction here to make sure these two inserts are atomic
1955     // In practice the only caller of this method is always wrapped in a transaction itself so placing another
1956     // here is unnecessary
1957     
1958     readySQLiteStatement(m_addIconToIconInfoStatement, m_syncDB, "INSERT INTO IconInfo (url, stamp) VALUES (?, 0);");
1959     m_addIconToIconInfoStatement->bindText(1, iconURL);
1960     
1961     int result = m_addIconToIconInfoStatement->step();
1962     m_addIconToIconInfoStatement->reset();
1963     if (result != SQLResultDone) {
1964         LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconInfo", urlForLogging(iconURL).ascii().data());
1965         return 0;
1966     }
1967     int64_t iconID = m_syncDB.lastInsertRowID();
1968     
1969     readySQLiteStatement(m_addIconToIconDataStatement, m_syncDB, "INSERT INTO IconData (iconID, data) VALUES (?, ?);");
1970     m_addIconToIconDataStatement->bindInt64(1, iconID);
1971     
1972     result = m_addIconToIconDataStatement->step();
1973     m_addIconToIconDataStatement->reset();
1974     if (result != SQLResultDone) {
1975         LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconData", urlForLogging(iconURL).ascii().data());
1976         return 0;
1977     }
1978     
1979     return iconID;
1980 }
1981
1982 PassRefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(const String& iconURL)
1983 {
1984     ASSERT_ICON_SYNC_THREAD();
1985     
1986     RefPtr<SharedBuffer> imageData;
1987     
1988     readySQLiteStatement(m_getImageDataForIconURLStatement, m_syncDB, "SELECT IconData.data FROM IconData WHERE IconData.iconID IN (SELECT iconID FROM IconInfo WHERE IconInfo.url = (?));");
1989     m_getImageDataForIconURLStatement->bindText(1, iconURL);
1990     
1991     int result = m_getImageDataForIconURLStatement->step();
1992     if (result == SQLResultRow) {
1993         Vector<char> data;
1994         m_getImageDataForIconURLStatement->getColumnBlobAsVector(0, data);
1995         imageData = SharedBuffer::create(data.data(), data.size());
1996     } else if (result != SQLResultDone)
1997         LOG_ERROR("getImageDataForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data());
1998
1999     m_getImageDataForIconURLStatement->reset();
2000     
2001     return imageData.release();
2002 }
2003
2004 void IconDatabase::removeIconFromSQLDatabase(const String& iconURL)
2005 {
2006     ASSERT_ICON_SYNC_THREAD();
2007     
2008     if (iconURL.isEmpty())
2009         return;
2010
2011     // There would be a transaction here to make sure these removals are atomic
2012     // In practice the only caller of this method is always wrapped in a transaction itself so placing another here is unnecessary
2013     
2014     // It's possible this icon is not in the database because of certain rapid browsing patterns (such as a stress test) where the
2015     // icon is marked to be added then marked for removal before it is ever written to disk.  No big deal, early return
2016     int64_t iconID = getIconIDForIconURLFromSQLDatabase(iconURL);
2017     if (!iconID)
2018         return;
2019     
2020     readySQLiteStatement(m_deletePageURLsForIconURLStatement, m_syncDB, "DELETE FROM PageURL WHERE PageURL.iconID = (?);");
2021     m_deletePageURLsForIconURLStatement->bindInt64(1, iconID);
2022     
2023     if (m_deletePageURLsForIconURLStatement->step() != SQLResultDone)
2024         LOG_ERROR("m_deletePageURLsForIconURLStatement failed for url %s", urlForLogging(iconURL).ascii().data());
2025     
2026     readySQLiteStatement(m_deleteIconFromIconInfoStatement, m_syncDB, "DELETE FROM IconInfo WHERE IconInfo.iconID = (?);");
2027     m_deleteIconFromIconInfoStatement->bindInt64(1, iconID);
2028     
2029     if (m_deleteIconFromIconInfoStatement->step() != SQLResultDone)
2030         LOG_ERROR("m_deleteIconFromIconInfoStatement failed for url %s", urlForLogging(iconURL).ascii().data());
2031         
2032     readySQLiteStatement(m_deleteIconFromIconDataStatement, m_syncDB, "DELETE FROM IconData WHERE IconData.iconID = (?);");
2033     m_deleteIconFromIconDataStatement->bindInt64(1, iconID);
2034     
2035     if (m_deleteIconFromIconDataStatement->step() != SQLResultDone)
2036         LOG_ERROR("m_deleteIconFromIconDataStatement failed for url %s", urlForLogging(iconURL).ascii().data());
2037         
2038     m_deletePageURLsForIconURLStatement->reset();
2039     m_deleteIconFromIconInfoStatement->reset();
2040     m_deleteIconFromIconDataStatement->reset();
2041 }
2042
2043 void IconDatabase::writeIconSnapshotToSQLDatabase(const IconSnapshot& snapshot)
2044 {
2045     ASSERT_ICON_SYNC_THREAD();
2046     
2047     if (snapshot.iconURL().isEmpty())
2048         return;
2049         
2050     // A nulled out timestamp and data means this icon is destined to be deleted - do that instead of writing it out
2051     if (!snapshot.timestamp() && !snapshot.data()) {
2052         LOG(IconDatabase, "Removing %s from on-disk database", urlForLogging(snapshot.iconURL()).ascii().data());
2053         removeIconFromSQLDatabase(snapshot.iconURL());
2054         return;
2055     }
2056
2057     // There would be a transaction here to make sure these removals are atomic
2058     // In practice the only caller of this method is always wrapped in a transaction itself so placing another here is unnecessary
2059         
2060     // Get the iconID for this url
2061     int64_t iconID = getIconIDForIconURLFromSQLDatabase(snapshot.iconURL());
2062     
2063     // If there is already an iconID in place, update the database.  
2064     // Otherwise, insert new records
2065     if (iconID) {    
2066         readySQLiteStatement(m_updateIconInfoStatement, m_syncDB, "UPDATE IconInfo SET stamp = ?, url = ? WHERE iconID = ?;");
2067         m_updateIconInfoStatement->bindInt64(1, snapshot.timestamp());
2068         m_updateIconInfoStatement->bindText(2, snapshot.iconURL());
2069         m_updateIconInfoStatement->bindInt64(3, iconID);
2070
2071         if (m_updateIconInfoStatement->step() != SQLResultDone)
2072             LOG_ERROR("Failed to update icon info for url %s", urlForLogging(snapshot.iconURL()).ascii().data());
2073         
2074         m_updateIconInfoStatement->reset();
2075         
2076         readySQLiteStatement(m_updateIconDataStatement, m_syncDB, "UPDATE IconData SET data = ? WHERE iconID = ?;");
2077         m_updateIconDataStatement->bindInt64(2, iconID);
2078                 
2079         // If we *have* image data, bind it to this statement - Otherwise bind "null" for the blob data, 
2080         // signifying that this icon doesn't have any data    
2081         if (snapshot.data() && snapshot.data()->size())
2082             m_updateIconDataStatement->bindBlob(1, snapshot.data()->data(), snapshot.data()->size());
2083         else
2084             m_updateIconDataStatement->bindNull(1);
2085         
2086         if (m_updateIconDataStatement->step() != SQLResultDone)
2087             LOG_ERROR("Failed to update icon data for url %s", urlForLogging(snapshot.iconURL()).ascii().data());
2088
2089         m_updateIconDataStatement->reset();
2090     } else {    
2091         readySQLiteStatement(m_setIconInfoStatement, m_syncDB, "INSERT INTO IconInfo (url,stamp) VALUES (?, ?);");
2092         m_setIconInfoStatement->bindText(1, snapshot.iconURL());
2093         m_setIconInfoStatement->bindInt64(2, snapshot.timestamp());
2094
2095         if (m_setIconInfoStatement->step() != SQLResultDone)
2096             LOG_ERROR("Failed to set icon info for url %s", urlForLogging(snapshot.iconURL()).ascii().data());
2097         
2098         m_setIconInfoStatement->reset();
2099         
2100         int64_t iconID = m_syncDB.lastInsertRowID();
2101
2102         readySQLiteStatement(m_setIconDataStatement, m_syncDB, "INSERT INTO IconData (iconID, data) VALUES (?, ?);");
2103         m_setIconDataStatement->bindInt64(1, iconID);
2104
2105         // If we *have* image data, bind it to this statement - Otherwise bind "null" for the blob data, 
2106         // signifying that this icon doesn't have any data    
2107         if (snapshot.data() && snapshot.data()->size())
2108             m_setIconDataStatement->bindBlob(2, snapshot.data()->data(), snapshot.data()->size());
2109         else
2110             m_setIconDataStatement->bindNull(2);
2111         
2112         if (m_setIconDataStatement->step() != SQLResultDone)
2113             LOG_ERROR("Failed to set icon data for url %s", urlForLogging(snapshot.iconURL()).ascii().data());
2114
2115         m_setIconDataStatement->reset();
2116     }
2117 }
2118
2119 bool IconDatabase::wasExcludedFromBackup()
2120 {
2121     ASSERT_ICON_SYNC_THREAD();
2122
2123     return SQLiteStatement(m_syncDB, "SELECT value FROM IconDatabaseInfo WHERE key = 'ExcludedFromBackup';").getColumnInt(0);
2124 }
2125
2126 void IconDatabase::setWasExcludedFromBackup()
2127 {
2128     ASSERT_ICON_SYNC_THREAD();
2129
2130     SQLiteStatement(m_syncDB, "INSERT INTO IconDatabaseInfo (key, value) VALUES ('ExcludedFromBackup', 1)").executeCommand();
2131 }
2132
2133 class ClientWorkItem {
2134 public:
2135     ClientWorkItem(IconDatabaseClient* client)
2136         : m_client(client)
2137     { }
2138     virtual void performWork() = 0;
2139     virtual ~ClientWorkItem() { }
2140
2141 protected:
2142     IconDatabaseClient* m_client;
2143 };
2144
2145 class ImportedIconURLForPageURLWorkItem : public ClientWorkItem {
2146 public:
2147     ImportedIconURLForPageURLWorkItem(IconDatabaseClient* client, const String& pageURL)
2148         : ClientWorkItem(client)
2149         , m_pageURL(new String(pageURL.isolatedCopy()))
2150     { }
2151     
2152     virtual ~ImportedIconURLForPageURLWorkItem()
2153     {
2154         delete m_pageURL;
2155     }
2156
2157     virtual void performWork()
2158     {
2159         ASSERT(m_client);
2160         m_client->didImportIconURLForPageURL(*m_pageURL);
2161         m_client = 0;
2162     }
2163     
2164 private:
2165     String* m_pageURL;
2166 };
2167
2168 class ImportedIconDataForPageURLWorkItem : public ClientWorkItem {
2169 public:
2170     ImportedIconDataForPageURLWorkItem(IconDatabaseClient* client, const String& pageURL)
2171         : ClientWorkItem(client)
2172         , m_pageURL(new String(pageURL.isolatedCopy()))
2173     { }
2174     
2175     virtual ~ImportedIconDataForPageURLWorkItem()
2176     {
2177         delete m_pageURL;
2178     }
2179
2180     virtual void performWork()
2181     {
2182         ASSERT(m_client);
2183         m_client->didImportIconDataForPageURL(*m_pageURL);
2184         m_client = 0;
2185     }
2186     
2187 private:
2188     String* m_pageURL;
2189 };
2190
2191 class RemovedAllIconsWorkItem : public ClientWorkItem {
2192 public:
2193     RemovedAllIconsWorkItem(IconDatabaseClient* client)
2194         : ClientWorkItem(client)
2195     { }
2196
2197     virtual void performWork()
2198     {
2199         ASSERT(m_client);
2200         m_client->didRemoveAllIcons();
2201         m_client = 0;
2202     }
2203 };
2204
2205 class FinishedURLImport : public ClientWorkItem {
2206 public:
2207     FinishedURLImport(IconDatabaseClient* client)
2208         : ClientWorkItem(client)
2209     { }
2210
2211     virtual void performWork()
2212     {
2213         ASSERT(m_client);
2214         m_client->didFinishURLImport();
2215         m_client = 0;
2216     }
2217 };
2218
2219 static void performWorkItem(void* context)
2220 {
2221     ClientWorkItem* item = static_cast<ClientWorkItem*>(context);
2222     item->performWork();
2223     delete item;
2224 }
2225
2226 void IconDatabase::dispatchDidImportIconURLForPageURLOnMainThread(const String& pageURL)
2227 {
2228     ASSERT_ICON_SYNC_THREAD();
2229
2230     ImportedIconURLForPageURLWorkItem* work = new ImportedIconURLForPageURLWorkItem(m_client, pageURL);
2231     callOnMainThread(performWorkItem, work);
2232 }
2233
2234 void IconDatabase::dispatchDidImportIconDataForPageURLOnMainThread(const String& pageURL)
2235 {
2236     ASSERT_ICON_SYNC_THREAD();
2237
2238     ImportedIconDataForPageURLWorkItem* work = new ImportedIconDataForPageURLWorkItem(m_client, pageURL);
2239     callOnMainThread(performWorkItem, work);
2240 }
2241
2242 void IconDatabase::dispatchDidRemoveAllIconsOnMainThread()
2243 {
2244     ASSERT_ICON_SYNC_THREAD();
2245
2246     RemovedAllIconsWorkItem* work = new RemovedAllIconsWorkItem(m_client);
2247     callOnMainThread(performWorkItem, work);
2248 }
2249
2250 void IconDatabase::dispatchDidFinishURLImportOnMainThread()
2251 {
2252     ASSERT_ICON_SYNC_THREAD();
2253
2254     FinishedURLImport* work = new FinishedURLImport(m_client);
2255     callOnMainThread(performWorkItem, work);
2256 }
2257
2258
2259 } // namespace WebCore
2260
2261 #endif // ENABLE(ICONDATABASE)