/* * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * 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 COMPUTER, 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 COMPUTER, 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. */ #include "config.h" #include "WebKitDLL.h" #include "WebIconDatabase.h" #include "CFDictionaryPropertyBag.h" #include "COMPtr.h" #include "WebPreferences.h" #include "WebNotificationCenter.h" #pragma warning(push, 0) #include #include #include #include #include #pragma warning(pop) #include "shlobj.h" using namespace WebCore; using namespace WTF; // WebIconDatabase ---------------------------------------------------------------- WebIconDatabase* WebIconDatabase::m_sharedWebIconDatabase = 0; WebIconDatabase::WebIconDatabase() : m_refCount(0) , m_deliveryRequested(false) { gClassCount++; } WebIconDatabase::~WebIconDatabase() { gClassCount--; } void WebIconDatabase::init() { WebPreferences* standardPrefs = WebPreferences::sharedStandardPreferences(); BOOL enabled = FALSE; if (FAILED(standardPrefs->iconDatabaseEnabled(&enabled))) { enabled = FALSE; LOG_ERROR("Unable to get icon database enabled preference"); } iconDatabase()->setEnabled(!!enabled); iconDatabase()->setClient(this); BSTR prefDatabasePath = 0; if (FAILED(standardPrefs->iconDatabaseLocation(&prefDatabasePath))) LOG_ERROR("Unable to get icon database location preference"); String databasePath(prefDatabasePath, SysStringLen(prefDatabasePath)); SysFreeString(prefDatabasePath); if (databasePath.isEmpty()) { databasePath = localUserSpecificStorageDirectory(); if (databasePath.isEmpty()) LOG_ERROR("Failed to construct default icon database path"); } if (!iconDatabase()->open(databasePath)) LOG_ERROR("Failed to open icon database path"); } WebIconDatabase* WebIconDatabase::createInstance() { WebIconDatabase* instance = new WebIconDatabase(); instance->AddRef(); return instance; } WebIconDatabase* WebIconDatabase::sharedWebIconDatabase() { if (m_sharedWebIconDatabase) { m_sharedWebIconDatabase->AddRef(); return m_sharedWebIconDatabase; } m_sharedWebIconDatabase = createInstance(); m_sharedWebIconDatabase->init(); return m_sharedWebIconDatabase; } // IUnknown ------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE WebIconDatabase::QueryInterface(REFIID riid, void** ppvObject) { *ppvObject = 0; if (IsEqualGUID(riid, IID_IUnknown)) *ppvObject = static_cast(this); else if (IsEqualGUID(riid, IID_IWebIconDatabase)) *ppvObject = static_cast(this); else return E_NOINTERFACE; AddRef(); return S_OK; } ULONG STDMETHODCALLTYPE WebIconDatabase::AddRef(void) { return ++m_refCount; } ULONG STDMETHODCALLTYPE WebIconDatabase::Release(void) { ULONG newRef = --m_refCount; if (!newRef) delete(this); return newRef; } // IWebIconDatabase -------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE WebIconDatabase::sharedIconDatabase( /* [retval][out] */ IWebIconDatabase** result) { *result = sharedWebIconDatabase(); return S_OK; } HRESULT STDMETHODCALLTYPE WebIconDatabase::iconForURL( /* [in] */ BSTR url, /* [optional][in] */ LPSIZE size, /* [optional][in] */ BOOL /*cache*/, /* [retval][out] */ OLE_HANDLE* bitmap) { IntSize intSize(*size); Image* icon = 0; if (url) icon = iconDatabase()->iconForPageURL(String(url, SysStringLen(url)), intSize); // Make sure we check for the case of an "empty image" if (icon && icon->width()) { *bitmap = (OLE_HANDLE)(ULONG64)getOrCreateSharedBitmap(size); if (!icon->getHBITMAPOfSize((HBITMAP)(ULONG64)*bitmap, size)) { LOG_ERROR("Failed to draw Image to HBITMAP"); *bitmap = 0; return E_FAIL; } return S_OK; } return defaultIconWithSize(size, bitmap); } HRESULT STDMETHODCALLTYPE WebIconDatabase::defaultIconWithSize( /* [in] */ LPSIZE size, /* [retval][out] */ OLE_HANDLE* result) { *result = (OLE_HANDLE)(ULONG64)getOrCreateDefaultIconBitmap(size); return S_OK; } HRESULT STDMETHODCALLTYPE WebIconDatabase::retainIconForURL( /* [in] */ BSTR url) { iconDatabase()->retainIconForPageURL(String(url, SysStringLen(url))); return S_OK; } HRESULT STDMETHODCALLTYPE WebIconDatabase::releaseIconForURL( /* [in] */ BSTR url) { iconDatabase()->releaseIconForPageURL(String(url, SysStringLen(url))); return S_OK; } HRESULT STDMETHODCALLTYPE WebIconDatabase::removeAllIcons(void) { iconDatabase()->removeAllIcons(); return S_OK; } HRESULT STDMETHODCALLTYPE WebIconDatabase::delayDatabaseCleanup(void) { IconDatabase::delayDatabaseCleanup(); return S_OK; } HRESULT STDMETHODCALLTYPE WebIconDatabase::allowDatabaseCleanup(void) { IconDatabase::allowDatabaseCleanup(); return S_OK; } HRESULT STDMETHODCALLTYPE WebIconDatabase::iconURLForURL( /* [in] */ BSTR url, /* [retval][out] */ BSTR* iconURL) { if (!url || !iconURL) return E_POINTER; BString iconURLBSTR(iconDatabase()->iconURLForPageURL(String(url, SysStringLen(url)))); *iconURL = iconURLBSTR.release(); return S_OK; } HBITMAP createDIB(LPSIZE size) { HBITMAP result; BITMAPINFO bmInfo = {0}; bmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmInfo.bmiHeader.biWidth = size->cx; bmInfo.bmiHeader.biHeight = size->cy; bmInfo.bmiHeader.biPlanes = 1; bmInfo.bmiHeader.biBitCount = 32; bmInfo.bmiHeader.biCompression = BI_RGB; HDC dc = GetDC(0); result = CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0); ReleaseDC(0, dc); return result; } HBITMAP WebIconDatabase::getOrCreateSharedBitmap(LPSIZE size) { HBITMAP result = m_sharedIconMap.get(*size); if (result) return result; result = createDIB(size); m_sharedIconMap.set(*size, result); return result; } HBITMAP WebIconDatabase::getOrCreateDefaultIconBitmap(LPSIZE size) { HBITMAP result = m_defaultIconMap.get(*size); if (result) return result; result = createDIB(size); static Image* defaultIconImage = 0; if (!defaultIconImage) { defaultIconImage = Image::loadPlatformResource("urlIcon"); } m_defaultIconMap.set(*size, result); if (!defaultIconImage->getHBITMAPOfSize(result, size)) { LOG_ERROR("Failed to draw Image to HBITMAP"); return 0; } return result; } // IconDatabaseClient void WebIconDatabase::dispatchDidRemoveAllIcons() { // Queueing the empty string is a special way of saying "this queued notification is the didRemoveAllIcons notification" MutexLocker locker(m_notificationMutex); m_notificationQueue.append(String()); scheduleNotificationDelivery(); } void WebIconDatabase::dispatchDidAddIconForPageURL(const String& pageURL) { MutexLocker locker(m_notificationMutex); m_notificationQueue.append(pageURL.copy()); scheduleNotificationDelivery(); } void WebIconDatabase::scheduleNotificationDelivery() { // Caller of this method must hold the m_notificationQueue lock ASSERT(!m_notificationMutex.tryLock()); if (!m_deliveryRequested) { m_deliveryRequested = true; callOnMainThread(WebIconDatabase::deliverNotifications); } } BSTR WebIconDatabase::iconDatabaseDidAddIconNotification() { static BSTR didAddIconName = SysAllocString(WebIconDatabaseDidAddIconNotification); return didAddIconName; } CFStringRef WebIconDatabase::iconDatabaseNotificationUserInfoURLKey() { static CFStringRef iconUserInfoURLKey = String(WebIconNotificationUserInfoURLKey).createCFString(); return iconUserInfoURLKey; } BSTR WebIconDatabase::iconDatabaseDidRemoveAllIconsNotification() { static BSTR didRemoveAllIconsName = SysAllocString(WebIconDatabaseDidRemoveAllIconsNotification); return didRemoveAllIconsName; } static void postDidRemoveAllIconsNotification(WebIconDatabase* iconDB) { IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal(); notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidRemoveAllIconsNotification(), static_cast(iconDB), 0); } static void postDidAddIconNotification(const String& pageURL, WebIconDatabase* iconDB) { RetainPtr dictionary(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); RetainPtr url(AdoptCF, pageURL.createCFString()); CFDictionaryAddValue(dictionary.get(), WebIconDatabase::iconDatabaseNotificationUserInfoURLKey(), url.get()); COMPtr userInfo = CFDictionaryPropertyBag::createInstance(); userInfo->setDictionary(dictionary.get()); IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal(); notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidAddIconNotification(), static_cast(iconDB), userInfo.get()); } void WebIconDatabase::deliverNotifications() { ASSERT(m_sharedWebIconDatabase); if (!m_sharedWebIconDatabase) return; ASSERT(m_sharedWebIconDatabase->m_deliveryRequested); Vector queue; { MutexLocker locker(m_sharedWebIconDatabase->m_notificationMutex); queue.swap(m_sharedWebIconDatabase->m_notificationQueue); m_sharedWebIconDatabase->m_deliveryRequested = false; } for (unsigned i = 0; i < queue.size(); ++i) { if (queue[i].isNull()) postDidRemoveAllIconsNotification(m_sharedWebIconDatabase); else postDidAddIconNotification(queue[i], m_sharedWebIconDatabase); } }