summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Aleksandersen <code@daniel.priv.no>2016-06-08 12:23:58 +0200
committerMichael Catanzaro <mcatanzaro@igalia.com>2016-06-08 09:11:34 -0500
commit61e068a5e65286b7bac7f828f309273548d60f94 (patch)
tree711a31ff64673a0a3ae5c51a5513668e1358f661
parent1576a809883e2da46a55234fd7889ddde57f4b28 (diff)
downloadepiphany-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--AUTHORS1
-rw-r--r--embed/ephy-web-extension-proxy.c3
-rw-r--r--embed/ephy-web-extension-proxy.h1
-rw-r--r--embed/ephy-web-view.c7
-rw-r--r--embed/ephy-web-view.h1
-rw-r--r--embed/web-extension/ephy-web-dom-utils.c190
-rw-r--r--embed/web-extension/ephy-web-dom-utils.h8
-rw-r--r--embed/web-extension/ephy-web-extension.c6
-rw-r--r--src/window-commands.c9
9 files changed, 144 insertions, 82 deletions
diff --git a/AUTHORS b/AUTHORS
index f033be37a..7f0123bce 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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);
}