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