[epiphany/wip/downloads: 9/10] downloads: Add content-type property to EphyDownload



commit bdd6e19d1584d5fbd9c2b18f7e27bebed4cc74d9
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Wed Oct 7 15:05:05 2015 +0200

    downloads: Add content-type property to EphyDownload
    
    This way it can be monitorized, instead of the caller deciding when to
    ask for it during the whole download process. We first try to get it
    when the server response is received. Then we try again when the
    destination file has been created if the server didn't provide a valid
    content type.

 embed/ephy-download.c              |  135 ++++++++++++++++++++++++------------
 embed/ephy-download.h              |    2 +-
 lib/widgets/ephy-download-widget.c |   53 ++++++---------
 3 files changed, 113 insertions(+), 77 deletions(-)
---
diff --git a/embed/ephy-download.c b/embed/ephy-download.c
index e1cc90e..4c29a94 100644
--- a/embed/ephy-download.c
+++ b/embed/ephy-download.c
@@ -47,7 +47,7 @@ struct _EphyDownloadPrivate
   WebKitDownload *download;
 
   char *destination;
-  char *source;
+  char *content_type;
 
   EphyDownloadActionType action;
   guint32 start_time;
@@ -61,7 +61,8 @@ enum
   PROP_DOWNLOAD,
   PROP_DESTINATION,
   PROP_ACTION,
-  PROP_START_TIME
+  PROP_START_TIME,
+  PROP_CONTENT_TYPE
 };
 
 enum
