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