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