Home | History | Annotate | Download | only in win
      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 "WebPreferences.h"
     32 #include "WebNotificationCenter.h"
     33 #include <WebCore/BitmapInfo.h>
     34 #include <WebCore/BString.h>
     35 #include <WebCore/COMPtr.h>
     36 #include <WebCore/FileSystem.h>
     37 #include <WebCore/IconDatabase.h>
     38 #include <WebCore/Image.h>
     39 #include <WebCore/PlatformString.h>
     40 #include <WebCore/SharedBuffer.h>
     41 #include <wtf/MainThread.h>
     42 #include "shlobj.h"
     43 
     44 using namespace WebCore;
     45 using namespace WTF;
     46 
     47 // WebIconDatabase ----------------------------------------------------------------
     48 
     49 WebIconDatabase* WebIconDatabase::m_sharedWebIconDatabase = 0;
     50 
     51 WebIconDatabase::WebIconDatabase()
     52 : m_refCount(0)
     53 , m_deliveryRequested(false)
     54 {
     55     gClassCount++;
     56     gClassNameCount.add("WebIconDatabase");
     57 }
     58 
     59 WebIconDatabase::~WebIconDatabase()
     60 {
     61     gClassCount--;
     62     gClassNameCount.remove("WebIconDatabase");
     63 }
     64 
     65 void WebIconDatabase::init()
     66 {
     67     WebPreferences* standardPrefs = WebPreferences::sharedStandardPreferences();
     68     BOOL enabled = FALSE;
     69     if (FAILED(standardPrefs->iconDatabaseEnabled(&enabled))) {
     70         enabled = FALSE;
     71         LOG_ERROR("Unable to get icon database enabled preference");
     72     }
     73     iconDatabase().setEnabled(!!enabled);
     74     if (!(!!enabled))
     75         return;
     76 
     77     startUpIconDatabase();
     78 }
     79 
     80 void WebIconDatabase::startUpIconDatabase()
     81 {
     82     WebPreferences* standardPrefs = WebPreferences::sharedStandardPreferences();
     83 
     84     iconDatabase().setClient(this);
     85 
     86     BSTR prefDatabasePath = 0;
     87     if (FAILED(standardPrefs->iconDatabaseLocation(&prefDatabasePath)))
     88         LOG_ERROR("Unable to get icon database location preference");
     89 
     90     String databasePath(prefDatabasePath, SysStringLen(prefDatabasePath));
     91     SysFreeString(prefDatabasePath);
     92 
     93     if (databasePath.isEmpty()) {
     94         databasePath = localUserSpecificStorageDirectory();
     95         if (databasePath.isEmpty())
     96             LOG_ERROR("Failed to construct default icon database path");
     97     }
     98 
     99     if (!iconDatabase().open(databasePath, WebCore::IconDatabase::defaultDatabaseFilename()))
    100             LOG_ERROR("Failed to open icon database path");
    101 }
    102 
    103 void WebIconDatabase::shutDownIconDatabase()
    104 {
    105 }
    106 
    107 WebIconDatabase* WebIconDatabase::createInstance()
    108 {
    109     WebIconDatabase* instance = new WebIconDatabase();
    110     instance->AddRef();
    111     return instance;
    112 }
    113 
    114 WebIconDatabase* WebIconDatabase::sharedWebIconDatabase()
    115 {
    116     if (m_sharedWebIconDatabase) {
    117         m_sharedWebIconDatabase->AddRef();
    118         return m_sharedWebIconDatabase;
    119     }
    120     m_sharedWebIconDatabase = createInstance();
    121     m_sharedWebIconDatabase->init();
    122     return m_sharedWebIconDatabase;
    123 }
    124 
    125 // IUnknown -------------------------------------------------------------------
    126 
    127 HRESULT STDMETHODCALLTYPE WebIconDatabase::QueryInterface(REFIID riid, void** ppvObject)
    128 {
    129     *ppvObject = 0;
    130     if (IsEqualGUID(riid, IID_IUnknown))
    131         *ppvObject = static_cast<IWebIconDatabase*>(this);
    132     else if (IsEqualGUID(riid, IID_IWebIconDatabase))
    133         *ppvObject = static_cast<IWebIconDatabase*>(this);
    134     else
    135         return E_NOINTERFACE;
    136 
    137     AddRef();
    138     return S_OK;
    139 }
    140 
    141 ULONG STDMETHODCALLTYPE WebIconDatabase::AddRef(void)
    142 {
    143     return ++m_refCount;
    144 }
    145 
    146 ULONG STDMETHODCALLTYPE WebIconDatabase::Release(void)
    147 {
    148     ULONG newRef = --m_refCount;
    149     if (!newRef)
    150         delete(this);
    151 
    152     return newRef;
    153 }
    154 
    155 // IWebIconDatabase --------------------------------------------------------------------
    156 
    157 HRESULT STDMETHODCALLTYPE WebIconDatabase::sharedIconDatabase(
    158         /* [retval][out] */ IWebIconDatabase** result)
    159 {
    160     *result = sharedWebIconDatabase();
    161     return S_OK;
    162 }
    163 
    164 HRESULT STDMETHODCALLTYPE WebIconDatabase::iconForURL(
    165         /* [in] */ BSTR url,
    166         /* [optional][in] */ LPSIZE size,
    167         /* [optional][in] */ BOOL /*cache*/,
    168         /* [retval][out] */ OLE_HANDLE* bitmap)
    169 {
    170     IntSize intSize(*size);
    171 
    172     Image* icon = 0;
    173     if (url)
    174         icon = iconDatabase().synchronousIconForPageURL(String(url, SysStringLen(url)), intSize);
    175 
    176     // Make sure we check for the case of an "empty image"
    177     if (icon && icon->width()) {
    178         *bitmap = (OLE_HANDLE)(ULONG64)getOrCreateSharedBitmap(size);
    179         if (!icon->getHBITMAPOfSize((HBITMAP)(ULONG64)*bitmap, size)) {
    180             LOG_ERROR("Failed to draw Image to HBITMAP");
    181             *bitmap = 0;
    182             return E_FAIL;
    183         }
    184         return S_OK;
    185     }
    186 
    187     return defaultIconWithSize(size, bitmap);
    188 }
    189 
    190 HRESULT STDMETHODCALLTYPE WebIconDatabase::defaultIconWithSize(
    191         /* [in] */ LPSIZE size,
    192         /* [retval][out] */ OLE_HANDLE* result)
    193 {
    194     *result = (OLE_HANDLE)(ULONG64)getOrCreateDefaultIconBitmap(size);
    195     return S_OK;
    196 }
    197 
    198 HRESULT STDMETHODCALLTYPE WebIconDatabase::retainIconForURL(
    199         /* [in] */ BSTR url)
    200 {
    201     iconDatabase().retainIconForPageURL(String(url, SysStringLen(url)));
    202     return S_OK;
    203 }
    204 
    205 HRESULT STDMETHODCALLTYPE WebIconDatabase::releaseIconForURL(
    206         /* [in] */ BSTR url)
    207 {
    208     iconDatabase().releaseIconForPageURL(String(url, SysStringLen(url)));
    209     return S_OK;
    210 }
    211 
    212 HRESULT STDMETHODCALLTYPE WebIconDatabase::removeAllIcons(void)
    213 {
    214     iconDatabase().removeAllIcons();
    215     return S_OK;
    216 }
    217 
    218 HRESULT STDMETHODCALLTYPE WebIconDatabase::delayDatabaseCleanup(void)
    219 {
    220     IconDatabase::delayDatabaseCleanup();
    221     return S_OK;
    222 }
    223 
    224 HRESULT STDMETHODCALLTYPE WebIconDatabase::allowDatabaseCleanup(void)
    225 {
    226     IconDatabase::allowDatabaseCleanup();
    227     return S_OK;
    228 }
    229 
    230 HRESULT STDMETHODCALLTYPE WebIconDatabase::iconURLForURL(
    231         /* [in] */ BSTR url,
    232         /* [retval][out] */ BSTR* iconURL)
    233 {
    234     if (!url || !iconURL)
    235         return E_POINTER;
    236     BString iconURLBSTR(iconDatabase().synchronousIconURLForPageURL(String(url, SysStringLen(url))));
    237     *iconURL = iconURLBSTR.release();
    238     return S_OK;
    239 }
    240 
    241 HRESULT STDMETHODCALLTYPE WebIconDatabase::isEnabled(
    242         /* [retval][out] */ BOOL *result)
    243 {
    244     *result = iconDatabase().isEnabled();
    245     return S_OK;
    246 }
    247 
    248 HRESULT STDMETHODCALLTYPE WebIconDatabase::setEnabled(
    249         /* [in] */ BOOL flag)
    250 {
    251     BOOL currentlyEnabled;
    252     isEnabled(&currentlyEnabled);
    253     if (currentlyEnabled && !flag) {
    254         iconDatabase().setEnabled(false);
    255         shutDownIconDatabase();
    256     } else if (!currentlyEnabled && flag) {
    257         iconDatabase().setEnabled(true);
    258         startUpIconDatabase();
    259     }
    260     return S_OK;
    261 }
    262 
    263 HRESULT STDMETHODCALLTYPE WebIconDatabase::hasIconForURL(
    264         /* [in] */ BSTR url,
    265         /* [out][retval] */ BOOL* result)
    266 {
    267     if (!url || !result)
    268         return E_POINTER;
    269 
    270     String urlString(url, SysStringLen(url));
    271 
    272     // Passing a size parameter of 0, 0 means we don't care about the result of the image, we just
    273     // want to make sure the read from disk to load the icon is kicked off.
    274     iconDatabase().synchronousIconForPageURL(urlString, IntSize(0, 0));
    275 
    276     // Check to see if we have a non-empty icon URL for the page, and if we do, we have an icon for
    277     // the page.
    278     *result = !(iconDatabase().synchronousIconURLForPageURL(urlString).isEmpty());
    279 
    280     return S_OK;
    281 }
    282 
    283 HBITMAP createDIB(LPSIZE size)
    284 {
    285     BitmapInfo bmInfo = BitmapInfo::create(IntSize(*size));
    286 
    287     HDC dc = GetDC(0);
    288     HBITMAP result = CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0);
    289     ReleaseDC(0, dc);
    290 
    291     return result;
    292 }
    293 
    294 HBITMAP WebIconDatabase::getOrCreateSharedBitmap(LPSIZE size)
    295 {
    296     HBITMAP result = m_sharedIconMap.get(*size);
    297     if (result)
    298         return result;
    299     result = createDIB(size);
    300     m_sharedIconMap.set(*size, result);
    301     return result;
    302 }
    303 
    304 HBITMAP WebIconDatabase::getOrCreateDefaultIconBitmap(LPSIZE size)
    305 {
    306     HBITMAP result = m_defaultIconMap.get(*size);
    307     if (result)
    308         return result;
    309 
    310     result = createDIB(size);
    311 
    312     m_defaultIconMap.set(*size, result);
    313     if (!iconDatabase().defaultIcon(*size)->getHBITMAPOfSize(result, size)) {
    314         LOG_ERROR("Failed to draw Image to HBITMAP");
    315         return 0;
    316     }
    317     return result;
    318 }
    319 
    320 // IconDatabaseClient
    321 
    322 bool WebIconDatabase::performImport()
    323 {
    324     // Windows doesn't do any old-style database importing.
    325     return true;
    326 }
    327 
    328 void WebIconDatabase::didRemoveAllIcons()
    329 {
    330     // Queueing the empty string is a special way of saying "this queued notification is the didRemoveAllIcons notification"
    331     MutexLocker locker(m_notificationMutex);
    332     m_notificationQueue.append(String());
    333     scheduleNotificationDelivery();
    334 }
    335 
    336 void WebIconDatabase::didImportIconURLForPageURL(const WTF::String& pageURL)
    337 {
    338     MutexLocker locker(m_notificationMutex);
    339     m_notificationQueue.append(pageURL.threadsafeCopy());
    340     scheduleNotificationDelivery();
    341 }
    342 
    343 void WebIconDatabase::didImportIconDataForPageURL(const WTF::String& pageURL)
    344 {
    345     // WebKit1 only has a single "icon did change" notification.
    346     didImportIconURLForPageURL(pageURL);
    347 }
    348 
    349 void WebIconDatabase::didChangeIconForPageURL(const WTF::String& pageURL)
    350 {
    351     // WebKit1 only has a single "icon did change" notification.
    352     didImportIconURLForPageURL(pageURL);
    353 }
    354 
    355 void WebIconDatabase::didFinishURLImport()
    356 {
    357 }
    358 
    359 void WebIconDatabase::scheduleNotificationDelivery()
    360 {
    361     // Caller of this method must hold the m_notificationQueue lock
    362     ASSERT(!m_notificationMutex.tryLock());
    363 
    364     if (!m_deliveryRequested) {
    365         m_deliveryRequested = true;
    366         callOnMainThread(deliverNotifications, 0);
    367     }
    368 }
    369 
    370 BSTR WebIconDatabase::iconDatabaseDidAddIconNotification()
    371 {
    372     static BSTR didAddIconName = SysAllocString(WebIconDatabaseDidAddIconNotification);
    373     return didAddIconName;
    374 }
    375 
    376 CFStringRef WebIconDatabase::iconDatabaseNotificationUserInfoURLKey()
    377 {
    378     static CFStringRef iconUserInfoURLKey = String(WebIconNotificationUserInfoURLKey).createCFString();
    379     return iconUserInfoURLKey;
    380 }
    381 
    382 BSTR WebIconDatabase::iconDatabaseDidRemoveAllIconsNotification()
    383 {
    384     static BSTR didRemoveAllIconsName = SysAllocString(WebIconDatabaseDidRemoveAllIconsNotification);
    385     return didRemoveAllIconsName;
    386 }
    387 
    388 static void postDidRemoveAllIconsNotification(WebIconDatabase* iconDB)
    389 {
    390     IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
    391     notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidRemoveAllIconsNotification(), static_cast<IWebIconDatabase*>(iconDB), 0);
    392 }
    393 
    394 static void postDidAddIconNotification(const String& pageURL, WebIconDatabase* iconDB)
    395 {
    396     RetainPtr<CFMutableDictionaryRef> dictionary(AdoptCF,
    397     CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    398 
    399     RetainPtr<CFStringRef> url(AdoptCF, pageURL.createCFString());
    400     CFDictionaryAddValue(dictionary.get(), WebIconDatabase::iconDatabaseNotificationUserInfoURLKey(), url.get());
    401 
    402     COMPtr<CFDictionaryPropertyBag> userInfo = CFDictionaryPropertyBag::createInstance();
    403     userInfo->setDictionary(dictionary.get());
    404 
    405     IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
    406     notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidAddIconNotification(), static_cast<IWebIconDatabase*>(iconDB), userInfo.get());
    407 }
    408 
    409 void WebIconDatabase::deliverNotifications(void*)
    410 {
    411     ASSERT(m_sharedWebIconDatabase);
    412     if (!m_sharedWebIconDatabase)
    413         return;
    414 
    415     ASSERT(m_sharedWebIconDatabase->m_deliveryRequested);
    416 
    417     Vector<String> queue;
    418     {
    419         MutexLocker locker(m_sharedWebIconDatabase->m_notificationMutex);
    420         queue.swap(m_sharedWebIconDatabase->m_notificationQueue);
    421         m_sharedWebIconDatabase->m_deliveryRequested = false;
    422     }
    423 
    424     for (unsigned i = 0; i < queue.size(); ++i) {
    425         if (queue[i].isNull())
    426             postDidRemoveAllIconsNotification(m_sharedWebIconDatabase);
    427         else
    428             postDidAddIconNotification(queue[i], m_sharedWebIconDatabase);
    429     }
    430 }
    431