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