diff options
author | Daniel Aleksandersen <code@daniel.priv.no> | 2016-06-08 12:23:58 +0200 |
---|---|---|
committer | Michael Catanzaro <mcatanzaro@igalia.com> | 2016-06-08 09:11:34 -0500 |
commit | 61e068a5e65286b7bac7f828f309273548d60f94 (patch) | |
tree | 711a31ff64673a0a3ae5c51a5513668e1358f661 | |
parent | 1576a809883e2da46a55234fd7889ddde57f4b28 (diff) | |
download | epiphany-61e068a5e65286b7bac7f828f309273548d60f94.tar.gz |
Support large standard HTML icons and SVG for web app icons
* Always prefer standard HTML over vendor specific extensions.
* Add support for SVG icons and large HTML icons.
* Change icon discovery order to large html5 icons, mstile,
touch-icons, og:image, and lastly favicons.
* Fallback to optimistically requesting /favicon.ico.
* Change metadata matching to be case-insensitive.
* Remove return value from ephy_web_dom_utils_get_best_icon and
data->icon_result, check if icon_href is set instead.
* Resolve memory leaks when creating web application icons.
https://bugzilla.gnome.org/show_bug.cgi?id=767257
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | embed/ephy-web-extension-proxy.c | 3 | ||||
-rw-r--r-- | embed/ephy-web-extension-proxy.h | 1 | ||||
-rw-r--r-- | embed/ephy-web-view.c | 7 | ||||
-rw-r--r-- | embed/ephy-web-view.h | 1 | ||||
-rw-r--r-- | embed/web-extension/ephy-web-dom-utils.c | 190 | ||||
-rw-r--r-- | embed/web-extension/ephy-web-dom-utils.h | 8 | ||||
-rw-r--r-- | embed/web-extension/ephy-web-extension.c | 6 | ||||
-rw-r--r-- | src/window-commands.c | 9 |
9 files changed, 144 insertions, 82 deletions
@@ -4,4 +4,5 @@ Christian Persch <chpe@gnome.org> Adam Hooper <adamh@cvs.gnome.org> Jean-François Rameau <jframeau@cvs.gnome.org> Crispin Flowerday <crispin@flowerday.cx> +Daniel Aleksandersen <code@daniel.priv.no> diff --git a/embed/ephy-web-extension-proxy.c b/embed/ephy-web-extension-proxy.c index fdbe8d515..3e16dbd49 100644 --- a/embed/ephy-web-extension-proxy.c +++ b/embed/ephy-web-extension-proxy.c @@ -305,7 +305,6 @@ ephy_web_extension_proxy_get_best_web_app_icon (EphyWebExtensionProxy *web_exten gboolean ephy_web_extension_proxy_get_best_web_app_icon_finish (EphyWebExtensionProxy *web_extension, GAsyncResult *result, - gboolean *icon_result, char **icon_uri, char **icon_color, GError **error) @@ -319,7 +318,7 @@ ephy_web_extension_proxy_get_best_web_app_icon_finish (EphyWebExtensionProxy *we if (!variant) return FALSE; - g_variant_get (variant, "(bss)", icon_result, icon_uri, icon_color); + g_variant_get (variant, "(ss)", icon_uri, icon_color); g_variant_unref (variant); return TRUE; diff --git a/embed/ephy-web-extension-proxy.h b/embed/ephy-web-extension-proxy.h index 28cf0afef..b5ec0779f 100644 --- a/embed/ephy-web-extension-proxy.h +++ b/embed/ephy-web-extension-proxy.h @@ -47,7 +47,6 @@ void ephy_web_extension_proxy_get_best_web_app_icon gpointer user_data); gboolean ephy_web_extension_proxy_get_best_web_app_icon_finish (EphyWebExtensionProxy *web_extension, GAsyncResult *result, - gboolean *icon_result, char **icon_uri, char **icon_color, GError **error); diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c index 36be71cce..71d75ea0f 100644 --- a/embed/ephy-web-view.c +++ b/embed/ephy-web-view.c @@ -2558,7 +2558,6 @@ ephy_web_view_has_modified_forms_finish (EphyWebView *view, } typedef struct { - gboolean icon_result; char *icon_uri; char *icon_color; } GetBestWebAppIconAsyncData; @@ -2577,17 +2576,14 @@ get_best_web_app_icon_cb (EphyWebExtensionProxy *web_extension, GAsyncResult *result, GTask *task) { - gboolean retval = FALSE; char *uri = NULL; char *color = NULL; GError *error = NULL; - if (!ephy_web_extension_proxy_get_best_web_app_icon_finish (web_extension, result, &retval, &uri, &color, &error)) { + if (!ephy_web_extension_proxy_get_best_web_app_icon_finish (web_extension, result, &uri, &color, &error)) { g_task_return_error (task, error); } else { GetBestWebAppIconAsyncData *data = g_slice_new0 (GetBestWebAppIconAsyncData); - - data->icon_result = retval; data->icon_uri = uri; data->icon_color = color; g_task_return_pointer (task, data, (GDestroyNotify)get_best_web_app_icon_async_data_free); @@ -2624,7 +2620,6 @@ ephy_web_view_get_best_web_app_icon (EphyWebView *view, gboolean ephy_web_view_get_best_web_app_icon_finish (EphyWebView *view, GAsyncResult *result, - gboolean *icon_result, char **icon_uri, GdkRGBA *icon_color, GError **error) diff --git a/embed/ephy-web-view.h b/embed/ephy-web-view.h index 4bda784b0..ca0b04a29 100644 --- a/embed/ephy-web-view.h +++ b/embed/ephy-web-view.h @@ -105,7 +105,6 @@ void ephy_web_view_get_best_web_app_icon (EphyWebView gpointer user_data); gboolean ephy_web_view_get_best_web_app_icon_finish (EphyWebView *view, GAsyncResult *result, - gboolean *icon_result, char **icon_uri, GdkRGBA *icon_color, GError **error); diff --git a/embed/web-extension/ephy-web-dom-utils.c b/embed/web-extension/ephy-web-dom-utils.c index bff164321..32a9233ac 100644 --- a/embed/web-extension/ephy-web-dom-utils.c +++ b/embed/web-extension/ephy-web-dom-utils.c @@ -21,6 +21,7 @@ #include "ephy-web-dom-utils.h" #include <libsoup/soup.h> +#include <stdio.h> /** * ephy_web_dom_utils_has_modified_forms: @@ -125,9 +126,13 @@ ephy_web_dom_utils_get_application_title (WebKitDOMDocument *document) * commonly seen on the web in the name attribute. Both are supported. */ name = webkit_dom_html_meta_element_get_name (WEBKIT_DOM_HTML_META_ELEMENT (node)); property = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "property"); - if ((name != NULL && g_ascii_strcasecmp (name, "application-name") == 0) || - (property != NULL && g_ascii_strcasecmp (property, "og:site_name") == 0) || - (name != NULL && g_ascii_strcasecmp (name, "og:site_name") == 0)) { + if (name != NULL && g_ascii_strcasecmp (name, "application-name") == 0) { + g_free (title); + title = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); + break; /* Best name candidate. */ + } else if ((property != NULL && g_ascii_strcasecmp (property, "og:site_name") == 0) || + (name != NULL && g_ascii_strcasecmp (name, "og:site_name") == 0)) { + g_free (title); title = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); } g_free (property); @@ -161,6 +166,76 @@ resolve_uri (const char *base_uri, } static gboolean +get_icon_from_html_icon (WebKitDOMDocument *document, + char **uri_out) +{ + gboolean ret; + WebKitDOMNodeList *links; + gulong length, i; + char *image = NULL; + int largest_icon = 0; + + links = webkit_dom_document_get_elements_by_tag_name (document, "link"); + length = webkit_dom_node_list_get_length (links); + + for (i = 0; i < length; i++) { + char *rel; + WebKitDOMNode *node = webkit_dom_node_list_item (links, i); + + rel = webkit_dom_html_link_element_get_rel (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); + if (rel != NULL && ( + g_ascii_strcasecmp (rel, "icon") == 0 || + g_ascii_strcasecmp (rel, "shortcut icon") == 0 || + g_ascii_strcasecmp (rel, "icon shortcut") == 0 || + g_ascii_strcasecmp (rel, "shortcut-icon") == 0)) { + char *sizes; + int width; + int height; + + g_free (rel); + + sizes = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "sizes"); + if (sizes != NULL) { + if (g_ascii_strcasecmp (sizes, "any") == 0) { + g_free (sizes); + g_free (image); + + /* TODO: Keep the SVG rather than rasterizing it to PNG. */ + image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); + /* "any" means a vector, and thus it will always be the largest icon. */ + break; + } + + /* Only accept square icons. */ + if (sscanf (sizes, "%ix%i", &width, &height) != 2 || width != height) { + g_free (sizes); + continue; + } + + /* Only accept icons of 96 px (smallest GNOME HIG app icon) or larger. + * It's better to defer to other icon discovery methods if smaller + * icons are returned here. */ + if (width >= 96 && width > largest_icon) { + g_free (image); + image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); + largest_icon = width; /* Keep largest candidate if multiple are found. */ + } + g_free (sizes); + } + } + else + g_free (rel); + } + + ret = (image != NULL && *image != '\0'); + + if (uri_out != NULL) + *uri_out = image; + + return ret; +} + +static gboolean get_icon_from_mstile (WebKitDOMDocument *document, char **uri_out, char **color_out) @@ -182,38 +257,36 @@ get_icon_from_mstile (WebKitDOMDocument *document, char *name; name = webkit_dom_html_meta_element_get_name (WEBKIT_DOM_HTML_META_ELEMENT (node)); - if (g_strcmp0 (name, "msapplication-TileImage") == 0) { - if (image == NULL) + if (name != NULL) { + if (g_ascii_strcasecmp (name, "msapplication-TileImage") == 0) { + g_free (image); image = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); - } else if (g_strcmp0 (name, "msapplication-TileColor") == 0) { - if (color == NULL) + } + else if (g_ascii_strcasecmp (name, "msapplication-TileColor") == 0) { + g_free (color); color = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); + } } } ret = (image != NULL && *image != '\0'); if (uri_out != NULL) - *uri_out = g_strdup (image); + *uri_out = image; if (color_out != NULL) - *color_out = g_strdup (color); - - g_free (image); - g_free (color); + *color_out = color; return ret; } static gboolean get_icon_from_ogp (WebKitDOMDocument *document, - char **uri_out, - char **color_out) + char **uri_out) { gboolean ret; WebKitDOMNodeList *metas; gulong length, i; char *image = NULL; - char *color = NULL; metas = webkit_dom_document_get_elements_by_tag_name (document, "meta"); length = webkit_dom_node_list_get_length (metas); @@ -225,8 +298,9 @@ get_icon_from_ogp (WebKitDOMDocument *document, property = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "property"); itemprop = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "itemprop"); - if (g_strcmp0 (property, "og:image") == 0 || - g_strcmp0 (itemprop, "image") == 0) { + if ((property != NULL && g_ascii_strcasecmp (property, "og:image") == 0) || + (itemprop != NULL && g_ascii_strcasecmp (itemprop, "image") == 0)) { + g_free (image); image = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); } g_free (property); @@ -236,23 +310,19 @@ get_icon_from_ogp (WebKitDOMDocument *document, ret = (image != NULL && *image != '\0'); if (uri_out != NULL) - *uri_out = g_strdup (image); - if (color_out != NULL) - *color_out = g_strdup (color); + *uri_out = image; return ret; } static gboolean get_icon_from_touch_icon (WebKitDOMDocument *document, - char **uri_out, - char **color_out) + char **uri_out) { gboolean ret; WebKitDOMNodeList *links; gulong length, i; char *image = NULL; - char *color = NULL; links = webkit_dom_document_get_elements_by_tag_name (document, "link"); length = webkit_dom_node_list_get_length (links); @@ -263,33 +333,36 @@ get_icon_from_touch_icon (WebKitDOMDocument *document, rel = webkit_dom_html_link_element_get_rel (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); /* TODO: support more than one possible icon. */ - if (g_strcmp0 (rel, "apple-touch-icon") == 0 || - g_strcmp0 (rel, "apple-touch-icon-precomposed") == 0) { - image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); + if (rel != NULL) { + if (g_ascii_strcasecmp (rel, "apple-touch-icon") == 0) { + g_free (image); + image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); + break; /* Best touch-icon candidate. */ + } else if (g_ascii_strcasecmp (rel, "apple-touch-icon-precomposed") == 0) { + g_free (image); + image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); + } } g_free (rel); } + /* TODO: Try to retrieve /apple-touch-icon.png, and return it if it exist. */ + ret = (image != NULL && *image != '\0'); if (uri_out != NULL) - *uri_out = g_strdup (image); - if (color_out != NULL) - *color_out = g_strdup (color); + *uri_out = image; return ret; } static gboolean get_icon_from_favicon (WebKitDOMDocument *document, - char **uri_out, - char **color_out) + char **uri_out) { - gboolean ret; WebKitDOMNodeList *links; gulong length, i; char *image = NULL; - char *color = NULL; links = webkit_dom_document_get_elements_by_tag_name (document, "link"); length = webkit_dom_node_list_get_length (links); @@ -299,25 +372,25 @@ get_icon_from_favicon (WebKitDOMDocument *document, WebKitDOMNode *node = webkit_dom_node_list_item (links, i); rel = webkit_dom_html_link_element_get_rel (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); - if (g_strcmp0 (rel, "shortcut-icon") == 0 || - g_strcmp0 (rel, "shortcut icon") == 0 || - g_strcmp0 (rel, "SHORTCUT ICON") == 0 || - g_strcmp0 (rel, "Shortcut Icon") == 0 || - g_strcmp0 (rel, "icon shortcut") == 0 || - g_strcmp0 (rel, "icon") == 0) { + if (rel != NULL && ( + g_ascii_strcasecmp (rel, "icon") == 0 || + g_ascii_strcasecmp (rel, "shortcut icon") == 0 || + g_ascii_strcasecmp (rel, "icon shortcut") == 0 || + g_ascii_strcasecmp (rel, "shortcut-icon") == 0)) { + g_free (image); image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); } g_free (rel); } - ret = (image != NULL && *image != '\0'); + /* Last ditch effort: just fallback to the default favicon location. */ + if (image == NULL) + image = g_strdup("/favicon.ico"); if (uri_out != NULL) - *uri_out = g_strdup (image); - if (color_out != NULL) - *color_out = g_strdup (color); + *uri_out = image; - return ret; + return TRUE; } /** @@ -328,41 +401,40 @@ get_icon_from_favicon (WebKitDOMDocument *document, * @color_out: Icon background color. * * Tries to get the icon (and its background color if any) for a web application - * from the @document meta data. First try to get a mstile, then OGP, then touch - * icon and finally favicon. + * from the @document meta data. First try to get a large standard icon, then mstile, + * then OpenGraph, then touch icon, and finally use the favicon. * * Returns %TRUE if it finds an icon in the @document. **/ -gboolean +void ephy_web_dom_utils_get_best_icon (WebKitDOMDocument *document, const char *base_uri, char **uri_out, char **color_out) { - gboolean ret = FALSE; + gboolean found_icon = FALSE; char *image = NULL; char *color = NULL; /* FIXME: These functions could be improved considerably. See the first two answers at: * http://stackoverflow.com/questions/21991044/how-to-get-high-resolution-website-logo-favicon-for-a-given-url */ - ret = get_icon_from_mstile (document, &image, &color); - if (!ret) - ret = get_icon_from_ogp (document, &image, &color); - if (!ret) - ret = get_icon_from_touch_icon (document, &image, &color); - if (!ret) - ret = get_icon_from_favicon (document, &image, &color); + found_icon = get_icon_from_html_icon (document, &image); + if (!found_icon) + found_icon = get_icon_from_mstile (document, &image, &color); + if (!found_icon) + found_icon = get_icon_from_touch_icon (document, &image); + if (!found_icon) + found_icon = get_icon_from_ogp (document, &image); + if (!found_icon) + found_icon = get_icon_from_favicon (document, &image); if (uri_out != NULL) *uri_out = resolve_uri (base_uri, image); if (color_out != NULL) - *color_out = g_strdup (color); + *color_out = color; g_free (image); - g_free (color); - - return ret; } gboolean diff --git a/embed/web-extension/ephy-web-dom-utils.h b/embed/web-extension/ephy-web-dom-utils.h index 3d05af15c..5c7aea930 100644 --- a/embed/web-extension/ephy-web-dom-utils.h +++ b/embed/web-extension/ephy-web-dom-utils.h @@ -28,10 +28,10 @@ gboolean ephy_web_dom_utils_has_modified_forms (WebKitDOMDocument *document); char * ephy_web_dom_utils_get_application_title (WebKitDOMDocument *document); -gboolean ephy_web_dom_utils_get_best_icon (WebKitDOMDocument *document, - const char *base_uri, - char **uri_out, - char **color_out); +void ephy_web_dom_utils_get_best_icon (WebKitDOMDocument *document, + const char *base_uri, + char **uri_out, + char **color_out); gboolean ephy_web_dom_utils_find_form_auth_elements (WebKitDOMHTMLFormElement *form, WebKitDOMNode **username, diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c index a0ec04d82..0ea5cf0f1 100644 --- a/embed/web-extension/ephy-web-extension.c +++ b/embed/web-extension/ephy-web-extension.c @@ -75,7 +75,6 @@ static const char introspection_xml[] = " <method name='GetBestWebAppIcon'>" " <arg type='t' name='page_id' direction='in'/>" " <arg type='s' name='base_uri' direction='in'/>" - " <arg type='b' name='result' direction='out'/>" " <arg type='s' name='uri' direction='out'/>" " <arg type='s' name='color' direction='out'/>" " </method>" @@ -1180,7 +1179,6 @@ handle_method_call (GDBusConnection *connection, char *uri = NULL; char *color = NULL; guint64 page_id; - gboolean result; g_variant_get (parameters, "(ts)", &page_id, &base_uri); web_page = get_webkit_web_page_or_return_dbus_error (invocation, extension->extension, page_id); @@ -1194,10 +1192,10 @@ handle_method_call (GDBusConnection *connection, } document = webkit_web_page_get_dom_document (web_page); - result = ephy_web_dom_utils_get_best_icon (document, base_uri, &uri, &color); + ephy_web_dom_utils_get_best_icon (document, base_uri, &uri, &color); g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(bss)", result, uri ? uri : "", color ? color : "")); + g_variant_new ("(ss)", uri ? uri : "", color ? color : "")); } else if (g_strcmp0 (method_name, "FormAuthDataSaveConfirmationResponse") == 0) { EphyEmbedFormAuth *form_auth; guint request_id; diff --git a/src/window-commands.c b/src/window-commands.c index f06a7ca41..498660cd6 100644 --- a/src/window-commands.c +++ b/src/window-commands.c @@ -485,7 +485,7 @@ set_app_icon_from_filename (EphyApplicationDialogData *data, GdkPixbuf *pixbuf; GdkPixbuf *framed; - pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + pixbuf = gdk_pixbuf_new_from_file_at_size (filename, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, NULL); if (pixbuf == NULL) return; @@ -558,15 +558,14 @@ fill_default_application_image_cb (GObject *source, EphyApplicationDialogData *data = user_data; char *uri = NULL; GdkRGBA color = { 0.5, 0.5, 0.5, 0.3 }; - gboolean res = FALSE; - ephy_web_view_get_best_web_app_icon_finish (EPHY_WEB_VIEW (source), async_result, &res, &uri, &color, NULL); + ephy_web_view_get_best_web_app_icon_finish (EPHY_WEB_VIEW (source), async_result, &uri, &color, NULL); data->icon_href = uri; data->icon_rgba = color; - if (res) { + if (data->icon_href != NULL) download_icon_and_set_image (data); - } else { + else { gtk_widget_show (data->image); set_image_from_favicon (data); } |