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