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