@@ -96,6 +97,9 @@ ephy_download_get_property (GObject    *object,
     case PROP_START_TIME:
       g_value_set_uint (value, ephy_download_get_start_time (download));
       break;
+    case PROP_CONTENT_TYPE:
+      g_value_set_string (value, ephy_download_get_content_type (download));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -133,54 +137,16 @@ ephy_download_set_property (GObject      *object,
  * Gets content-type information for @download. If the server didn't
  * provide a content type, the destination file is queried.
  *
- * Returns: content-type for @download, must be freed with g_free()
+ * Returns: content-type for @download
  **/
-char *
+const char *
 ephy_download_get_content_type (EphyDownload *download)
 {
-  WebKitURIResponse *response;
-  const char *destination_uri;
-  GFile *destination;
-  GFileInfo *info;
-  char *content_type = NULL;
-  GError *error = NULL;
-
-  response = webkit_download_get_response (download->priv->download);
-  if (response) {
-    const char *mime_type = webkit_uri_response_get_mime_type (response);
-
-    LOG ("ephy_download_get_content_type: WebKit mime type: %s", mime_type);
-
-    if (mime_type) {
-      content_type = g_content_type_from_mime_type (mime_type);
-      if (content_type)
-        return content_type;
-    }
-  }
-
-  destination_uri = webkit_download_get_destination (download->priv->download);
-  if (!destination_uri)
-    return NULL;
-
-  destination = g_file_new_for_uri (destination_uri);
-  info = g_file_query_info (destination, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
-                            G_FILE_QUERY_INFO_NONE, NULL, &error);
-  if (info) {
-    content_type = g_strdup (g_file_info_get_content_type (info));
-    LOG ("ephy_download_get_content_type: GIO: %s", content_type);
-    g_object_unref (info);
-  } else {
-    LOG ("ephy_download_get_content_type: error getting file "
-         "content-type: %s", error->message);
-    g_error_free (error);
-  }
-
-  g_object_unref (destination);
+  g_return_val_if_fail (EPHY_IS_DOWNLOAD (download), NULL);
 
-  return content_type;
+  return download->priv->content_type;
 }
 
-
 /* Helper function to decide what EphyDownloadActionType should be the
  * default for the download. This implies that you want something to
  * happen, this function will never return EPHY_DOWNLOAD_ACTION_NONE.
@@ -555,6 +521,7 @@ ephy_download_dispose (GObject *object)
   }
 
   g_clear_error(&priv->error);
+  g_clear_pointer (&priv->content_type, g_free);
 
   G_OBJECT_CLASS (ephy_download_parent_class)->dispose (object);
 }
@@ -634,6 +601,16 @@ ephy_download_class_init (EphyDownloadClass *klass)
                                                       G_PARAM_STATIC_NICK |
                                                       G_PARAM_STATIC_BLURB));
 
+  g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
+                                   g_param_spec_string ("content-type",
+                                                        "Content Type",
+                                                        "The download content type",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_NICK |
+                                                        G_PARAM_STATIC_BLURB));
+
   /**
    * EphyDownload::filename-suggested:
    *
@@ -692,6 +669,25 @@ ephy_download_init (EphyDownload *download)
   download->priv->start_time = gtk_get_current_event_time ();
 }
 
+static void
+download_response_changed_cb (WebKitDownload *wk_download,
+                              GParamSpec *spec,
+                              EphyDownload *download)
+{
+  WebKitURIResponse *response;
+  const char *mime_type;
+
+  response = webkit_download_get_response (download->priv->download);
+  mime_type = webkit_uri_response_get_mime_type (response);
+  if (!mime_type)
+    return;
+
+  download->priv->content_type = g_content_type_from_mime_type (mime_type);
+  if (download->priv->content_type)
+    g_object_notify (G_OBJECT (download), "content-type");
+
+}
+
 static gboolean
 download_decide_destination_cb (WebKitDownload *wk_download,
                                 const gchar *suggested_filename,
@@ -709,6 +705,51 @@ download_decide_destination_cb (WebKitDownload *wk_download,
 }
 
 static void
+download_created_destination_cb (WebKitDownload *wk_download,
+                                 const char *destination,
+                                 EphyDownload *download)
+{
+  char *filename;
+  char *content_type;
+  EphyDownloadPrivate *priv = download->priv;
+
+  if (priv->content_type && !g_content_type_is_unknown (priv->content_type))
+    return;
+
+  /* The server didn't provide a valid content type, let's try to guess it from the
+   * destination filename. We use g_content_type_guess() here instead of g_file_query_info(),
+   * because we are only using the filename to guess the content type, since it doesn't make
+   * sense to sniff the destination URI that will be empty until the download is completed.
+   * We can't use g_file_query_info() with the partial download file either, because it will
+   * always return application/x-partial-download based on the .wkdownload extension.
+   */
+  filename = g_filename_from_uri (destination, NULL, NULL);
+  if (!filename)
+    return;
+
+  content_type = g_content_type_guess (filename, NULL, 0, NULL);
+  g_free (filename);
+
+  if (g_content_type_is_unknown (content_type)) {
+    /* We could try to connect to received-data signal and sniff the contents when we have
+     * enough data written in the file, but I don't think it's worth it.
+     */
+    g_free (content_type);
+    return;
+  }
+
+  if (!priv->content_type ||
+      (priv->content_type && !g_content_type_equals (priv->content_type, content_type))) {
+    g_free (priv->content_type);
+    priv->content_type = content_type;
+    g_object_notify (G_OBJECT (download), "content-type");
+    return;
+  }
+
+  g_free (content_type);
+}
+
+static void
 download_finished_cb (WebKitDownload *wk_download,
                       EphyDownload *download)
 {
@@ -756,9 +797,15 @@ ephy_download_new (WebKitDownload *download)
 
   ephy_download = g_object_new (EPHY_TYPE_DOWNLOAD, NULL);
 
+  g_signal_connect (download, "notify::response",
+                    G_CALLBACK (download_response_changed_cb),
+                    ephy_download);
   g_signal_connect (download, "decide-destination",
                     G_CALLBACK (download_decide_destination_cb),
                     ephy_download);
+  g_signal_connect (download, "created-destination",
+                    G_CALLBACK (download_created_destination_cb),
+                    ephy_download);
   g_signal_connect (download, "finished",
                     G_CALLBACK (download_finished_cb),
                     ephy_download);
diff --git a/embed/ephy-download.h b/embed/ephy-download.h
index d95190c..e6ca3ad 100644
--- a/embed/ephy-download.h
+++ b/embed/ephy-download.h
@@ -83,7 +83,7 @@ void          ephy_download_set_destination_uri   (EphyDownload *download,
 WebKitDownload *ephy_download_get_webkit_download (EphyDownload *download);
 
 const char   *ephy_download_get_destination_uri   (EphyDownload *download);
-char         *ephy_download_get_content_type      (EphyDownload *download);
+const char   *ephy_download_get_content_type      (EphyDownload *download);
 
 guint32       ephy_download_get_start_time        (EphyDownload *download);
 
diff --git a/lib/widgets/ephy-download-widget.c b/lib/widgets/ephy-download-widget.c
index 6734a5e..6b1da16 100644
--- a/lib/widgets/ephy-download-widget.c
+++ b/lib/widgets/ephy-download-widget.c
@@ -55,25 +55,6 @@ enum
   PROP_DOWNLOAD
 };
 
-static GIcon *
-get_gicon_from_download (EphyDownload *ephy_download)
-{
-  char *content_type;
-  GIcon *gicon;
-
-  /* FIXME: this might do sync IO during downloads */
-  content_type = ephy_download_get_content_type (ephy_download);
-
-  if (content_type) {
-    gicon = g_content_type_get_icon (content_type);
-    g_free (content_type);
-  } else {
-    gicon = g_icon_new_for_string ("package-x-generic", NULL);
-  }
-
-  return gicon;
-}
-
 static char *
 get_destination_basename_from_download (EphyDownload *ephy_download)
 {
@@ -163,15 +144,17 @@ download_clicked_cb (GtkButton *button,
 static void
 update_download_icon (EphyDownloadWidget *widget)
 {
-  GIcon *new_icon, *old_icon;
+  GIcon *icon;
+  const char *content_type;
 
-  new_icon = get_gicon_from_download (widget->priv->download);
-  gtk_image_get_gicon (GTK_IMAGE (widget->priv->icon), &old_icon, NULL);
-  if (!g_icon_equal (new_icon, old_icon)) {
-    gtk_image_set_from_gicon (GTK_IMAGE (widget->priv->icon), new_icon,
-                              GTK_ICON_SIZE_DIALOG);
-  }
-  g_object_unref (new_icon);
+  content_type = ephy_download_get_content_type (widget->priv->download);
+  if (content_type)
+    icon = g_content_type_get_icon (content_type);
+  else
+    icon = g_icon_new_for_string ("package-x-generic", NULL);
+
+  gtk_image_set_from_gicon (GTK_IMAGE (widget->priv->icon), icon, GTK_ICON_SIZE_DIALOG);
+  g_object_unref (icon);
 }
 
 static void
@@ -226,7 +209,6 @@ download_progress_cb (WebKitDownload *download,
                       EphyDownloadWidget *widget)
 {
   gdouble progress;
-  int percentage;
   WebKitURIResponse *response;
   guint64 content_length;
   guint64 received_length;
@@ -236,10 +218,6 @@ download_progress_cb (WebKitDownload *download,
     return;
 
   progress = webkit_download_get_estimated_progress (download);
-  percentage = progress * 100;
-  if (percentage % 10 == 0)
-    update_download_icon (widget);
-
   response = webkit_download_get_response (download);
   content_length = webkit_uri_response_get_content_length (response);
   received_length = webkit_download_get_received_data_length (download);
@@ -305,6 +283,14 @@ download_failed_cb (EphyDownload *download,
 }
 
 static void
+download_content_type_changed_cb (EphyDownload *download,
+                                  GParamSpec *spec,
+                                  EphyDownloadWidget *widget)
+{
+  update_download_icon (widget);
+}
+
+static void
 widget_action_button_clicked_cb (EphyDownloadWidget *widget)
 {
   if (ephy_download_is_active (widget->priv->download)) {
@@ -490,6 +476,9 @@ ephy_download_widget_constructed (GObject *object)
   g_signal_connect (priv->download, "error",
                     G_CALLBACK (download_failed_cb),
                     widget);
+  g_signal_connect (priv->download, "notify::content-type",
+                    G_CALLBACK (download_content_type_changed_cb),
+                    widget);
 }
 
 static void


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