webkitfavicondatabase.cpp [plain text]
#include "config.h"
#include "webkitfavicondatabase.h"
#include "DatabaseDetails.h"
#include "DatabaseTracker.h"
#include "FileSystem.h"
#include "IconDatabase.h"
#include "IconDatabaseClient.h"
#include "Image.h"
#include "IntSize.h"
#include "webkitfavicondatabaseprivate.h"
#include "webkitglobals.h"
#include "webkitglobalsprivate.h"
#include "webkitmarshal.h"
#include "webkitsecurityoriginprivate.h"
#include "webkitwebframe.h"
#include <glib/gi18n-lib.h>
#include <wtf/MainThread.h>
#include <wtf/gobject/GOwnPtr.h>
#include <wtf/gobject/GRefPtr.h>
#include <wtf/text/CString.h>
using namespace WebKit;
using namespace WebCore;
class PendingIconRequest;
static void webkitFaviconDatabaseProcessPendingIconsForURI(WebKitFaviconDatabase*, const String& pageURI);
static void webkitFaviconDatabaseImportFinished(WebKitFaviconDatabase*);
static void webkitFaviconDatabaseGetIconPixbufCancelled(GCancellable*, PendingIconRequest*);
static void webkitFaviconDatabaseClose(WebKitFaviconDatabase* database);
class IconDatabaseClientGtk : public IconDatabaseClient {
public:
virtual bool performImport() { return true; }
virtual void didRemoveAllIcons() { };
virtual void didImportIconURLForPageURL(const String& URL) { };
virtual void didImportIconDataForPageURL(const String& URL)
{
WebKitFaviconDatabase* database = webkit_get_favicon_database();
g_signal_emit_by_name(database, "icon-loaded", URL.utf8().data());
webkitFaviconDatabaseProcessPendingIconsForURI(database, URL);
}
virtual void didChangeIconForPageURL(const String& URL)
{
}
virtual void didFinishURLImport()
{
webkitFaviconDatabaseImportFinished(webkit_get_favicon_database());
IconDatabase::allowDatabaseCleanup();
}
};
class PendingIconRequest {
public:
PendingIconRequest(const String& pageURL, GSimpleAsyncResult* result, GCancellable* cancellable, IntSize iconSize)
: m_pageURL(pageURL)
, m_asyncResult(result)
, m_cancellable(cancellable)
, m_cancelledId(0)
, m_iconSize(iconSize)
{
if (cancellable) {
m_cancelledId = g_cancellable_connect(cancellable, G_CALLBACK(webkitFaviconDatabaseGetIconPixbufCancelled), this, 0);
g_object_set_data_full(G_OBJECT(result), "cancellable", g_object_ref(cancellable), static_cast<GDestroyNotify>(g_object_unref));
}
}
~PendingIconRequest()
{
if (m_cancelledId > 0)
g_cancellable_disconnect(m_cancellable.get(), m_cancelledId);
}
const String& pageURL() { return m_pageURL; }
GSimpleAsyncResult* asyncResult() { return m_asyncResult.get(); }
const IntSize& iconSize() { return m_iconSize; }
void asyncResultCancel()
{
ASSERT(m_asyncResult);
g_simple_async_result_set_error(m_asyncResult.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED, "%s", _("Operation was cancelled"));
g_simple_async_result_complete(m_asyncResult.get());
}
void asyncResultCompleteInIdle(GdkPixbuf* icon)
{
ASSERT(m_asyncResult);
g_simple_async_result_set_op_res_gpointer(m_asyncResult.get(), icon, 0);
g_simple_async_result_complete_in_idle(m_asyncResult.get());
}
void asyncResultComplete(GdkPixbuf* icon)
{
ASSERT(m_asyncResult);
g_simple_async_result_set_op_res_gpointer(m_asyncResult.get(), icon, 0);
g_simple_async_result_complete(m_asyncResult.get());
}
private:
String m_pageURL;
GRefPtr<GSimpleAsyncResult> m_asyncResult;
GRefPtr<GCancellable> m_cancellable;
gulong m_cancelledId;
IntSize m_iconSize;
};
enum {
PROP_0,
PROP_PATH,
};
enum {
ICON_LOADED,
LAST_SIGNAL
};
static guint webkit_favicon_database_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE(WebKitFaviconDatabase, webkit_favicon_database, G_TYPE_OBJECT)
typedef Vector<OwnPtr<PendingIconRequest> > PendingIconRequestVector;
typedef HashMap<String, PendingIconRequestVector*> PendingIconRequestMap;
struct _WebKitFaviconDatabasePrivate {
GOwnPtr<gchar> path;
IconDatabaseClientGtk iconDatabaseClient;
PendingIconRequestMap pendingIconRequests;
bool importFinished;
};
static void webkit_favicon_database_finalize(GObject* object)
{
WebKitFaviconDatabase* database = WEBKIT_FAVICON_DATABASE(object);
webkitFaviconDatabaseClose(database);
database->priv->~WebKitFaviconDatabasePrivate();
G_OBJECT_CLASS(webkit_favicon_database_parent_class)->finalize(object);
}
static void webkit_favicon_database_set_property(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
{
WebKitFaviconDatabase* database = WEBKIT_FAVICON_DATABASE(object);
switch (propId) {
case PROP_PATH:
webkit_favicon_database_set_path(database, g_value_get_string(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
break;
}
}
static void webkit_favicon_database_get_property(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
{
WebKitFaviconDatabase* database = WEBKIT_FAVICON_DATABASE(object);
switch (propId) {
case PROP_PATH:
g_value_set_string(value, webkit_favicon_database_get_path(database));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
break;
}
}
static void webkit_favicon_database_class_init(WebKitFaviconDatabaseClass* klass)
{
webkitInit();
GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
gobjectClass->finalize = webkit_favicon_database_finalize;
gobjectClass->set_property = webkit_favicon_database_set_property;
gobjectClass->get_property = webkit_favicon_database_get_property;
g_object_class_install_property(gobjectClass, PROP_PATH,
g_param_spec_string("path",
_("Path"),
_("The absolute path of the icon database folder"),
NULL,
WEBKIT_PARAM_READWRITE));
webkit_favicon_database_signals[ICON_LOADED] = g_signal_new("icon-loaded",
G_TYPE_FROM_CLASS(klass),
(GSignalFlags)G_SIGNAL_RUN_LAST,
0, 0, 0,
webkit_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
g_type_class_add_private(klass, sizeof(WebKitFaviconDatabasePrivate));
}
static void webkit_favicon_database_init(WebKitFaviconDatabase* database)
{
database->priv = G_TYPE_INSTANCE_GET_PRIVATE(database, WEBKIT_TYPE_FAVICON_DATABASE, WebKitFaviconDatabasePrivate);
new (database->priv) WebKitFaviconDatabasePrivate();
}
void webkitFaviconDatabaseDispatchDidReceiveIcon(WebKitFaviconDatabase* database, const char* frameURI)
{
g_signal_emit(database, webkit_favicon_database_signals[ICON_LOADED], 0, frameURI);
iconDatabase().retainIconForPageURL(String::fromUTF8(frameURI));
}
const gchar* webkit_favicon_database_get_path(WebKitFaviconDatabase* database)
{
g_return_val_if_fail(WEBKIT_IS_FAVICON_DATABASE(database), 0);
return database->priv->path.get();
}
static void webkitFaviconDatabaseClose(WebKitFaviconDatabase* database)
{
if (iconDatabase().isEnabled()) {
iconDatabase().setEnabled(false);
iconDatabase().close();
}
}
void webkit_favicon_database_set_path(WebKitFaviconDatabase* database, const gchar* path)
{
g_return_if_fail(WEBKIT_IS_FAVICON_DATABASE(database));
webkitFaviconDatabaseClose(database);
database->priv->importFinished = false;
if (!path || !path[0]) {
database->priv->path.set(0);
iconDatabase().setEnabled(false);
return;
}
iconDatabase().setClient(&database->priv->iconDatabaseClient);
IconDatabase::delayDatabaseCleanup();
iconDatabase().setEnabled(true);
if (!iconDatabase().open(filenameToString(path), IconDatabase::defaultDatabaseFilename())) {
IconDatabase::allowDatabaseCleanup();
return;
}
database->priv->path.set(g_strdup(path));
}
gchar* webkit_favicon_database_get_favicon_uri(WebKitFaviconDatabase* database, const gchar* pageURI)
{
g_return_val_if_fail(WEBKIT_IS_FAVICON_DATABASE(database), 0);
g_return_val_if_fail(pageURI, 0);
ASSERT(isMainThread());
String iconURI = iconDatabase().synchronousIconURLForPageURL(String::fromUTF8(pageURI));
if (iconURI.isEmpty())
return 0;
return g_strdup(iconURI.utf8().data());
}
static GdkPixbuf* getIconPixbufSynchronously(WebKitFaviconDatabase* database, const String& pageURL, const IntSize& iconSize)
{
ASSERT(isMainThread());
Image* icon = iconDatabase().synchronousIconForPageURL(pageURL, !iconSize.isZero() ? iconSize : IntSize(1, 1));
if (!icon)
return 0;
GRefPtr<GdkPixbuf> pixbuf = adoptGRef(icon->getGdkPixbuf());
if (!pixbuf)
return 0;
if (!iconSize.isZero() && (icon->width() != iconSize.width() || icon->height() != iconSize.height()))
pixbuf = gdk_pixbuf_scale_simple(pixbuf.get(), iconSize.width(), iconSize.height(), GDK_INTERP_BILINEAR);
return pixbuf.leakRef();
}
GdkPixbuf* webkit_favicon_database_try_get_favicon_pixbuf(WebKitFaviconDatabase* database, const gchar* pageURI, guint width, guint height)
{
g_return_val_if_fail(WEBKIT_IS_FAVICON_DATABASE(database), 0);
g_return_val_if_fail(pageURI, 0);
g_return_val_if_fail((width && height) || (!width && !height), 0);
return getIconPixbufSynchronously(database, String::fromUTF8(pageURI), IntSize(width, height));
}
static PendingIconRequestVector* webkitFaviconDatabaseGetOrCreateRequests(WebKitFaviconDatabase* database, const String& pageURL)
{
PendingIconRequestVector* icons = database->priv->pendingIconRequests.get(pageURL);
if (!icons) {
icons = new PendingIconRequestVector;
database->priv->pendingIconRequests.set(pageURL, icons);
}
return icons;
}
static void webkitfavicondatabaseDeleteRequests(WebKitFaviconDatabase* database, PendingIconRequestVector* requests, const String& pageURL)
{
database->priv->pendingIconRequests.remove(pageURL);
delete requests;
}
static void getIconPixbufCancelled(void* userData)
{
PendingIconRequest* request = static_cast<PendingIconRequest*>(userData);
request->asyncResultCancel();
const String& pageURL = request->pageURL();
WebKitFaviconDatabase* database = webkit_get_favicon_database();
PendingIconRequestVector* icons = database->priv->pendingIconRequests.get(pageURL);
if (!icons)
return;
size_t itemIndex = icons->find(request);
if (itemIndex != notFound)
icons->remove(itemIndex);
if (icons->isEmpty())
webkitfavicondatabaseDeleteRequests(database, icons, pageURL);
}
static void webkitFaviconDatabaseGetIconPixbufCancelled(GCancellable* cancellable, PendingIconRequest* request)
{
callOnMainThread(getIconPixbufCancelled, request);
}
void webkit_favicon_database_get_favicon_pixbuf(WebKitFaviconDatabase* database, const gchar* pageURI, guint width, guint height, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
{
g_return_if_fail(WEBKIT_IS_FAVICON_DATABASE(database));
g_return_if_fail(pageURI);
g_return_if_fail((width && height) || (!width && !height));
GRefPtr<GSimpleAsyncResult> result = adoptGRef(g_simple_async_result_new(G_OBJECT(database), callback, userData,
reinterpret_cast<gpointer>(webkit_favicon_database_get_favicon_pixbuf)));
if ((database->priv->importFinished && iconDatabase().synchronousIconURLForPageURL(String::fromUTF8(pageURI)).isEmpty())
|| !iconDatabase().isOpen()) {
g_simple_async_result_set_op_res_gpointer(result.get(), 0, 0);
g_simple_async_result_complete_in_idle(result.get());
return;
}
String pageURL = String::fromUTF8(pageURI);
PendingIconRequest* request = new PendingIconRequest(pageURL, result.get(), cancellable, IntSize(width, height));
PendingIconRequestVector* icons = webkitFaviconDatabaseGetOrCreateRequests(database, pageURL);
ASSERT(icons);
icons->append(adoptPtr(request));
GdkPixbuf* pixbuf = getIconPixbufSynchronously(database, pageURL, IntSize(width, height));
if (!pixbuf)
return;
request->asyncResultCompleteInIdle(pixbuf);
ASSERT(icons->last().get() == request);
icons->removeLast();
if (icons->isEmpty())
webkitfavicondatabaseDeleteRequests(database, icons, pageURL);
}
GdkPixbuf* webkit_favicon_database_get_favicon_pixbuf_finish(WebKitFaviconDatabase* database, GAsyncResult* result, GError** error)
{
GSimpleAsyncResult* simpleResult = G_SIMPLE_ASYNC_RESULT(result);
g_return_val_if_fail(g_simple_async_result_get_source_tag(simpleResult) == webkit_favicon_database_get_favicon_pixbuf, 0);
if (g_simple_async_result_propagate_error(simpleResult, error))
return 0;
GCancellable* cancellable = static_cast<GCancellable*>(g_object_get_data(G_OBJECT(simpleResult), "cancellable"));
if (cancellable && g_cancellable_is_cancelled(cancellable)) {
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled"));
return 0;
}
GdkPixbuf* icon = static_cast<GdkPixbuf*>(g_simple_async_result_get_op_res_gpointer(simpleResult));
if (!icon)
return 0;
return static_cast<GdkPixbuf*>(icon);
}
static void webkitFaviconDatabaseProcessPendingIconsForURI(WebKitFaviconDatabase* database, const String& pageURL)
{
PendingIconRequestVector* icons = database->priv->pendingIconRequests.get(pageURL);
if (!icons)
return;
for (size_t i = 0; i < icons->size(); ++i) {
PendingIconRequest* request = icons->at(i).get();
if (request->asyncResult())
request->asyncResultComplete(getIconPixbufSynchronously(database, pageURL, request->iconSize()));
}
webkitfavicondatabaseDeleteRequests(database, icons, pageURL);
}
static void webkitFaviconDatabaseImportFinished(WebKitFaviconDatabase* database)
{
ASSERT(isMainThread());
database->priv->importFinished = true;
Vector<String> toDeleteURLs;
PendingIconRequestMap::const_iterator end = database->priv->pendingIconRequests.end();
for (PendingIconRequestMap::const_iterator iter = database->priv->pendingIconRequests.begin(); iter != end; ++iter) {
String iconURL = iconDatabase().synchronousIconURLForPageURL(iter->first);
if (!iconURL.isEmpty())
continue;
PendingIconRequestVector* icons = iter->second;
for (size_t i = 0; i < icons->size(); ++i) {
PendingIconRequest* request = icons->at(i).get();
if (request->asyncResult())
request->asyncResultComplete(0);
}
toDeleteURLs.append(iter->first);
}
for (size_t i = 0; i < toDeleteURLs.size(); ++i)
webkitfavicondatabaseDeleteRequests(database, database->priv->pendingIconRequests.get(toDeleteURLs[i]), toDeleteURLs[i]);
}
void webkit_favicon_database_clear(WebKitFaviconDatabase* database)
{
g_return_if_fail(WEBKIT_IS_FAVICON_DATABASE(database));
iconDatabase().removeAllIcons();
}