| /* |
| * Copyright (C) 2006, 2007, 2008, 2009 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 "WebPreferences.h" |
| #include "WebNotificationCenter.h" |
| #include <WebCore/BitmapInfo.h> |
| #include <WebCore/BString.h> |
| #include <WebCore/COMPtr.h> |
| #include <WebCore/FileSystem.h> |
| #include <WebCore/IconDatabase.h> |
| #include <WebCore/Image.h> |
| #include <WebCore/PlatformString.h> |
| #include <WebCore/SharedBuffer.h> |
| #include <wtf/MainThread.h> |
| #include "shlobj.h" |
| |
| using namespace WebCore; |
| using namespace WTF; |
| |
| // WebIconDatabase ---------------------------------------------------------------- |
| |
| WebIconDatabase* WebIconDatabase::m_sharedWebIconDatabase = 0; |
| |
| WebIconDatabase::WebIconDatabase() |
| : m_refCount(0) |
| , m_deliveryRequested(false) |
| { |
| gClassCount++; |
| gClassNameCount.add("WebIconDatabase"); |
| } |
| |
| WebIconDatabase::~WebIconDatabase() |
| { |
| gClassCount--; |
| gClassNameCount.remove("WebIconDatabase"); |
| } |
| |
| 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); |
| if (!(!!enabled)) |
| return; |
| |
| startUpIconDatabase(); |
| } |
| |
| void WebIconDatabase::startUpIconDatabase() |
| { |
| WebPreferences* standardPrefs = WebPreferences::sharedStandardPreferences(); |
| |
| 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, WebCore::IconDatabase::defaultDatabaseFilename())) |
| LOG_ERROR("Failed to open icon database path"); |
| } |
| |
| void WebIconDatabase::shutDownIconDatabase() |
| { |
| } |
| |
| 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<IWebIconDatabase*>(this); |
| else if (IsEqualGUID(riid, IID_IWebIconDatabase)) |
| *ppvObject = static_cast<IWebIconDatabase*>(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().synchronousIconForPageURL(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().synchronousIconURLForPageURL(String(url, SysStringLen(url)))); |
| *iconURL = iconURLBSTR.release(); |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebIconDatabase::isEnabled( |
| /* [retval][out] */ BOOL *result) |
| { |
| *result = iconDatabase().isEnabled(); |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebIconDatabase::setEnabled( |
| /* [in] */ BOOL flag) |
| { |
| BOOL currentlyEnabled; |
| isEnabled(¤tlyEnabled); |
| if (currentlyEnabled && !flag) { |
| iconDatabase().setEnabled(false); |
| shutDownIconDatabase(); |
| } else if (!currentlyEnabled && flag) { |
| iconDatabase().setEnabled(true); |
| startUpIconDatabase(); |
| } |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebIconDatabase::hasIconForURL( |
| /* [in] */ BSTR url, |
| /* [out][retval] */ BOOL* result) |
| { |
| if (!url || !result) |
| return E_POINTER; |
| |
| String urlString(url, SysStringLen(url)); |
| |
| // Passing a size parameter of 0, 0 means we don't care about the result of the image, we just |
| // want to make sure the read from disk to load the icon is kicked off. |
| iconDatabase().synchronousIconForPageURL(urlString, IntSize(0, 0)); |
| |
| // Check to see if we have a non-empty icon URL for the page, and if we do, we have an icon for |
| // the page. |
| *result = !(iconDatabase().synchronousIconURLForPageURL(urlString).isEmpty()); |
| |
| return S_OK; |
| } |
| |
| HBITMAP createDIB(LPSIZE size) |
| { |
| BitmapInfo bmInfo = BitmapInfo::create(IntSize(*size)); |
| |
| HDC dc = GetDC(0); |
| HBITMAP 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); |
| |
| m_defaultIconMap.set(*size, result); |
| if (!iconDatabase().defaultIcon(*size)->getHBITMAPOfSize(result, size)) { |
| LOG_ERROR("Failed to draw Image to HBITMAP"); |
| return 0; |
| } |
| return result; |
| } |
| |
| // IconDatabaseClient |
| |
| bool WebIconDatabase::performImport() |
| { |
| // Windows doesn't do any old-style database importing. |
| return true; |
| } |
| |
| void WebIconDatabase::didRemoveAllIcons() |
| { |
| // 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::didImportIconURLForPageURL(const WTF::String& pageURL) |
| { |
| MutexLocker locker(m_notificationMutex); |
| m_notificationQueue.append(pageURL.threadsafeCopy()); |
| scheduleNotificationDelivery(); |
| } |
| |
| void WebIconDatabase::didImportIconDataForPageURL(const WTF::String& pageURL) |
| { |
| // WebKit1 only has a single "icon did change" notification. |
| didImportIconURLForPageURL(pageURL); |
| } |
| |
| void WebIconDatabase::didChangeIconForPageURL(const WTF::String& pageURL) |
| { |
| // WebKit1 only has a single "icon did change" notification. |
| didImportIconURLForPageURL(pageURL); |
| } |
| |
| void WebIconDatabase::didFinishURLImport() |
| { |
| } |
| |
| void WebIconDatabase::scheduleNotificationDelivery() |
| { |
| // Caller of this method must hold the m_notificationQueue lock |
| ASSERT(!m_notificationMutex.tryLock()); |
| |
| if (!m_deliveryRequested) { |
| m_deliveryRequested = true; |
| callOnMainThread(deliverNotifications, 0); |
| } |
| } |
| |
| 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<IWebIconDatabase*>(iconDB), 0); |
| } |
| |
| static void postDidAddIconNotification(const String& pageURL, WebIconDatabase* iconDB) |
| { |
| RetainPtr<CFMutableDictionaryRef> dictionary(AdoptCF, |
| CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); |
| |
| RetainPtr<CFStringRef> url(AdoptCF, pageURL.createCFString()); |
| CFDictionaryAddValue(dictionary.get(), WebIconDatabase::iconDatabaseNotificationUserInfoURLKey(), url.get()); |
| |
| COMPtr<CFDictionaryPropertyBag> userInfo = CFDictionaryPropertyBag::createInstance(); |
| userInfo->setDictionary(dictionary.get()); |
| |
| IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal(); |
| notifyCenter->postNotificationName(WebIconDatabase::iconDatabaseDidAddIconNotification(), static_cast<IWebIconDatabase*>(iconDB), userInfo.get()); |
| } |
| |
| void WebIconDatabase::deliverNotifications(void*) |
| { |
| ASSERT(m_sharedWebIconDatabase); |
| if (!m_sharedWebIconDatabase) |
| return; |
| |
| ASSERT(m_sharedWebIconDatabase->m_deliveryRequested); |
| |
| Vector<String> 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); |
| } |
| } |