| 1 | /* |
| 2 | * Copyright (C) 2012, 2017 Igalia S.L. |
| 3 | * |
| 4 | * This library is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Lesser General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2,1 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This library is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | * Library General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Library General Public License |
| 15 | * along with this library; see the file COPYING.LIB. If not, write to |
| 16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 17 | * Boston, MA 02110-1301, USA. |
| 18 | */ |
| 19 | |
| 20 | #include "config.h" |
| 21 | #include "WebKitFaviconDatabase.h" |
| 22 | |
| 23 | #include "IconDatabase.h" |
| 24 | #include "WebKitFaviconDatabasePrivate.h" |
| 25 | #include "WebPreferences.h" |
| 26 | #include <WebCore/Image.h> |
| 27 | #include <WebCore/IntSize.h> |
| 28 | #include <WebCore/RefPtrCairo.h> |
| 29 | #include <WebCore/SharedBuffer.h> |
| 30 | #include <glib/gi18n-lib.h> |
| 31 | #include <wtf/FileSystem.h> |
| 32 | #include <wtf/RunLoop.h> |
| 33 | #include <wtf/SetForScope.h> |
| 34 | #include <wtf/glib/GRefPtr.h> |
| 35 | #include <wtf/glib/GUniquePtr.h> |
| 36 | #include <wtf/glib/WTFGType.h> |
| 37 | #include <wtf/text/CString.h> |
| 38 | #include <wtf/text/StringHash.h> |
| 39 | |
| 40 | using namespace WebKit; |
| 41 | using namespace WebCore; |
| 42 | |
| 43 | /** |
| 44 | * SECTION: WebKitFaviconDatabase |
| 45 | * @Short_description: A WebKit favicon database |
| 46 | * @Title: WebKitFaviconDatabase |
| 47 | * |
| 48 | * #WebKitFaviconDatabase provides access to the icons associated with |
| 49 | * web sites. |
| 50 | * |
| 51 | * WebKit will automatically look for available icons in <link> |
| 52 | * elements on opened pages as well as an existing favicon.ico and |
| 53 | * load the images found into a memory cache if possible. That cache |
| 54 | * is frozen to an on-disk database for persistence. |
| 55 | * |
| 56 | * If #WebKitSettings:enable-private-browsing is %TRUE, new icons |
| 57 | * won't be added to the on-disk database and no existing icons will |
| 58 | * be deleted from it. Nevertheless, WebKit will still store them in |
| 59 | * the in-memory cache during the current execution. |
| 60 | * |
| 61 | */ |
| 62 | |
| 63 | enum { |
| 64 | FAVICON_CHANGED, |
| 65 | |
| 66 | LAST_SIGNAL |
| 67 | }; |
| 68 | |
| 69 | static guint signals[LAST_SIGNAL] = { 0, }; |
| 70 | |
| 71 | typedef Vector<GRefPtr<GTask> > PendingIconRequestVector; |
| 72 | typedef HashMap<String, PendingIconRequestVector*> PendingIconRequestMap; |
| 73 | |
| 74 | struct _WebKitFaviconDatabasePrivate { |
| 75 | std::unique_ptr<IconDatabase> iconDatabase; |
| 76 | Vector<std::pair<String, Function<void(bool)>>> pendingLoadDecisions; |
| 77 | PendingIconRequestMap pendingIconRequests; |
| 78 | HashMap<String, String> pageURLToIconURLMap; |
| 79 | bool isURLImportCompleted; |
| 80 | bool isSettingIcon; |
| 81 | }; |
| 82 | |
| 83 | WEBKIT_DEFINE_TYPE(WebKitFaviconDatabase, webkit_favicon_database, G_TYPE_OBJECT) |
| 84 | |
| 85 | static void webkitFaviconDatabaseDispose(GObject* object) |
| 86 | { |
| 87 | WebKitFaviconDatabase* database = WEBKIT_FAVICON_DATABASE(object); |
| 88 | |
| 89 | if (webkitFaviconDatabaseIsOpen(database)) |
| 90 | database->priv->iconDatabase->close(); |
| 91 | |
| 92 | G_OBJECT_CLASS(webkit_favicon_database_parent_class)->dispose(object); |
| 93 | } |
| 94 | |
| 95 | static void webkit_favicon_database_class_init(WebKitFaviconDatabaseClass* faviconDatabaseClass) |
| 96 | { |
| 97 | GObjectClass* gObjectClass = G_OBJECT_CLASS(faviconDatabaseClass); |
| 98 | gObjectClass->dispose = webkitFaviconDatabaseDispose; |
| 99 | |
| 100 | /** |
| 101 | * WebKitFaviconDatabase::favicon-changed: |
| 102 | * @database: the object on which the signal is emitted |
| 103 | * @page_uri: the URI of the Web page containing the icon |
| 104 | * @favicon_uri: the URI of the favicon |
| 105 | * |
| 106 | * This signal is emitted when the favicon URI of @page_uri has |
| 107 | * been changed to @favicon_uri in the database. You can connect |
| 108 | * to this signal and call webkit_favicon_database_get_favicon() |
| 109 | * to get the favicon. If you are interested in the favicon of a |
| 110 | * #WebKitWebView it's easier to use the #WebKitWebView:favicon |
| 111 | * property. See webkit_web_view_get_favicon() for more details. |
| 112 | */ |
| 113 | signals[FAVICON_CHANGED] = g_signal_new( |
| 114 | "favicon-changed" , |
| 115 | G_TYPE_FROM_CLASS(faviconDatabaseClass), |
| 116 | G_SIGNAL_RUN_LAST, |
| 117 | 0, nullptr, nullptr, |
| 118 | g_cclosure_marshal_generic, |
| 119 | G_TYPE_NONE, 2, |
| 120 | G_TYPE_STRING, |
| 121 | G_TYPE_STRING); |
| 122 | } |
| 123 | |
| 124 | #if PLATFORM(GTK) |
| 125 | struct GetFaviconSurfaceAsyncData { |
| 126 | ~GetFaviconSurfaceAsyncData() |
| 127 | { |
| 128 | if (shouldReleaseIconForPageURL) |
| 129 | faviconDatabase->priv->iconDatabase->releaseIconForPageURL(pageURL); |
| 130 | } |
| 131 | |
| 132 | GRefPtr<WebKitFaviconDatabase> faviconDatabase; |
| 133 | String pageURL; |
| 134 | RefPtr<cairo_surface_t> icon; |
| 135 | GRefPtr<GCancellable> cancellable; |
| 136 | bool shouldReleaseIconForPageURL; |
| 137 | }; |
| 138 | WEBKIT_DEFINE_ASYNC_DATA_STRUCT(GetFaviconSurfaceAsyncData) |
| 139 | |
| 140 | static RefPtr<cairo_surface_t> getIconSurfaceSynchronously(WebKitFaviconDatabase* database, const String& pageURL, GError** error) |
| 141 | { |
| 142 | ASSERT(RunLoop::isMain()); |
| 143 | |
| 144 | // The exact size we pass is irrelevant to the iconDatabase code. |
| 145 | // We must pass something greater than 0x0 to get an icon. |
| 146 | auto iconData = database->priv->iconDatabase->synchronousIconForPageURL(pageURL, WebCore::IntSize(1, 1)); |
| 147 | if (iconData.second == IconDatabase::IsKnownIcon::No) { |
| 148 | g_set_error(error, WEBKIT_FAVICON_DATABASE_ERROR, WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_UNKNOWN, _("Unknown favicon for page %s" ), pageURL.utf8().data()); |
| 149 | return nullptr; |
| 150 | } |
| 151 | |
| 152 | if (!iconData.first) { |
| 153 | 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()); |
| 154 | return nullptr; |
| 155 | } |
| 156 | |
| 157 | return iconData.first; |
| 158 | } |
| 159 | |
| 160 | static void deletePendingIconRequests(WebKitFaviconDatabase* database, PendingIconRequestVector* requests, const String& pageURL) |
| 161 | { |
| 162 | database->priv->pendingIconRequests.remove(pageURL); |
| 163 | delete requests; |
| 164 | } |
| 165 | |
| 166 | static void processPendingIconsForPageURL(WebKitFaviconDatabase* database, const String& pageURL) |
| 167 | { |
| 168 | PendingIconRequestVector* pendingIconRequests = database->priv->pendingIconRequests.get(pageURL); |
| 169 | if (!pendingIconRequests) |
| 170 | return; |
| 171 | |
| 172 | GUniqueOutPtr<GError> error; |
| 173 | RefPtr<cairo_surface_t> icon = getIconSurfaceSynchronously(database, pageURL, &error.outPtr()); |
| 174 | |
| 175 | for (size_t i = 0; i < pendingIconRequests->size(); ++i) { |
| 176 | GTask* task = pendingIconRequests->at(i).get(); |
| 177 | if (error) |
| 178 | g_task_return_error(task, error.release().release()); |
| 179 | else { |
| 180 | GetFaviconSurfaceAsyncData* data = static_cast<GetFaviconSurfaceAsyncData*>(g_task_get_task_data(task)); |
| 181 | data->icon = icon; |
| 182 | data->shouldReleaseIconForPageURL = false; |
| 183 | g_task_return_boolean(task, TRUE); |
| 184 | } |
| 185 | } |
| 186 | deletePendingIconRequests(database, pendingIconRequests, pageURL); |
| 187 | } |
| 188 | #endif |
| 189 | |
| 190 | static void webkitFaviconDatabaseSetIconURLForPageURL(WebKitFaviconDatabase* database, const String& iconURL, const String& pageURL) |
| 191 | { |
| 192 | WebKitFaviconDatabasePrivate* priv = database->priv; |
| 193 | if (!priv->isURLImportCompleted) |
| 194 | return; |
| 195 | |
| 196 | if (pageURL.isEmpty()) |
| 197 | return; |
| 198 | |
| 199 | const String& currentIconURL = priv->pageURLToIconURLMap.get(pageURL); |
| 200 | if (iconURL == currentIconURL) |
| 201 | return; |
| 202 | |
| 203 | priv->pageURLToIconURLMap.set(pageURL, iconURL); |
| 204 | if (priv->isSettingIcon) |
| 205 | return; |
| 206 | |
| 207 | g_signal_emit(database, signals[FAVICON_CHANGED], 0, pageURL.utf8().data(), iconURL.utf8().data()); |
| 208 | } |
| 209 | |
| 210 | class WebKitIconDatabaseClient final : public IconDatabaseClient { |
| 211 | public: |
| 212 | explicit WebKitIconDatabaseClient(WebKitFaviconDatabase* database) |
| 213 | : m_database(database) |
| 214 | { |
| 215 | } |
| 216 | |
| 217 | private: |
| 218 | void didImportIconURLForPageURL(const String& pageURL) override |
| 219 | { |
| 220 | String iconURL = m_database->priv->iconDatabase->synchronousIconURLForPageURL(pageURL); |
| 221 | webkitFaviconDatabaseSetIconURLForPageURL(m_database, iconURL, pageURL); |
| 222 | } |
| 223 | |
| 224 | void didChangeIconForPageURL(const String& pageURL) override |
| 225 | { |
| 226 | if (m_database->priv->isSettingIcon) |
| 227 | return; |
| 228 | String iconURL = m_database->priv->iconDatabase->synchronousIconURLForPageURL(pageURL); |
| 229 | webkitFaviconDatabaseSetIconURLForPageURL(m_database, iconURL, pageURL); |
| 230 | } |
| 231 | |
| 232 | void didImportIconDataForPageURL(const String& pageURL) override |
| 233 | { |
| 234 | #if PLATFORM(GTK) |
| 235 | processPendingIconsForPageURL(m_database, pageURL); |
| 236 | #endif |
| 237 | String iconURL = m_database->priv->iconDatabase->synchronousIconURLForPageURL(pageURL); |
| 238 | webkitFaviconDatabaseSetIconURLForPageURL(m_database, iconURL, pageURL); |
| 239 | } |
| 240 | |
| 241 | void didFinishURLImport() override |
| 242 | { |
| 243 | WebKitFaviconDatabasePrivate* priv = m_database->priv; |
| 244 | |
| 245 | while (!priv->pendingLoadDecisions.isEmpty()) { |
| 246 | auto iconURLAndCallback = priv->pendingLoadDecisions.takeLast(); |
| 247 | auto decision = priv->iconDatabase->synchronousLoadDecisionForIconURL(iconURLAndCallback.first); |
| 248 | // Decisions should never be unknown after the inital import is complete. |
| 249 | ASSERT(decision != IconDatabase::IconLoadDecision::Unknown); |
| 250 | iconURLAndCallback.second(decision == IconDatabase::IconLoadDecision::Yes); |
| 251 | } |
| 252 | |
| 253 | priv->isURLImportCompleted = true; |
| 254 | } |
| 255 | |
| 256 | WebKitFaviconDatabase* m_database; |
| 257 | }; |
| 258 | |
| 259 | WebKitFaviconDatabase* webkitFaviconDatabaseCreate() |
| 260 | { |
| 261 | return WEBKIT_FAVICON_DATABASE(g_object_new(WEBKIT_TYPE_FAVICON_DATABASE, nullptr)); |
| 262 | } |
| 263 | |
| 264 | void webkitFaviconDatabaseOpen(WebKitFaviconDatabase* database, const String& path) |
| 265 | { |
| 266 | if (webkitFaviconDatabaseIsOpen(database)) |
| 267 | return; |
| 268 | |
| 269 | WebKitFaviconDatabasePrivate* priv = database->priv; |
| 270 | priv->iconDatabase = std::make_unique<IconDatabase>(); |
| 271 | priv->iconDatabase->setClient(std::make_unique<WebKitIconDatabaseClient>(database)); |
| 272 | IconDatabase::delayDatabaseCleanup(); |
| 273 | priv->iconDatabase->setEnabled(true); |
| 274 | priv->iconDatabase->setPrivateBrowsingEnabled(WebPreferences::anyPagesAreUsingPrivateBrowsing()); |
| 275 | |
| 276 | if (!priv->iconDatabase->open(FileSystem::directoryName(path), FileSystem::pathGetFileName(path))) { |
| 277 | priv->iconDatabase = nullptr; |
| 278 | IconDatabase::allowDatabaseCleanup(); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | bool webkitFaviconDatabaseIsOpen(WebKitFaviconDatabase* database) |
| 283 | { |
| 284 | return database->priv->iconDatabase && database->priv->iconDatabase->isOpen(); |
| 285 | } |
| 286 | |
| 287 | void webkitFaviconDatabaseSetPrivateBrowsingEnabled(WebKitFaviconDatabase* database, bool enabled) |
| 288 | { |
| 289 | if (database->priv->iconDatabase) |
| 290 | database->priv->iconDatabase->setPrivateBrowsingEnabled(enabled); |
| 291 | } |
| 292 | |
| 293 | #if PLATFORM(GTK) |
| 294 | void webkitFaviconDatabaseGetLoadDecisionForIcon(WebKitFaviconDatabase* database, const LinkIcon& icon, const String& pageURL, Function<void(bool)>&& completionHandler) |
| 295 | { |
| 296 | if (!webkitFaviconDatabaseIsOpen(database)) { |
| 297 | completionHandler(false); |
| 298 | return; |
| 299 | } |
| 300 | |
| 301 | WebKitFaviconDatabasePrivate* priv = database->priv; |
| 302 | auto decision = priv->iconDatabase->synchronousLoadDecisionForIconURL(icon.url.string()); |
| 303 | switch (decision) { |
| 304 | case IconDatabase::IconLoadDecision::Unknown: |
| 305 | priv->pendingLoadDecisions.append(std::make_pair(icon.url.string(), WTFMove(completionHandler))); |
| 306 | priv->iconDatabase->setIconURLForPageURL(icon.url.string(), pageURL); |
| 307 | break; |
| 308 | case IconDatabase::IconLoadDecision::No: |
| 309 | priv->iconDatabase->setIconURLForPageURL(icon.url.string(), pageURL); |
| 310 | completionHandler(false); |
| 311 | break; |
| 312 | case IconDatabase::IconLoadDecision::Yes: |
| 313 | completionHandler(true); |
| 314 | break; |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | void webkitFaviconDatabaseSetIconForPageURL(WebKitFaviconDatabase* database, const LinkIcon& icon, API::Data& iconData, const String& pageURL) |
| 319 | { |
| 320 | if (!webkitFaviconDatabaseIsOpen(database)) |
| 321 | return; |
| 322 | |
| 323 | if (pageURL.isEmpty()) |
| 324 | return; |
| 325 | |
| 326 | WebKitFaviconDatabasePrivate* priv = database->priv; |
| 327 | SetForScope<bool> change(priv->isSettingIcon, true); |
| 328 | priv->iconDatabase->setIconURLForPageURL(icon.url.string(), pageURL); |
| 329 | priv->iconDatabase->setIconDataForIconURL(SharedBuffer::create(iconData.bytes(), iconData.size()), icon.url.string()); |
| 330 | webkitFaviconDatabaseSetIconURLForPageURL(database, icon.url.string(), pageURL); |
| 331 | g_signal_emit(database, signals[FAVICON_CHANGED], 0, pageURL.utf8().data(), icon.url.string().utf8().data()); |
| 332 | processPendingIconsForPageURL(database, pageURL); |
| 333 | } |
| 334 | |
| 335 | static PendingIconRequestVector* getOrCreatePendingIconRequests(WebKitFaviconDatabase* database, const String& pageURL) |
| 336 | { |
| 337 | PendingIconRequestVector* icons = database->priv->pendingIconRequests.get(pageURL); |
| 338 | if (!icons) { |
| 339 | icons = new PendingIconRequestVector; |
| 340 | database->priv->pendingIconRequests.set(pageURL, icons); |
| 341 | } |
| 342 | |
| 343 | return icons; |
| 344 | } |
| 345 | #endif |
| 346 | |
| 347 | GQuark webkit_favicon_database_error_quark(void) |
| 348 | { |
| 349 | return g_quark_from_static_string("WebKitFaviconDatabaseError" ); |
| 350 | } |
| 351 | |
| 352 | #if PLATFORM(GTK) |
| 353 | /** |
| 354 | * webkit_favicon_database_get_favicon: |
| 355 | * @database: a #WebKitFaviconDatabase |
| 356 | * @page_uri: URI of the page for which we want to retrieve the favicon |
| 357 | * @cancellable: (allow-none): A #GCancellable or %NULL. |
| 358 | * @callback: (scope async): A #GAsyncReadyCallback to call when the request is |
| 359 | * satisfied or %NULL if you don't care about the result. |
| 360 | * @user_data: (closure): The data to pass to @callback. |
| 361 | * |
| 362 | * Asynchronously obtains a #cairo_surface_t of the favicon for the |
| 363 | * given page URI. It returns the cached icon if it's in the database |
| 364 | * asynchronously waiting for the icon to be read from the database. |
| 365 | * |
| 366 | * This is an asynchronous method. When the operation is finished, callback will |
| 367 | * be invoked. You can then call webkit_favicon_database_get_favicon_finish() |
| 368 | * to get the result of the operation. |
| 369 | * |
| 370 | * You must call webkit_web_context_set_favicon_database_directory() for |
| 371 | * the #WebKitWebContext associated with this #WebKitFaviconDatabase |
| 372 | * before attempting to use this function; otherwise, |
| 373 | * webkit_favicon_database_get_favicon_finish() will return |
| 374 | * %WEBKIT_FAVICON_DATABASE_ERROR_NOT_INITIALIZED. |
| 375 | */ |
| 376 | void webkit_favicon_database_get_favicon(WebKitFaviconDatabase* database, const gchar* pageURI, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) |
| 377 | { |
| 378 | g_return_if_fail(WEBKIT_IS_FAVICON_DATABASE(database)); |
| 379 | g_return_if_fail(pageURI); |
| 380 | |
| 381 | if (!webkitFaviconDatabaseIsOpen(database)) { |
| 382 | g_task_report_new_error(database, callback, userData, 0, |
| 383 | WEBKIT_FAVICON_DATABASE_ERROR, WEBKIT_FAVICON_DATABASE_ERROR_NOT_INITIALIZED, _("Favicons database not initialized yet" )); |
| 384 | return; |
| 385 | } |
| 386 | |
| 387 | if (g_str_has_prefix(pageURI, "about:" )) { |
| 388 | g_task_report_new_error(database, callback, userData, 0, |
| 389 | WEBKIT_FAVICON_DATABASE_ERROR, WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_NOT_FOUND, _("Page %s does not have a favicon" ), pageURI); |
| 390 | return; |
| 391 | } |
| 392 | |
| 393 | GRefPtr<GTask> task = adoptGRef(g_task_new(database, cancellable, callback, userData)); |
| 394 | |
| 395 | GetFaviconSurfaceAsyncData* data = createGetFaviconSurfaceAsyncData(); |
| 396 | data->faviconDatabase = database; |
| 397 | data->pageURL = String::fromUTF8(pageURI); |
| 398 | g_task_set_task_data(task.get(), data, reinterpret_cast<GDestroyNotify>(destroyGetFaviconSurfaceAsyncData)); |
| 399 | |
| 400 | WebKitFaviconDatabasePrivate* priv = database->priv; |
| 401 | priv->iconDatabase->retainIconForPageURL(data->pageURL); |
| 402 | |
| 403 | // We ask for the icon directly. If we don't get the icon data now, |
| 404 | // we'll be notified later (even if the database is still importing icons). |
| 405 | GUniqueOutPtr<GError> error; |
| 406 | data->icon = getIconSurfaceSynchronously(database, data->pageURL, &error.outPtr()); |
| 407 | if (data->icon) { |
| 408 | g_task_return_boolean(task.get(), TRUE); |
| 409 | return; |
| 410 | } |
| 411 | |
| 412 | // At this point we still don't know whether we will get a valid icon for pageURL. |
| 413 | data->shouldReleaseIconForPageURL = true; |
| 414 | |
| 415 | if (g_error_matches(error.get(), WEBKIT_FAVICON_DATABASE_ERROR, WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_NOT_FOUND)) { |
| 416 | g_task_return_error(task.get(), error.release().release()); |
| 417 | return; |
| 418 | } |
| 419 | |
| 420 | // If there's not a valid icon, but there's an iconURL registered, |
| 421 | // or it's still not registered but the import process hasn't |
| 422 | // finished yet, we need to wait for iconDataReadyForPage to be |
| 423 | // called before making and informed decision. |
| 424 | String iconURLForPageURL = priv->iconDatabase->synchronousIconURLForPageURL(data->pageURL); |
| 425 | if (!iconURLForPageURL.isEmpty() || !priv->isURLImportCompleted) { |
| 426 | PendingIconRequestVector* iconRequests = getOrCreatePendingIconRequests(database, data->pageURL); |
| 427 | ASSERT(iconRequests); |
| 428 | iconRequests->append(task); |
| 429 | return; |
| 430 | } |
| 431 | |
| 432 | g_task_return_new_error(task.get(), WEBKIT_FAVICON_DATABASE_ERROR, WEBKIT_FAVICON_DATABASE_ERROR_FAVICON_UNKNOWN, |
| 433 | _("Unknown favicon for page %s" ), pageURI); |
| 434 | } |
| 435 | |
| 436 | /** |
| 437 | * webkit_favicon_database_get_favicon_finish: |
| 438 | * @database: a #WebKitFaviconDatabase |
| 439 | * @result: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to webkit_favicon_database_get_favicon() |
| 440 | * @error: (allow-none): Return location for error or %NULL. |
| 441 | * |
| 442 | * Finishes an operation started with webkit_favicon_database_get_favicon(). |
| 443 | * |
| 444 | * Returns: (transfer full): a new reference to a #cairo_surface_t, or |
| 445 | * %NULL in case of error. |
| 446 | */ |
| 447 | cairo_surface_t* webkit_favicon_database_get_favicon_finish(WebKitFaviconDatabase* database, GAsyncResult* result, GError** error) |
| 448 | { |
| 449 | g_return_val_if_fail(WEBKIT_IS_FAVICON_DATABASE(database), 0); |
| 450 | g_return_val_if_fail(g_task_is_valid(result, database), 0); |
| 451 | |
| 452 | GTask* task = G_TASK(result); |
| 453 | if (!g_task_propagate_boolean(task, error)) |
| 454 | return 0; |
| 455 | |
| 456 | GetFaviconSurfaceAsyncData* data = static_cast<GetFaviconSurfaceAsyncData*>(g_task_get_task_data(task)); |
| 457 | return cairo_surface_reference(data->icon.get()); |
| 458 | } |
| 459 | #endif |
| 460 | |
| 461 | /** |
| 462 | * webkit_favicon_database_get_favicon_uri: |
| 463 | * @database: a #WebKitFaviconDatabase |
| 464 | * @page_uri: URI of the page containing the icon |
| 465 | * |
| 466 | * Obtains the URI of the favicon for the given @page_uri. |
| 467 | * |
| 468 | * Returns: a newly allocated URI for the favicon, or %NULL if the |
| 469 | * database doesn't have a favicon for @page_uri. |
| 470 | */ |
| 471 | gchar* webkit_favicon_database_get_favicon_uri(WebKitFaviconDatabase* database, const gchar* pageURL) |
| 472 | { |
| 473 | g_return_val_if_fail(WEBKIT_IS_FAVICON_DATABASE(database), nullptr); |
| 474 | g_return_val_if_fail(pageURL, nullptr); |
| 475 | ASSERT(RunLoop::isMain()); |
| 476 | |
| 477 | if (!webkitFaviconDatabaseIsOpen(database)) |
| 478 | return nullptr; |
| 479 | |
| 480 | String iconURLForPageURL = database->priv->iconDatabase->synchronousIconURLForPageURL(String::fromUTF8(pageURL)); |
| 481 | if (iconURLForPageURL.isEmpty()) |
| 482 | return nullptr; |
| 483 | |
| 484 | return g_strdup(iconURLForPageURL.utf8().data()); |
| 485 | } |
| 486 | |
| 487 | /** |
| 488 | * webkit_favicon_database_clear: |
| 489 | * @database: a #WebKitFaviconDatabase |
| 490 | * |
| 491 | * Clears all icons from the database. |
| 492 | */ |
| 493 | void webkit_favicon_database_clear(WebKitFaviconDatabase* database) |
| 494 | { |
| 495 | g_return_if_fail(WEBKIT_IS_FAVICON_DATABASE(database)); |
| 496 | |
| 497 | if (!webkitFaviconDatabaseIsOpen(database)) |
| 498 | return; |
| 499 | |
| 500 | database->priv->iconDatabase->removeAllIcons(); |
| 501 | } |
| 502 | |