[epiphany] Support large standard HTML icons and SVG for web app icons



commit 61e068a5e65286b7bac7f828f309273548d60f94
Author: Daniel Aleksandersen <code daniel priv no>
Date:   Wed Jun 8 12:23:58 2016 +0200

    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

 AUTHORS                                  |    1 +
 embed/ephy-web-extension-proxy.c         |    3 +-
 embed/ephy-web-extension-proxy.h         |    1 -
 embed/ephy-web-view.c                    |    7 +-
 embed/ephy-web-view.h                    |    1 -
 embed/web-extension/ephy-web-dom-utils.c |  190 ++++++++++++++++++++---------
 embed/web-extension/ephy-web-dom-utils.h |    8 +-
 embed/web-extension/ephy-web-extension.c |    6 +-
 src/window-commands.c                    |    9 +-
 9 files changed, 144 insertions(+), 82 deletions(-)
---
diff --git a/AUTHORS b/AUTHORS
index f033be3..7f0123b 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 fdbe8d5..3e16dbd 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 28cf0af..b5ec077 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 36be71c..71d75ea 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 4bda784..ca0b04a 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 bff1643..32a9233 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 3d05af1..5c7aea9 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 a0ec04d..0ea5cf0 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 f06a7ca..498660c 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);
   }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]