WebIconDatabase.cpp revision 81bc750723a18f21cd17d1b173cd2a4dda9cea6e
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
45using namespace WebCore;
46using namespace WTF;
47
48// WebIconDatabase ----------------------------------------------------------------
49
50WebIconDatabase* WebIconDatabase::m_sharedWebIconDatabase = 0;
51
52WebIconDatabase::WebIconDatabase()
53: m_refCount(0)
54, m_deliveryRequested(false)
55{
56    gClassCount++;
57    gClassNameCount.add("WebIconDatabase");
58}
59
60WebIconDatabase::~WebIconDatabase()
61{
62    gClassCount--;
63    gClassNameCount.remove("WebIconDatabase");
64}
65
66void 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
81void 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
104void WebIconDatabase::shutDownIconDatabase()
105{
106}
107
108WebIconDatabase* WebIconDatabase::createInstance()
109{
110    WebIconDatabase* instance = new WebIconDatabase();
111    instance->AddRef();
112    return instance;
113}
114
115WebIconDatabase* 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
128HRESULT 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
142ULONG STDMETHODCALLTYPE WebIconDatabase::AddRef(void)
143{
144    return ++m_refCount;
145}
146
147ULONG 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
158HRESULT STDMETHODCALLTYPE WebIconDatabase::sharedIconDatabase(
159        /* [retval][out] */ IWebIconDatabase** result)
160{
161    *result = sharedWebIconDatabase();
162    return S_OK;
163}
164
165HRESULT 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
191HRESULT 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
199HRESULT STDMETHODCALLTYPE WebIconDatabase::retainIconForURL(
200        /* [in] */ BSTR url)
201{
202    iconDatabase().retainIconForPageURL(String(url, SysStringLen(url)));
203    return S_OK;
204}
205
206HRESULT STDMETHODCALLTYPE WebIconDatabase::releaseIconForURL(
207        /* [in] */ BSTR url)
208{
209    iconDatabase().releaseIconForPageURL(String(url, SysStringLen(url)));
210    return S_OK;
211}
212
213HRESULT STDMETHODCALLTYPE WebIconDatabase::removeAllIcons(void)
214{
215    iconDatabase().removeAllIcons();
216    return S_OK;
217}
218
219HRESULT STDMETHODCALLTYPE WebIconDatabase::delayDatabaseCleanup(void)
220{
221    IconDatabase::delayDatabaseCleanup();
222    return S_OK;
223}
224
225HRESULT STDMETHODCALLTYPE WebIconDatabase::allowDatabaseCleanup(void)
226{
227    IconDatabase::allowDatabaseCleanup();
228    return S_OK;
229}
230
231HRESULT 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
242HRESULT STDMETHODCALLTYPE WebIconDatabase::isEnabled(
243        /* [retval][out] */ BOOL *result)
244{
245    *result = iconDatabase().isEnabled();
246    return S_OK;
247}
248
249HRESULT STDMETHODCALLTYPE WebIconDatabase::setEnabled(
250        /* [in] */ BOOL flag)
251{
252    BOOL currentlyEnabled;
253    isEnabled(&currentlyEnabled);
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
264HRESULT STDMETHODCALLTYPE WebIconDatabase::hasIconForURL(
265        /* [in] */ BSTR url,
266        /* [out][retval] */ BOOL* result)
267{
268    if (!url || !result)
269        return E_POINTER;
270
271    String urlString(url, SysStringLen(url));
272
273    // Passing a size parameter of 0, 0 means we don't care about the result of the image, we just
274    // want to make sure the read from disk to load the icon is kicked off.
275    iconDatabase().iconForPageURL(urlString, IntSize(0, 0));
276
277    // Check to see if we have a non-empty icon URL for the page, and if we do, we have an icon for
278    // the page.
279    *result = !(iconDatabase().iconURLForPageURL(urlString).isEmpty());
280
281    return S_OK;
282}
283
284HBITMAP createDIB(LPSIZE size)
285{
286    BitmapInfo bmInfo = BitmapInfo::create(IntSize(*size));
287
288    HDC dc = GetDC(0);
289    HBITMAP result = CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0);
290    ReleaseDC(0, dc);
291
292    return result;
293}
294
295HBITMAP WebIconDatabase::getOrCreateSharedBitmap(LPSIZE size)
296{
297    HBITMAP result = m_sharedIconMap.get(*size);
298    if (result)
299        return result;
300    result = createDIB(size);
301    m_sharedIconMap.set(*size, result);
302    return result;
303}
304
305HBITMAP WebIconDatabase::getOrCreateDefaultIconBitmap(LPSIZE size)
306{
307    HBITMAP result = m_defaultIconMap.get(*size);
308    if (result)
309        return result;
310
311    result = createDIB(size);
312
313    m_defaultIconMap.set(*size, result);
314    if (!iconDatabase().defaultIcon(*size)->getHBITMAPOfSize(result, size)) {
315        LOG_ERROR("Failed to draw Image to HBITMAP");
316        return 0;
317    }
318    return result;
319}
320
321// IconDatabaseClient
322
323void WebIconDatabase::dispatchDidRemoveAllIcons()
324{
325    // Queueing the empty string is a special way of saying "this queued notification is the didRemoveAllIcons notification"
326    MutexLocker locker(m_notificationMutex);
327    m_notificationQueue.append(String());
328    scheduleNotificationDelivery();
329}
330
331void WebIconDatabase::dispatchDidAddIconForPageURL(const String& pageURL)
332{
333    MutexLocker locker(m_notificationMutex);
334    m_notificationQueue.append(pageURL.threadsafeCopy());
335    scheduleNotificationDelivery();
336}
337
338void WebIconDatabase::scheduleNotificationDelivery()
339{
340    // Caller of this method must hold the m_notificationQueue lock
341    ASSERT(!m_notificationMutex.tryLock());
342
343    if (!m_deliveryRequested) {
344        m_deliveryRequested = true;
345        callOnMainThread(deliverNotifications, 0);
346    }
347}
348
349BSTR WebIconDatabase::iconDatabaseDidAddIconNotification()
350{
351    static BSTR didAddIconName = SysAllocString(WebIconDatabaseDidAddIconNotification);
352    return didAddIconName;
353}
354
355CFStringRef WebIconDatabase::iconDatabaseNotificationUserInfoURLKey()
356{
357    static CFStringRef iconUserInfoURLKey = String(WebIconNotificationUserInfoURLKey).createCFString();
358    return iconUserInfoURLKey;
359}
360
361BSTR WebIconDatabase::iconDatabaseDidRemoveAllIconsNotification()
362{
363    static BSTR didRemoveAllIconsName = SysAllocString(WebIconDatabaseDidRemoveAllIconsNotification);
364    return didRemoveAllIconsName;
365}
366
367static void postDidRemoveAllIconsNotification(WebIconDatabase* iconDB)
368{
369    IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
370    notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidRemoveAllIconsNotification(), static_cast<IWebIconDatabase*>(iconDB), 0);
371}
372
373static void postDidAddIconNotification(const String& pageURL, WebIconDatabase* iconDB)
374{
375    RetainPtr<CFMutableDictionaryRef> dictionary(AdoptCF,
376    CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
377
378    RetainPtr<CFStringRef> url(AdoptCF, pageURL.createCFString());
379    CFDictionaryAddValue(dictionary.get(), WebIconDatabase::iconDatabaseNotificationUserInfoURLKey(), url.get());
380
381    COMPtr<CFDictionaryPropertyBag> userInfo = CFDictionaryPropertyBag::createInstance();
382    userInfo->setDictionary(dictionary.get());
383
384    IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
385    notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidAddIconNotification(), static_cast<IWebIconDatabase*>(iconDB), userInfo.get());
386}
387
388void WebIconDatabase::deliverNotifications(void*)
389{
390    ASSERT(m_sharedWebIconDatabase);
391    if (!m_sharedWebIconDatabase)
392        return;
393
394    ASSERT(m_sharedWebIconDatabase->m_deliveryRequested);
395
396    Vector<String> queue;
397    {
398        MutexLocker locker(m_sharedWebIconDatabase->m_notificationMutex);
399        queue.swap(m_sharedWebIconDatabase->m_notificationQueue);
400        m_sharedWebIconDatabase->m_deliveryRequested = false;
401    }
402
403    for (unsigned i = 0; i < queue.size(); ++i) {
404        if (queue[i].isNull())
405            postDidRemoveAllIconsNotification(m_sharedWebIconDatabase);
406        else
407            postDidAddIconNotification(queue[i], m_sharedWebIconDatabase);
408    }
409}
410