1 /* 2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "WebKitDLL.h" 28 #include "WebIconDatabase.h" 29 30 #include "CFDictionaryPropertyBag.h" 31 #include "COMPtr.h" 32 #include "WebPreferences.h" 33 #include "WebNotificationCenter.h" 34 #pragma warning(push, 0) 35 #include <WebCore/BitmapInfo.h> 36 #include <WebCore/BString.h> 37 #include <WebCore/FileSystem.h> 38 #include <WebCore/IconDatabase.h> 39 #include <WebCore/Image.h> 40 #include <WebCore/PlatformString.h> 41 #pragma warning(pop) 42 #include <wtf/MainThread.h> 43 #include "shlobj.h" 44 45 using namespace WebCore; 46 using namespace WTF; 47 48 // WebIconDatabase ---------------------------------------------------------------- 49 50 WebIconDatabase* WebIconDatabase::m_sharedWebIconDatabase = 0; 51 52 WebIconDatabase::WebIconDatabase() 53 : m_refCount(0) 54 , m_deliveryRequested(false) 55 { 56 gClassCount++; 57 gClassNameCount.add("WebIconDatabase"); 58 } 59 60 WebIconDatabase::~WebIconDatabase() 61 { 62 gClassCount--; 63 gClassNameCount.remove("WebIconDatabase"); 64 } 65 66 void WebIconDatabase::init() 67 { 68 WebPreferences* standardPrefs = WebPreferences::sharedStandardPreferences(); 69 BOOL enabled = FALSE; 70 if (FAILED(standardPrefs->iconDatabaseEnabled(&enabled))) { 71 enabled = FALSE; 72 LOG_ERROR("Unable to get icon database enabled preference"); 73 } 74 iconDatabase()->setEnabled(!!enabled); 75 if (!(!!enabled)) 76 return; 77 78 startUpIconDatabase(); 79 } 80 81 void WebIconDatabase::startUpIconDatabase() 82 { 83 WebPreferences* standardPrefs = WebPreferences::sharedStandardPreferences(); 84 85 iconDatabase()->setClient(this); 86 87 BSTR prefDatabasePath = 0; 88 if (FAILED(standardPrefs->iconDatabaseLocation(&prefDatabasePath))) 89 LOG_ERROR("Unable to get icon database location preference"); 90 91 String databasePath(prefDatabasePath, SysStringLen(prefDatabasePath)); 92 SysFreeString(prefDatabasePath); 93 94 if (databasePath.isEmpty()) { 95 databasePath = localUserSpecificStorageDirectory(); 96 if (databasePath.isEmpty()) 97 LOG_ERROR("Failed to construct default icon database path"); 98 } 99 100 if (!iconDatabase()->open(databasePath)) 101 LOG_ERROR("Failed to open icon database path"); 102 } 103 104 void WebIconDatabase::shutDownIconDatabase() 105 { 106 } 107 108 WebIconDatabase* WebIconDatabase::createInstance() 109 { 110 WebIconDatabase* instance = new WebIconDatabase(); 111 instance->AddRef(); 112 return instance; 113 } 114 115 WebIconDatabase* WebIconDatabase::sharedWebIconDatabase() 116 { 117 if (m_sharedWebIconDatabase) { 118 m_sharedWebIconDatabase->AddRef(); 119 return m_sharedWebIconDatabase; 120 } 121 m_sharedWebIconDatabase = createInstance(); 122 m_sharedWebIconDatabase->init(); 123 return m_sharedWebIconDatabase; 124 } 125 126 // IUnknown ------------------------------------------------------------------- 127 128 HRESULT STDMETHODCALLTYPE WebIconDatabase::QueryInterface(REFIID riid, void** ppvObject) 129 { 130 *ppvObject = 0; 131 if (IsEqualGUID(riid, IID_IUnknown)) 132 *ppvObject = static_cast<IWebIconDatabase*>(this); 133 else if (IsEqualGUID(riid, IID_IWebIconDatabase)) 134 *ppvObject = static_cast<IWebIconDatabase*>(this); 135 else 136 return E_NOINTERFACE; 137 138 AddRef(); 139 return S_OK; 140 } 141 142 ULONG STDMETHODCALLTYPE WebIconDatabase::AddRef(void) 143 { 144 return ++m_refCount; 145 } 146 147 ULONG STDMETHODCALLTYPE WebIconDatabase::Release(void) 148 { 149 ULONG newRef = --m_refCount; 150 if (!newRef) 151 delete(this); 152 153 return newRef; 154 } 155 156 // IWebIconDatabase -------------------------------------------------------------------- 157 158 HRESULT STDMETHODCALLTYPE WebIconDatabase::sharedIconDatabase( 159 /* [retval][out] */ IWebIconDatabase** result) 160 { 161 *result = sharedWebIconDatabase(); 162 return S_OK; 163 } 164 165 HRESULT STDMETHODCALLTYPE WebIconDatabase::iconForURL( 166 /* [in] */ BSTR url, 167 /* [optional][in] */ LPSIZE size, 168 /* [optional][in] */ BOOL /*cache*/, 169 /* [retval][out] */ OLE_HANDLE* bitmap) 170 { 171 IntSize intSize(*size); 172 173 Image* icon = 0; 174 if (url) 175 icon = iconDatabase()->iconForPageURL(String(url, SysStringLen(url)), intSize); 176 177 // Make sure we check for the case of an "empty image" 178 if (icon && icon->width()) { 179 *bitmap = (OLE_HANDLE)(ULONG64)getOrCreateSharedBitmap(size); 180 if (!icon->getHBITMAPOfSize((HBITMAP)(ULONG64)*bitmap, size)) { 181 LOG_ERROR("Failed to draw Image to HBITMAP"); 182 *bitmap = 0; 183 return E_FAIL; 184 } 185 return S_OK; 186 } 187 188 return defaultIconWithSize(size, bitmap); 189 } 190 191 HRESULT STDMETHODCALLTYPE WebIconDatabase::defaultIconWithSize( 192 /* [in] */ LPSIZE size, 193 /* [retval][out] */ OLE_HANDLE* result) 194 { 195 *result = (OLE_HANDLE)(ULONG64)getOrCreateDefaultIconBitmap(size); 196 return S_OK; 197 } 198 199 HRESULT STDMETHODCALLTYPE WebIconDatabase::retainIconForURL( 200 /* [in] */ BSTR url) 201 { 202 iconDatabase()->retainIconForPageURL(String(url, SysStringLen(url))); 203 return S_OK; 204 } 205 206 HRESULT STDMETHODCALLTYPE WebIconDatabase::releaseIconForURL( 207 /* [in] */ BSTR url) 208 { 209 iconDatabase()->releaseIconForPageURL(String(url, SysStringLen(url))); 210 return S_OK; 211 } 212 213 HRESULT STDMETHODCALLTYPE WebIconDatabase::removeAllIcons(void) 214 { 215 iconDatabase()->removeAllIcons(); 216 return S_OK; 217 } 218 219 HRESULT STDMETHODCALLTYPE WebIconDatabase::delayDatabaseCleanup(void) 220 { 221 IconDatabase::delayDatabaseCleanup(); 222 return S_OK; 223 } 224 225 HRESULT STDMETHODCALLTYPE WebIconDatabase::allowDatabaseCleanup(void) 226 { 227 IconDatabase::allowDatabaseCleanup(); 228 return S_OK; 229 } 230 231 HRESULT STDMETHODCALLTYPE WebIconDatabase::iconURLForURL( 232 /* [in] */ BSTR url, 233 /* [retval][out] */ BSTR* iconURL) 234 { 235 if (!url || !iconURL) 236 return E_POINTER; 237 BString iconURLBSTR(iconDatabase()->iconURLForPageURL(String(url, SysStringLen(url)))); 238 *iconURL = iconURLBSTR.release(); 239 return S_OK; 240 } 241 242 HRESULT STDMETHODCALLTYPE WebIconDatabase::isEnabled( 243 /* [retval][out] */ BOOL *result) 244 { 245 *result = iconDatabase()->isEnabled(); 246 return S_OK; 247 } 248 249 HRESULT STDMETHODCALLTYPE WebIconDatabase::setEnabled( 250 /* [in] */ BOOL flag) 251 { 252 BOOL currentlyEnabled; 253 isEnabled(¤tlyEnabled); 254 if (currentlyEnabled && !flag) { 255 iconDatabase()->setEnabled(false); 256 shutDownIconDatabase(); 257 } else if (!currentlyEnabled && flag) { 258 iconDatabase()->setEnabled(true); 259 startUpIconDatabase(); 260 } 261 return S_OK; 262 } 263 264 HBITMAP createDIB(LPSIZE size) 265 { 266 BitmapInfo bmInfo = BitmapInfo::create(IntSize(*size)); 267 268 HDC dc = GetDC(0); 269 HBITMAP result = CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0); 270 ReleaseDC(0, dc); 271 272 return result; 273 } 274 275 HBITMAP WebIconDatabase::getOrCreateSharedBitmap(LPSIZE size) 276 { 277 HBITMAP result = m_sharedIconMap.get(*size); 278 if (result) 279 return result; 280 result = createDIB(size); 281 m_sharedIconMap.set(*size, result); 282 return result; 283 } 284 285 HBITMAP WebIconDatabase::getOrCreateDefaultIconBitmap(LPSIZE size) 286 { 287 HBITMAP result = m_defaultIconMap.get(*size); 288 if (result) 289 return result; 290 291 result = createDIB(size); 292 293 m_defaultIconMap.set(*size, result); 294 if (!iconDatabase()->defaultIcon(*size)->getHBITMAPOfSize(result, size)) { 295 LOG_ERROR("Failed to draw Image to HBITMAP"); 296 return 0; 297 } 298 return result; 299 } 300 301 // IconDatabaseClient 302 303 void WebIconDatabase::dispatchDidRemoveAllIcons() 304 { 305 // Queueing the empty string is a special way of saying "this queued notification is the didRemoveAllIcons notification" 306 MutexLocker locker(m_notificationMutex); 307 m_notificationQueue.append(String()); 308 scheduleNotificationDelivery(); 309 } 310 311 void WebIconDatabase::dispatchDidAddIconForPageURL(const String& pageURL) 312 { 313 MutexLocker locker(m_notificationMutex); 314 m_notificationQueue.append(pageURL.threadsafeCopy()); 315 scheduleNotificationDelivery(); 316 } 317 318 void WebIconDatabase::scheduleNotificationDelivery() 319 { 320 // Caller of this method must hold the m_notificationQueue lock 321 ASSERT(!m_notificationMutex.tryLock()); 322 323 if (!m_deliveryRequested) { 324 m_deliveryRequested = true; 325 callOnMainThread(deliverNotifications, 0); 326 } 327 } 328 329 BSTR WebIconDatabase::iconDatabaseDidAddIconNotification() 330 { 331 static BSTR didAddIconName = SysAllocString(WebIconDatabaseDidAddIconNotification); 332 return didAddIconName; 333 } 334 335 CFStringRef WebIconDatabase::iconDatabaseNotificationUserInfoURLKey() 336 { 337 static CFStringRef iconUserInfoURLKey = String(WebIconNotificationUserInfoURLKey).createCFString(); 338 return iconUserInfoURLKey; 339 } 340 341 BSTR WebIconDatabase::iconDatabaseDidRemoveAllIconsNotification() 342 { 343 static BSTR didRemoveAllIconsName = SysAllocString(WebIconDatabaseDidRemoveAllIconsNotification); 344 return didRemoveAllIconsName; 345 } 346 347 static void postDidRemoveAllIconsNotification(WebIconDatabase* iconDB) 348 { 349 IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal(); 350 notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidRemoveAllIconsNotification(), static_cast<IWebIconDatabase*>(iconDB), 0); 351 } 352 353 static void postDidAddIconNotification(const String& pageURL, WebIconDatabase* iconDB) 354 { 355 RetainPtr<CFMutableDictionaryRef> dictionary(AdoptCF, 356 CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 357 358 RetainPtr<CFStringRef> url(AdoptCF, pageURL.createCFString()); 359 CFDictionaryAddValue(dictionary.get(), WebIconDatabase::iconDatabaseNotificationUserInfoURLKey(), url.get()); 360 361 COMPtr<CFDictionaryPropertyBag> userInfo = CFDictionaryPropertyBag::createInstance(); 362 userInfo->setDictionary(dictionary.get()); 363 364 IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal(); 365 notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidAddIconNotification(), static_cast<IWebIconDatabase*>(iconDB), userInfo.get()); 366 } 367 368 void WebIconDatabase::deliverNotifications(void*) 369 { 370 ASSERT(m_sharedWebIconDatabase); 371 if (!m_sharedWebIconDatabase) 372 return; 373 374 ASSERT(m_sharedWebIconDatabase->m_deliveryRequested); 375 376 Vector<String> queue; 377 { 378 MutexLocker locker(m_sharedWebIconDatabase->m_notificationMutex); 379 queue.swap(m_sharedWebIconDatabase->m_notificationQueue); 380 m_sharedWebIconDatabase->m_deliveryRequested = false; 381 } 382 383 for (unsigned i = 0; i < queue.size(); ++i) { 384 if (queue[i].isNull()) 385 postDidRemoveAllIconsNotification(m_sharedWebIconDatabase); 386 else 387 postDidAddIconNotification(queue[i], m_sharedWebIconDatabase); 388 } 389 } 390