diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/API/gtk/WebKitFaviconDatabase.cpp')
-rw-r--r-- | Source/WebKit2/UIProcess/API/gtk/WebKitFaviconDatabase.cpp | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/API/gtk/WebKitFaviconDatabase.cpp b/Source/WebKit2/UIProcess/API/gtk/WebKitFaviconDatabase.cpp new file mode 100644 index 000000000..dc320bb77 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/gtk/WebKitFaviconDatabase.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitFaviconDatabase.h" + +#include "WebKitFaviconDatabasePrivate.h" +#include "WebKitPrivate.h" +#include <WebCore/FileSystem.h> +#include <WebCore/Image.h> +#include <WebCore/IntSize.h> +#include <WebCore/RefPtrCairo.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; + +enum { + ICON_READY, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + +typedef Vector<GRefPtr<GSimpleAsyncResult> > PendingIconRequestVector; +typedef HashMap<String, PendingIconRequestVector*> PendingIconRequestMap; + +struct _WebKitFaviconDatabasePrivate { + RefPtr<WebIconDatabase> iconDatabase; + PendingIconRequestMap pendingIconRequests; +}; + +G_DEFINE_TYPE(WebKitFaviconDatabase, webkit_favicon_database, G_TYPE_OBJECT) + +static void webkit_favicon_database_init(WebKitFaviconDatabase* manager) +{ + WebKitFaviconDatabasePrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(manager, WEBKIT_TYPE_FAVICON_DATABASE, WebKitFaviconDatabasePrivate); + manager->priv = priv; + new (priv) WebKitFaviconDatabasePrivate(); +} + +static void webkitFaviconDatabaseDispose(GObject* object) +{ + WebKitFaviconDatabase* database = WEBKIT_FAVICON_DATABASE(object); + + WebKitFaviconDatabasePrivate* priv = database->priv; + if (priv->iconDatabase->isOpen()) + priv->iconDatabase->close(); + + G_OBJECT_CLASS(webkit_favicon_database_parent_class)->dispose(object); +} + +static void webkitFaviconDatabaseFinalize(GObject* object) +{ + WebKitFaviconDatabase* database = WEBKIT_FAVICON_DATABASE(object); + database->priv->~WebKitFaviconDatabasePrivate(); + G_OBJECT_CLASS(webkit_favicon_database_parent_class)->finalize(object); +} + +static void webkit_favicon_database_class_init(WebKitFaviconDatabaseClass* faviconDatabaseClass) +{ + GObjectClass* gObjectClass = G_OBJECT_CLASS(faviconDatabaseClass); + gObjectClass->dispose = webkitFaviconDatabaseDispose; + gObjectClass->finalize = webkitFaviconDatabaseFinalize; + + /** + * WebKitFaviconDatabase::favicon-ready: + * @database: the object on which the signal is emitted + * @page_uri: the URI of the Web page containing the icon. + * + * This signal gets emitted when the favicon of @page_uri is + * ready. This means that the favicon's data is ready to be used + * by the application, either because it has been loaded from the + * network, if it's the first time it gets retrieved, or because + * it has been already imported from the icon database. + */ + signals[ICON_READY] = + g_signal_new("favicon-ready", + G_TYPE_FROM_CLASS(faviconDatabaseClass), + G_SIGNAL_RUN_LAST, + 0, 0, 0, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + g_type_class_add_private(faviconDatabaseClass, sizeof(WebKitFaviconDatabasePrivate)); +} + +struct GetFaviconSurfaceAsyncData { + ~GetFaviconSurfaceAsyncData() + { + if (shouldReleaseIconForPageURL) + faviconDatabase->priv->iconDatabase->releaseIconForPageURL(pageURL); + } + + GRefPtr<WebKitFaviconDatabase> faviconDatabase; + String pageURL; + RefPtr<cairo_surface_t> icon; + GRefPtr<GCancellable> cancellable; + bool shouldReleaseIconForPageURL; +}; +WEBKIT_DEFINE_ASYNC_DATA_STRUCT(GetFaviconSurfaceAsyncData) + +static cairo_surface_t* getIconSurfaceSynchronously(WebKitFaviconDatabase* database, const String& pageURL, GError** error) +{ + ASSERT(isMainThread()); + + // The exact size we pass is irrelevant to the iconDatabase code. + // We must pass something greater than 0x0 to get an icon. + WebCore::Image* iconImage = database->priv->iconDatabase->imageForPageURL(pageURL, WebCore::IntSize(1, 1)); + if (!iconImage) { + g_set_error(error, WEBKIT_FAVICON_DATABASE_ERROR, WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_UNKNOWN, _("Unknown favicon for page %s"), pageURL.utf8().data()); + return 0; + } + + WebCore::NativeImagePtr icon = iconImage->nativeImageForCurrentFrame(); + if (!icon) { + g_set_error(error, WEBKIT_FAVICON_DATABASE_ERROR, WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_NOT_FOUND, _("Page %s does not have a favicon"), pageURL.utf8().data()); + return 0; + } + + return icon->surface(); +} + +static void deletePendingIconRequests(WebKitFaviconDatabase* database, PendingIconRequestVector* requests, const String& pageURL) +{ + database->priv->pendingIconRequests.remove(pageURL); + delete requests; +} + +static void processPendingIconsForPageURL(WebKitFaviconDatabase* database, const String& pageURL) +{ + PendingIconRequestVector* pendingIconRequests = database->priv->pendingIconRequests.get(pageURL); + if (!pendingIconRequests) + return; + + GOwnPtr<GError> error; + RefPtr<cairo_surface_t> icon = getIconSurfaceSynchronously(database, pageURL, &error.outPtr()); + + for (size_t i = 0; i < pendingIconRequests->size(); ++i) { + GSimpleAsyncResult* result = pendingIconRequests->at(i).get(); + GetFaviconSurfaceAsyncData* data = static_cast<GetFaviconSurfaceAsyncData*>(g_simple_async_result_get_op_res_gpointer(result)); + if (!g_cancellable_is_cancelled(data->cancellable.get())) { + if (error) + g_simple_async_result_take_error(result, error.release()); + else { + data->icon = icon; + data->shouldReleaseIconForPageURL = false; + } + } + + g_simple_async_result_complete(result); + } + deletePendingIconRequests(database, pendingIconRequests, pageURL); +} + +static void iconDataReadyForPageURLCallback(WKIconDatabaseRef wkIconDatabase, WKURLRef wkPageURL, const void* clientInfo) +{ + ASSERT(isMainThread()); + + WebKitFaviconDatabase* database = WEBKIT_FAVICON_DATABASE(clientInfo); + String pageURL = toImpl(wkPageURL)->string(); + + processPendingIconsForPageURL(database, pageURL); + g_signal_emit(database, signals[ICON_READY], 0, pageURL.utf8().data()); +} + +WebKitFaviconDatabase* webkitFaviconDatabaseCreate(WebIconDatabase* iconDatabase) +{ + WebKitFaviconDatabase* faviconDatabase = WEBKIT_FAVICON_DATABASE(g_object_new(WEBKIT_TYPE_FAVICON_DATABASE, NULL)); + faviconDatabase->priv->iconDatabase = iconDatabase; + + WKIconDatabaseClient wkIconDatabaseClient = { + kWKIconDatabaseClientCurrentVersion, + faviconDatabase, // clientInfo + 0, // didChangeIconForPageURLCallback + 0, // didRemoveAllIconsCallback + iconDataReadyForPageURLCallback, + }; + WKIconDatabaseSetIconDatabaseClient(toAPI(iconDatabase), &wkIconDatabaseClient); + return faviconDatabase; +} + +cairo_surface_t* webkitFaviconDatabaseGetFavicon(WebKitFaviconDatabase* database, const CString& pageURL) +{ + ASSERT(WEBKIT_IS_FAVICON_DATABASE(database)); + ASSERT(!pageURL.isNull()); + + cairo_surface_t* iconSurface = getIconSurfaceSynchronously(database, String::fromUTF8(pageURL.data()), 0); + if (!iconSurface) + return 0; + + return cairo_surface_reference(iconSurface); +} + +static PendingIconRequestVector* getOrCreatePendingIconRequests(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 setErrorForAsyncResult(GSimpleAsyncResult* result, WebKitFaviconDatabaseError error, const String& pageURL = String()) +{ + ASSERT(result); + + switch (error) { + case WEBKIT_FAVICON_DATABASE_ERROR_NOT_INITIALIZED: + g_simple_async_result_set_error(result, WEBKIT_FAVICON_DATABASE_ERROR, error, _("Favicons database not initialized yet")); + break; + + case WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_NOT_FOUND: + g_simple_async_result_set_error(result, WEBKIT_FAVICON_DATABASE_ERROR, error, _("Page %s does not have a favicon"), pageURL.utf8().data()); + break; + + case WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_UNKNOWN: + g_simple_async_result_set_error(result, WEBKIT_FAVICON_DATABASE_ERROR, error, _("Unknown favicon for page %s"), pageURL.utf8().data()); + break; + + default: + ASSERT_NOT_REACHED(); + } +} + +GQuark webkit_favicon_database_error_quark(void) +{ + return g_quark_from_static_string("WebKitFaviconDatabaseError"); +} + +/** + * webkit_favicon_database_get_favicon: + * @database: a #WebKitFaviconDatabase + * @page_uri: URI of the page for which we want to retrieve the favicon + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @callback: (scope async): A #GAsyncReadyCallback to call when the request is + * satisfied or %NULL if you don't care about the result. + * @user_data: (closure): The data to pass to @callback. + * + * Asynchronously obtains a #cairo_surface_t of the favicon for the + * given page URI. It returns the cached icon if it's in the database + * asynchronously waiting for the icon to be read from the database. + * + * This is an asynchronous method. When the operation is finished, callback will + * be invoked. You can then call webkit_favicon_database_get_favicon_finish() + * to get the result of the operation. + */ +void webkit_favicon_database_get_favicon(WebKitFaviconDatabase* database, const gchar* pageURI, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) +{ + g_return_if_fail(WEBKIT_IS_FAVICON_DATABASE(database)); + g_return_if_fail(pageURI); + + GRefPtr<GSimpleAsyncResult> result = adoptGRef(g_simple_async_result_new(G_OBJECT(database), callback, userData, reinterpret_cast<gpointer>(webkit_favicon_database_get_favicon))); + g_simple_async_result_set_check_cancellable(result.get(), cancellable); + + GetFaviconSurfaceAsyncData* data = createGetFaviconSurfaceAsyncData(); + g_simple_async_result_set_op_res_gpointer(result.get(), data, reinterpret_cast<GDestroyNotify>(destroyGetFaviconSurfaceAsyncData)); + data->faviconDatabase = database; + data->pageURL = String::fromUTF8(pageURI); + data->cancellable = cancellable; + + WebKitFaviconDatabasePrivate* priv = database->priv; + WebIconDatabase* iconDatabaseImpl = priv->iconDatabase.get(); + if (!iconDatabaseImpl->isOpen()) { + setErrorForAsyncResult(result.get(), WEBKIT_FAVICON_DATABASE_ERROR_NOT_INITIALIZED); + g_simple_async_result_complete_in_idle(result.get()); + return; + } + + if (data->pageURL.isEmpty() || data->pageURL.startsWith("about:")) { + setErrorForAsyncResult(result.get(), WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_NOT_FOUND, data->pageURL); + g_simple_async_result_complete_in_idle(result.get()); + return; + } + + priv->iconDatabase->retainIconForPageURL(data->pageURL); + + // We ask for the icon directly. If we don't get the icon data now, + // we'll be notified later (even if the database is still importing icons). + GOwnPtr<GError> error; + data->icon = getIconSurfaceSynchronously(database, data->pageURL, &error.outPtr()); + if (data->icon) { + g_simple_async_result_complete_in_idle(result.get()); + return; + } + + // At this point we still don't know whether we will get a valid icon for pageURL. + data->shouldReleaseIconForPageURL = true; + + if (g_error_matches(error.get(), WEBKIT_FAVICON_DATABASE_ERROR, WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_NOT_FOUND)) { + g_simple_async_result_take_error(result.get(), error.release()); + g_simple_async_result_complete_in_idle(result.get()); + return; + } + + // If there's not a valid icon, but there's an iconURL registered, + // or it's still not registered but the import process hasn't + // finished yet, we need to wait for iconDataReadyForPage to be + // called before making and informed decision. + String iconURLForPageURL; + iconDatabaseImpl->synchronousIconURLForPageURL(data->pageURL, iconURLForPageURL); + if (!iconURLForPageURL.isEmpty() || !iconDatabaseImpl->isUrlImportCompleted()) { + PendingIconRequestVector* icons = getOrCreatePendingIconRequests(database, data->pageURL); + ASSERT(icons); + icons->append(result); + return; + } + + setErrorForAsyncResult(result.get(), WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_UNKNOWN, data->pageURL); + g_simple_async_result_complete_in_idle(result.get()); +} + +/** + * webkit_favicon_database_get_favicon_finish: + * @database: a #WebKitFaviconDatabase + * @result: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to webkit_favicon_database_get_favicon() + * @error: (allow-none): Return location for error or %NULL. + * + * Finishes an operation started with webkit_favicon_database_get_favicon(). + * + * Returns: (transfer full): a new reference to a #cairo_surface_t, or + * %NULL in case of error. + */ +cairo_surface_t* webkit_favicon_database_get_favicon_finish(WebKitFaviconDatabase* database, GAsyncResult* result, GError** error) +{ + GSimpleAsyncResult* simpleResult = G_SIMPLE_ASYNC_RESULT(result); + g_warn_if_fail(g_simple_async_result_get_source_tag(simpleResult) == webkit_favicon_database_get_favicon); + + if (g_simple_async_result_propagate_error(simpleResult, error)) + return 0; + + GetFaviconSurfaceAsyncData* data = static_cast<GetFaviconSurfaceAsyncData*>(g_simple_async_result_get_op_res_gpointer(simpleResult)); + ASSERT(data); + return cairo_surface_reference(data->icon.get()); +} + +/** + * webkit_favicon_database_get_favicon_uri: + * @database: a #WebKitFaviconDatabase + * @page_uri: URI of the page containing the icon + * + * Obtains the URI of the favicon for the given @page_uri. + * + * Returns: a newly allocated URI for the favicon, or %NULL if the + * database doesn't have a favicon for @page_uri. + */ +gchar* webkit_favicon_database_get_favicon_uri(WebKitFaviconDatabase* database, const gchar* pageURL) +{ + g_return_val_if_fail(WEBKIT_IS_FAVICON_DATABASE(database), 0); + g_return_val_if_fail(pageURL, 0); + ASSERT(isMainThread()); + + String iconURLForPageURL; + database->priv->iconDatabase->synchronousIconURLForPageURL(String::fromUTF8(pageURL), iconURLForPageURL); + if (iconURLForPageURL.isEmpty()) + return 0; + + return g_strdup(iconURLForPageURL.utf8().data()); +} + +/** + * webkit_favicon_database_clear: + * @database: a #WebKitFaviconDatabase + * + * Clears all icons from the database. + */ +void webkit_favicon_database_clear(WebKitFaviconDatabase* database) +{ + g_return_if_fail(WEBKIT_IS_FAVICON_DATABASE(database)); + + database->priv->iconDatabase->removeAllIcons(); +} |