1/*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2014 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Justin Haygood ([email protected])
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#pragma once
28
29#include <WebCore/NativeImage.h>
30#include <WebCore/SQLiteDatabase.h>
31#include <wtf/Condition.h>
32#include <wtf/HashCountedSet.h>
33#include <wtf/HashMap.h>
34#include <wtf/HashSet.h>
35#include <wtf/RunLoop.h>
36#include <wtf/glib/RunLoopSourcePriority.h>
37#include <wtf/text/StringHash.h>
38#include <wtf/text/WTFString.h>
39
40namespace WebCore {
41class SharedBuffer;
42}
43
44namespace WebKit {
45
46class IconDatabaseClient {
47public:
48 virtual ~IconDatabaseClient() = default;
49
50 virtual void didImportIconURLForPageURL(const String&) { }
51 virtual void didImportIconDataForPageURL(const String&) { }
52 virtual void didChangeIconForPageURL(const String&) { }
53 virtual void didFinishURLImport() { }
54};
55
56class IconDatabase {
57 WTF_MAKE_FAST_ALLOCATED;
58
59private:
60 class IconSnapshot {
61 public:
62 IconSnapshot() = default;
63
64 IconSnapshot(const String& iconURL, int timestamp, WebCore::SharedBuffer* data)
65 : m_iconURL(iconURL)
66 , m_timestamp(timestamp)
67 , m_data(data)
68 { }
69
70 const String& iconURL() const { return m_iconURL; }
71 int timestamp() const { return m_timestamp; }
72 WebCore::SharedBuffer* data() const { return m_data.get(); }
73
74 private:
75 String m_iconURL;
76 int m_timestamp { 0 };
77 RefPtr<WebCore::SharedBuffer> m_data;
78 };
79
80 class IconRecord : public RefCounted<IconRecord> {
81 public:
82 static Ref<IconRecord> create(const String& url)
83 {
84 return adoptRef(*new IconRecord(url));
85 }
86 ~IconRecord();
87
88 time_t getTimestamp() { return m_stamp; }
89 void setTimestamp(time_t stamp) { m_stamp = stamp; }
90
91 void setImageData(RefPtr<WebCore::SharedBuffer>&&);
92 WebCore::NativeImagePtr image(const WebCore::IntSize&);
93
94 String iconURL() { return m_iconURL; }
95
96 enum class ImageDataStatus { Present, Missing, Unknown };
97 ImageDataStatus imageDataStatus();
98
99 HashSet<String>& retainingPageURLs() { return m_retainingPageURLs; }
100
101 IconSnapshot snapshot(bool forDeletion = false) const;
102
103 private:
104 IconRecord(const String& url);
105
106 String m_iconURL;
107 time_t m_stamp { 0 };
108 RefPtr<WebCore::SharedBuffer> m_imageData;
109 WebCore::NativeImagePtr m_image;
110
111 HashSet<String> m_retainingPageURLs;
112
113 // This allows us to cache whether or not a SiteIcon has had its data set yet
114 // This helps the IconDatabase know if it has to set the data on a new object or not,
115 // and also to determine if the icon is missing data or if it just hasn't been brought
116 // in from the DB yet
117 bool m_dataSet { false };
118 };
119
120 class PageURLSnapshot {
121 public:
122 PageURLSnapshot() = default;
123
124 PageURLSnapshot(const String& pageURL, const String& iconURL)
125 : m_pageURL(pageURL)
126 , m_iconURL(iconURL)
127 { }
128
129 const String& pageURL() const { return m_pageURL; }
130 const String& iconURL() const { return m_iconURL; }
131
132 private:
133 String m_pageURL;
134 String m_iconURL;
135 };
136
137 class PageURLRecord {
138 WTF_MAKE_NONCOPYABLE(PageURLRecord); WTF_MAKE_FAST_ALLOCATED;
139 public:
140 PageURLRecord(const String& pageURL);
141 ~PageURLRecord();
142
143 inline String url() const { return m_pageURL; }
144
145 void setIconRecord(RefPtr<IconRecord>&&);
146 IconRecord* iconRecord() { return m_iconRecord.get(); }
147
148 PageURLSnapshot snapshot(bool forDeletion = false) const;
149
150 // Returns false if the page wasn't retained beforehand, true if the retain count was already 1 or higher.
151 bool retain(int count)
152 {
153 bool wasRetained = m_retainCount > 0;
154 m_retainCount += count;
155 return wasRetained;
156 }
157
158 // Returns true if the page is still retained after the call. False if the retain count just dropped to 0.
159 bool release(int count)
160 {
161 ASSERT(m_retainCount >= count);
162 m_retainCount -= count;
163 return m_retainCount > 0;
164 }
165
166 int retainCount() const { return m_retainCount; }
167
168 private:
169 String m_pageURL;
170 RefPtr<IconRecord> m_iconRecord;
171 int m_retainCount { 0 };
172 };
173
174 class MainThreadNotifier {
175 public:
176 MainThreadNotifier()
177 : m_timer(RunLoop::main(), this, &MainThreadNotifier::timerFired)
178 {
179 m_timer.setPriority(RunLoopSourcePriority::MainThreadDispatcherTimer);
180 }
181
182 void setActive(bool active)
183 {
184 m_isActive.store(active);
185 }
186
187 void notify(Function<void()>&& notification)
188 {
189 if (!m_isActive.load())
190 return;
191
192 {
193 LockHolder locker(m_notificationQueueLock);
194 m_notificationQueue.append(WTFMove(notification));
195 }
196
197 if (!m_timer.isActive())
198 m_timer.startOneShot(0_s);
199 }
200
201 void stop()
202 {
203 setActive(false);
204 m_timer.stop();
205 LockHolder locker(m_notificationQueueLock);
206 m_notificationQueue.clear();
207 }
208
209 private:
210 void timerFired()
211 {
212 Deque<Function<void()>> notificationQueue;
213 {
214 LockHolder locker(m_notificationQueueLock);
215 notificationQueue = WTFMove(m_notificationQueue);
216 }
217
218 if (!m_isActive.load())
219 return;
220
221 while (!notificationQueue.isEmpty()) {
222 auto function = notificationQueue.takeFirst();
223 function();
224 }
225 }
226
227 Deque<Function<void()>> m_notificationQueue;
228 Lock m_notificationQueueLock;
229 Atomic<bool> m_isActive;
230 RunLoop::Timer<MainThreadNotifier> m_timer;
231 };
232
233// *** Main Thread Only ***
234public:
235 IconDatabase();
236 ~IconDatabase();
237
238 enum class IconLoadDecision { Yes, No, Unknown };
239
240 void setClient(std::unique_ptr<IconDatabaseClient>&&);
241
242 bool open(const String& directory, const String& filename);
243 void close();
244
245 void removeAllIcons();
246
247 void retainIconForPageURL(const String&);
248 void releaseIconForPageURL(const String&);
249 void setIconDataForIconURL(RefPtr<WebCore::SharedBuffer>&&, const String& iconURL);
250 void setIconURLForPageURL(const String& iconURL, const String& pageURL);
251
252 enum class IsKnownIcon { No, Yes };
253 std::pair<WebCore::NativeImagePtr, IsKnownIcon> synchronousIconForPageURL(const String&, const WebCore::IntSize&);
254 String synchronousIconURLForPageURL(const String&);
255 bool synchronousIconDataKnownForIconURL(const String&);
256 IconLoadDecision synchronousLoadDecisionForIconURL(const String&);
257
258 void setEnabled(bool);
259 bool isEnabled() const;
260
261 void setPrivateBrowsingEnabled(bool flag);
262 bool isPrivateBrowsingEnabled() const;
263
264 static void delayDatabaseCleanup();
265 static void allowDatabaseCleanup();
266 static void checkIntegrityBeforeOpening();
267
268private:
269 void wakeSyncThread();
270 void scheduleOrDeferSyncTimer();
271 void syncTimerFired();
272
273 RunLoop::Timer<IconDatabase> m_syncTimer;
274 RefPtr<Thread> m_syncThread;
275 bool m_syncThreadRunning { false };
276 bool m_scheduleOrDeferSyncTimerRequested { false };
277
278// *** Any Thread ***
279public:
280 bool isOpen() const;
281 String databasePath() const;
282 static String defaultDatabaseFilename();
283
284private:
285 Ref<IconRecord> getOrCreateIconRecord(const String& iconURL);
286 PageURLRecord* getOrCreatePageURLRecord(const String& pageURL);
287
288 bool m_isEnabled { false };
289 bool m_privateBrowsingEnabled { false };
290
291 mutable Lock m_syncLock;
292 Condition m_syncCondition;
293 String m_databaseDirectory;
294 // Holding m_syncLock is required when accessing m_completeDatabasePath
295 String m_completeDatabasePath;
296
297 bool m_threadTerminationRequested { false };
298 bool m_removeIconsRequested { false };
299 bool m_iconURLImportComplete { false };
300 bool m_syncThreadHasWorkToDo { false };
301
302 Lock m_urlAndIconLock;
303 // Holding m_urlAndIconLock is required when accessing any of the following data structures or the objects they contain
304 HashMap<String, IconRecord*> m_iconURLToRecordMap;
305 HashMap<String, PageURLRecord*> m_pageURLToRecordMap;
306 HashSet<String> m_retainedPageURLs;
307
308 Lock m_pendingSyncLock;
309 // Holding m_pendingSyncLock is required when accessing any of the following data structures
310 HashMap<String, PageURLSnapshot> m_pageURLsPendingSync;
311 HashMap<String, IconSnapshot> m_iconsPendingSync;
312
313 Lock m_pendingReadingLock;
314 // Holding m_pendingSyncLock is required when accessing any of the following data structures - when dealing with IconRecord*s, holding m_urlAndIconLock is also required
315 HashSet<String> m_pageURLsPendingImport;
316 HashSet<String> m_pageURLsInterestedInIcons;
317 HashSet<IconRecord*> m_iconsPendingReading;
318
319 Lock m_urlsToRetainOrReleaseLock;
320 // Holding m_urlsToRetainOrReleaseLock is required when accessing any of the following data structures.
321 HashCountedSet<String> m_urlsToRetain;
322 HashCountedSet<String> m_urlsToRelease;
323 bool m_retainOrReleaseIconRequested { false };
324
325// *** Sync Thread Only ***
326public:
327 bool shouldStopThreadActivity() const;
328
329private:
330 void iconDatabaseSyncThread();
331
332 // The following block of methods are called exclusively by the sync thread to manage i/o to and from the database
333 // Each method should periodically monitor m_threadTerminationRequested when it makes sense to return early on shutdown
334 void performOpenInitialization();
335 bool checkIntegrity();
336 void performURLImport();
337 void syncThreadMainLoop();
338 bool readFromDatabase();
339 bool writeToDatabase();
340 void pruneUnretainedIcons();
341 void checkForDanglingPageURLs(bool pruneIfFound);
342 void removeAllIconsOnThread();
343 void deleteAllPreparedStatements();
344 void* cleanupSyncThread();
345 void performRetainIconForPageURL(const String&, int retainCount);
346 void performReleaseIconForPageURL(const String&, int releaseCount);
347
348 bool m_initialPruningComplete { false };
349
350 void setIconURLForPageURLInSQLDatabase(const String&, const String&);
351 void setIconIDForPageURLInSQLDatabase(int64_t, const String&);
352 void removePageURLFromSQLDatabase(const String& pageURL);
353 int64_t getIconIDForIconURLFromSQLDatabase(const String& iconURL);
354 int64_t addIconURLToSQLDatabase(const String&);
355 RefPtr<WebCore::SharedBuffer> getImageDataForIconURLFromSQLDatabase(const String& iconURL);
356 void removeIconFromSQLDatabase(const String& iconURL);
357 void writeIconSnapshotToSQLDatabase(const IconSnapshot&);
358
359 void performPendingRetainAndReleaseOperations();
360
361 // Methods to dispatch client callbacks on the main thread
362 void dispatchDidImportIconURLForPageURLOnMainThread(const String&);
363 void dispatchDidImportIconDataForPageURLOnMainThread(const String&);
364 void dispatchDidFinishURLImportOnMainThread();
365
366 // The client is set by the main thread before the thread starts, and from then on is only used by the sync thread
367 std::unique_ptr<IconDatabaseClient> m_client;
368
369 WebCore::SQLiteDatabase m_syncDB;
370
371 std::unique_ptr<WebCore::SQLiteStatement> m_setIconIDForPageURLStatement;
372 std::unique_ptr<WebCore::SQLiteStatement> m_removePageURLStatement;
373 std::unique_ptr<WebCore::SQLiteStatement> m_getIconIDForIconURLStatement;
374 std::unique_ptr<WebCore::SQLiteStatement> m_getImageDataForIconURLStatement;
375 std::unique_ptr<WebCore::SQLiteStatement> m_addIconToIconInfoStatement;
376 std::unique_ptr<WebCore::SQLiteStatement> m_addIconToIconDataStatement;
377 std::unique_ptr<WebCore::SQLiteStatement> m_getImageDataStatement;
378 std::unique_ptr<WebCore::SQLiteStatement> m_deletePageURLsForIconURLStatement;
379 std::unique_ptr<WebCore::SQLiteStatement> m_deleteIconFromIconInfoStatement;
380 std::unique_ptr<WebCore::SQLiteStatement> m_deleteIconFromIconDataStatement;
381 std::unique_ptr<WebCore::SQLiteStatement> m_updateIconInfoStatement;
382 std::unique_ptr<WebCore::SQLiteStatement> m_updateIconDataStatement;
383 std::unique_ptr<WebCore::SQLiteStatement> m_setIconInfoStatement;
384 std::unique_ptr<WebCore::SQLiteStatement> m_setIconDataStatement;
385
386 MainThreadNotifier m_mainThreadNotifier;
387};
388
389} // namespace WebKit
390