[epiphany/downloads: 2/7] ephy-download: new EphyDownload goodness



commit b8c79abe978f46a088e99cc8b1c1726e41a5a0e1
Author: Diego Escalante Urrelo <descalante igalia com>
Date:   Tue Jan 18 11:10:11 2011 -0500

    ephy-download: new EphyDownload goodness

 embed/Makefile.am        |    2 +
 embed/ephy-download.c    |  933 ++++++++++++++++++++++++++++++++++++++++++++++
 embed/ephy-download.h    |  115 ++++++
 embed/ephy-embed-shell.c |   26 ++
 embed/ephy-embed-shell.h |    2 +
 tests/Makefile.am        |    4 +
 tests/ephy-download.c    |  211 +++++++++++
 7 files changed, 1293 insertions(+), 0 deletions(-)
---
diff --git a/embed/Makefile.am b/embed/Makefile.am
index 5138043..fdd5f11 100644
--- a/embed/Makefile.am
+++ b/embed/Makefile.am
@@ -16,6 +16,7 @@ NOINST_H_FILES = \
 INST_H_FILES = \
 	ephy-adblock.h			\
 	ephy-adblock-manager.h		\
+	ephy-download.h			\
 	ephy-embed.h			\
 	ephy-embed-container.h          \
 	ephy-embed-event.h		\
@@ -37,6 +38,7 @@ libephyembed_la_SOURCES = \
 	ephy-adblock.c			\
 	ephy-adblock-manager.c		\
 	downloader-view.c		\
+	ephy-download.c			\
 	ephy-embed.c			\
 	ephy-embed-container.c          \
 	ephy-embed-dialog.c		\
diff --git a/embed/ephy-download.c b/embed/ephy-download.c
new file mode 100644
index 0000000..136e888
--- /dev/null
+++ b/embed/ephy-download.c
@@ -0,0 +1,933 @@
+/* vim: set foldmethod=marker sw=2 ts=2 sts=2 et: */
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * ephy-download.c
+ * This file is part of Epiphany
+ *
+ * Copyright © 2011 - Igalia S.L.
+ *
+ * Epiphany is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#include "ephy-debug.h"
+#include "ephy-download.h"
+#include "ephy-embed.h"
+#include "ephy-embed-shell.h"
+#include "ephy-embed-type-builtins.h"
+#include "ephy-file-chooser.h"
+#include "ephy-file-helpers.h"
+#include "ephy-prefs.h"
+
+#include <errno.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <webkit/webkit.h>
+
+G_DEFINE_TYPE (EphyDownload, ephy_download, G_TYPE_OBJECT)
+
+#define EPHY_DOWNLOAD_GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), EPHY_TYPE_DOWNLOAD, EphyDownloadPrivate))
+
+struct _EphyDownloadPrivate
+{
+  WebKitDownload *download;
+
+  char *destination;
+  char *source;
+
+  EphyDownloadActionType action;
+
+  /* Timestamp for focus-stealing prevention. */
+  guint32 start_time;
+
+  EphyEmbed *embed;
+
+  GtkTreeRowReference *row_ref;
+  GtkWidget *widget;
+};
+
+enum
+{
+  PROP_0,
+  PROP_DOWNLOAD,
+
+  PROP_DESTINATION,
+  PROP_SOURCE,
+
+  PROP_ACTION,
+
+  PROP_START_TIME,
+
+  PROP_EMBED
+};
+
+/* GObject boilerplate {{{*/
+static void
+ephy_download_get_property (GObject    *object,
+                            guint       property_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  EphyDownload *download;
+  EphyDownloadPrivate *priv;
+
+  download = EPHY_DOWNLOAD (object);
+  priv = download->priv;
+
+  switch (property_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    case PROP_EMBED:
+      g_value_set_object (value, priv->embed);
+      break;
+    case PROP_DOWNLOAD:
+      g_value_set_object (value, ephy_download_get_webkit_download (download));
+    case PROP_DESTINATION:
+      g_value_set_string (value, ephy_download_get_destination_uri (download));
+      break;
+    case PROP_SOURCE:
+      g_value_set_string (value, ephy_download_get_source_uri (download));
+      break;
+    case PROP_ACTION:
+      g_value_set_enum (value, ephy_download_get_action (download));
+      break;
+    case PROP_START_TIME:
+      g_value_set_uint (value, ephy_download_get_start_time (download));
+      break;
+    }
+}
+
+static void
+ephy_download_set_property (GObject      *object,
+                            guint         property_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  EphyDownload *download;
+  download = EPHY_DOWNLOAD (object);
+
+  switch (property_id)
+    {
+    case PROP_DOWNLOAD:
+    case PROP_SOURCE:
+    case PROP_START_TIME:
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    case PROP_DESTINATION:
+      ephy_download_set_destination_uri (download, g_value_get_string (value));
+      break;
+    case PROP_ACTION:
+      ephy_download_set_action (download, g_value_get_enum (value));
+      break;
+    case PROP_EMBED:
+      ephy_download_set_embed (download, g_value_get_object (value));
+      break;
+    }
+}/*}}}*/
+
+/* Utility functions {{{*/
+static void
+do_download_action (EphyDownload *ephy_download,
+                    EphyDownloadActionType action)
+{
+    GFile *destination;
+    const char *destination_uri;
+    EphyDownloadPrivate *priv;
+
+    priv = ephy_download->priv;
+
+    destination_uri = webkit_download_get_destination_uri (priv->download);
+    destination = g_file_new_for_uri (destination_uri);
+
+    switch ((action ? action : priv->action))
+    {
+      case EPHY_DOWNLOAD_ACTION_BROWSE_TO:
+      {
+        LOG ("download_status_changed_cb: browse_to");
+        ephy_file_browse_to (destination, priv->start_time);
+        break;
+      }
+      case EPHY_DOWNLOAD_ACTION_OPEN:
+      {
+        LOG ("download_status_changed_cb: open");
+        ephy_file_launch_handler (NULL, destination, priv->start_time);
+        break;
+      }
+      case EPHY_DOWNLOAD_ACTION_NONE:
+        LOG ("download_status_changed_cb: nothing to do");
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+    g_object_unref (destination);
+}
+
+static GtkTreeIter
+get_iter_for_download (EphyDownload *download)
+{
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
+  path = gtk_tree_row_reference_get_path (download->priv->row_ref);
+  gtk_tree_model_get_iter (GTK_TREE_MODEL (ephy_embed_shell_get_downloads (embed_shell)), &iter, path);
+
+  gtk_tree_path_free (path);
+
+  return iter;
+}
+
+static GdkPixbuf *
+get_icon_from_download (WebKitDownload *download)
+{
+  WebKitNetworkResponse *response;
+  SoupMessage *message;
+  const char *content_type;
+
+  GIcon *gicon;
+  GtkIconInfo *icon_info;
+  GdkPixbuf *icon = NULL;
+
+  response = webkit_download_get_network_response (download);
+  message = webkit_network_response_get_message (response);
+  content_type = soup_message_headers_get_content_type (message->response_headers, NULL);
+
+  if (content_type != NULL) {
+    gicon = g_content_type_get_icon (content_type);
+  } else {
+    gicon = g_icon_new_for_string ("package-x-generic", NULL);
+  }
+
+  icon_info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
+                                              gicon, 32,
+                                              GTK_ICON_LOOKUP_GENERIC_FALLBACK);
+
+  if (icon_info == NULL) return NULL;
+
+  icon = gtk_icon_info_load_icon (icon_info, NULL);
+
+  return icon;
+}
+
+static EphyDownloadActionType
+decide_action_from_mime (EphyDownload *ephy_download)
+{
+  WebKitNetworkResponse *response;
+  SoupMessage *message;
+  char *mime_description = NULL;
+  GAppInfo *helper_app = NULL;
+  EphyMimePermission mime_permission = EPHY_MIME_PERMISSION_SAFE;
+  EphyDownloadActionType action;
+  WebKitDownload *download;
+
+  download = ephy_download_get_webkit_download (ephy_download);
+
+  response = webkit_download_get_network_response (download);
+  message = webkit_network_response_get_message (response);
+
+  if (message) {
+    const char *content_type = soup_message_headers_get_content_type (message->response_headers, NULL);
+
+    if (content_type) {
+      mime_description = g_content_type_get_description (content_type);
+      helper_app = g_app_info_get_default_for_type (content_type, FALSE);
+      mime_permission = ephy_file_check_mime (content_type);
+
+      if (helper_app)
+        action = EPHY_DOWNLOAD_ACTION_OPEN;
+     }
+  }
+
+  if (mime_description == NULL) {
+    mime_description = g_strdup (C_("file type", "Unknown"));
+    action = EPHY_DOWNLOAD_ACTION_BROWSE_TO;
+  }
+
+  /* Sometimes downloads can have a mime_description but a NULL helper_app
+   * in that case action is never changed so DOWNLOAD_ACTION_DOWNLOAD remains
+   * as action value. This is the same response value as Save as...
+   * button, which is wrong for the Download button.
+   */
+  if (helper_app == NULL)
+    action = EPHY_DOWNLOAD_ACTION_BROWSE_TO;
+
+  return action;
+}
+
+/* From the old embed/mozilla/MozDownload.cpp */
+static const char*
+file_is_compressed (const char *filename)
+{
+  int i;
+  static const char * const compression[] = {".gz", ".bz2", ".Z", ".lz", NULL};
+
+  for (i = 0; compression[i] != NULL; i++) {
+    if (g_str_has_suffix (filename, compression[i]))
+      return compression[i];
+  }
+
+  return NULL;
+}
+
+static const char*
+parse_extension (const char *filename)
+{
+  const char *compression;
+  const char *last_separator;
+
+  compression = file_is_compressed (filename);
+
+  /* if the file is compressed we might have a double extension */
+  if (compression != NULL) {
+    int i;
+    static const char * const extensions[] = {"tar", "ps", "xcf", "dvi", "txt", "text", NULL};
+
+    for (i = 0; extensions[i] != NULL; i++) {
+      char *suffix;
+      suffix = g_strdup_printf (".%s%s", extensions[i], compression);
+
+      if (g_str_has_suffix (filename, suffix)) {
+        char *p;
+
+        p = g_strrstr (filename, suffix);
+        g_free (suffix);
+
+        return p;
+      }
+
+      g_free (suffix);
+    }
+  }
+
+  /* no compression, just look for the last dot in the filename */
+  last_separator = strrchr (filename, G_DIR_SEPARATOR);
+  return strrchr ((last_separator) ? last_separator : filename, '.');
+}
+
+static char *
+define_destination_uri (EphyDownload *download)
+{
+  char *tmp_dir;
+  char *destination_filename;
+  char *destination_uri;
+  char *tmp_name;
+  const char *suggested_filename;
+  WebKitDownload *wk_download;
+
+  g_assert (EPHY_IS_DOWNLOAD (download));
+
+  wk_download = download->priv->download;
+  suggested_filename = webkit_download_get_suggested_filename (wk_download);
+
+  tmp_dir = ephy_file_get_downloads_dir ();
+
+  /* Make sure the download directory exists */
+  if (g_mkdir_with_parents (tmp_dir, 0700) == -1) {
+    g_critical ("Could not create downloads directory \"%s\": %s",
+                tmp_dir, strerror (errno));
+    g_free (tmp_dir);
+    return NULL;
+  }
+
+  if (suggested_filename != NULL) {
+    tmp_name = g_strdup (suggested_filename);
+  } else {
+    tmp_name = ephy_file_tmp_filename ("ephy-download-XXXXXX", NULL);
+  }
+  /* FIXME: BUG BUG BUG BUG BUG BUG */
+
+  destination_filename = g_build_filename (tmp_dir, tmp_name, NULL);
+
+  /* Append (n) as needed. */
+  if (g_file_test (destination_filename, G_FILE_TEST_EXISTS)) {
+    int i = 1;
+    const char *dot_pos;
+    gssize position;
+    char *serial = NULL;
+    GString *tmp_filename;
+
+    dot_pos = parse_extension (destination_filename);
+    if (dot_pos)
+      position = dot_pos - destination_filename;
+    else
+      position = strlen (destination_filename);
+
+    tmp_filename = g_string_new (NULL);
+
+    do {
+      serial = g_strdup_printf ("(%d)", i++);
+
+      g_string_assign (tmp_filename, destination_filename);
+      g_string_insert (tmp_filename, position, serial);
+
+      g_free (serial);
+    } while (g_file_test (tmp_filename->str, G_FILE_TEST_EXISTS));
+
+    destination_filename = g_strdup (tmp_filename->str);
+    g_string_free (tmp_filename, TRUE);
+  }
+
+  destination_uri = g_filename_to_uri (destination_filename, NULL, NULL);
+  g_free (destination_filename);
+
+  g_free (tmp_dir);
+  g_free (tmp_name);
+
+  g_assert (destination_uri);
+
+  return destination_uri;
+}/*}}}*/
+
+/* Public API {{{*/
+/* IMPORTANT: USE GOD DAMN FILE:// PREFIX */
+void
+ephy_download_set_destination_uri (EphyDownload *download, const char *destination)
+{
+  EphyDownloadPrivate *priv;
+  char *scheme;
+
+  priv = download->priv;
+
+  /* This should be a file:///some-place not /some-place */
+  scheme = g_uri_parse_scheme (destination);
+  g_return_if_fail (scheme != NULL);
+  g_free (scheme);
+
+  priv->destination = g_strdup (destination);
+  LOG ("destination set: %s", destination);
+
+  webkit_download_set_destination_uri (priv->download, priv->destination);
+  g_object_notify (G_OBJECT (download), "destination");
+}
+
+void
+ephy_download_set_auto_destination (EphyDownload *download)
+{
+  char *dest;
+
+  dest = define_destination_uri (download);
+  ephy_download_set_destination_uri (download, dest);
+}
+
+void
+ephy_download_set_action (EphyDownload *download,
+                          EphyDownloadActionType action)
+{
+  download->priv->action = action;
+  g_object_notify (G_OBJECT (download), "action");
+}
+
+void
+ephy_download_set_embed (EphyDownload *download,
+                         EphyEmbed *embed)
+{
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
+  download->priv->embed = embed;
+
+  if (embed != NULL)
+    g_object_add_weak_pointer (G_OBJECT (embed), (gpointer *) &download->priv->embed);
+
+  if (download->priv->row_ref) {
+    iter = get_iter_for_download (download);
+    path = gtk_tree_model_get_path (GTK_TREE_MODEL (ephy_embed_shell_get_downloads (embed_shell)), &iter);
+    gtk_tree_model_row_changed (GTK_TREE_MODEL (ephy_embed_shell_get_downloads (embed_shell)), path, &iter);
+    gtk_tree_path_free (path);
+  }
+
+  g_object_notify (G_OBJECT (download), "embed");
+}
+
+WebKitDownload *
+ephy_download_get_webkit_download (EphyDownload *download)
+{
+  return download->priv->download;
+}
+
+const char *
+ephy_download_get_destination_uri (EphyDownload *download)
+{
+  return download->priv->destination;
+}
+
+const char *
+ephy_download_get_source_uri (EphyDownload *download)
+{
+  return download->priv->source;
+}
+
+EphyDownloadActionType
+ephy_download_get_action (EphyDownload *download)
+{
+  return download->priv->action;
+}
+
+guint32
+ephy_download_get_start_time (EphyDownload *download)
+{
+  return download->priv->start_time;
+}
+
+EphyEmbed *
+ephy_download_get_embed (EphyDownload *download)
+{
+  return download->priv->embed;
+}
+
+gboolean
+ephy_download_start (EphyDownload *download)
+{
+  EphyDownloadPrivate *priv;
+
+  g_return_val_if_fail (EPHY_IS_DOWNLOAD (download), FALSE);
+
+  priv = download->priv;
+  priv->start_time = gtk_get_current_event_time ();
+
+  if (priv->destination == NULL)
+    ephy_download_set_auto_destination (download);
+
+  webkit_download_start (priv->download);
+
+  return TRUE;
+}
+
+void
+ephy_download_cancel (EphyDownload *download)
+{
+  webkit_download_cancel (download->priv->download);
+}
+/*}}}*/
+
+/* GObject dispose/init/finalize {{{*/
+static void
+ephy_download_dispose (GObject *object)
+{
+  LOG ("EphyDownload disposed %p", object);
+
+  G_OBJECT_CLASS (ephy_download_parent_class)->dispose (object);
+}
+
+static void
+ephy_download_finalize (GObject *object)
+{
+  EphyDownload *download = EPHY_DOWNLOAD (object);
+  EphyDownloadPrivate *priv;
+
+  priv = download->priv;
+
+  if (priv->download) {
+    g_object_unref (priv->download);
+    priv->download = NULL;
+  }
+
+  if (priv->row_ref) {
+    gtk_tree_row_reference_free (priv->row_ref);
+    priv->row_ref = NULL;
+  }
+
+  if (priv->widget) {
+    g_object_unref (priv->widget);
+    priv->widget = NULL;
+  }
+
+  g_free (priv->destination);
+  g_free (priv->source);
+
+  LOG ("EphyDownload finalised %p", object);
+
+  G_OBJECT_CLASS (ephy_download_parent_class)->finalize (object);
+}
+
+static void
+ephy_download_class_init (EphyDownloadClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (EphyDownloadPrivate));
+
+  object_class->get_property = ephy_download_get_property;
+  object_class->set_property = ephy_download_set_property;
+  object_class->dispose = ephy_download_dispose;
+  object_class->finalize = ephy_download_finalize;
+
+  /**
+   * EphyDownload::download:
+   *
+   * Internal WebKitDownload, it's not recommended to depend on this value or
+   * modify it.
+   */
+  g_object_class_install_property (object_class, PROP_DOWNLOAD,
+                                   g_param_spec_object ("download",
+                                                        "Internal WebKitDownload",
+                                                        "The WebKitDownload used internally by EphyDownload",
+                                                        GTK_TYPE_WINDOW,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_NICK |
+                                                        G_PARAM_STATIC_BLURB));
+
+  /**
+   * EphyDownload::destination:
+   *
+   * The destination where to store the download.
+   */
+  g_object_class_install_property (object_class, PROP_DESTINATION,
+                                   g_param_spec_string ("destination",
+                                                        "Destination",
+                                                        "Destination file path",
+                                                        NULL,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_NICK |
+                                                        G_PARAM_STATIC_BLURB));
+  /**
+   * EphyDownload::source:
+   *
+   * Downloads origin URI
+   */
+  g_object_class_install_property (object_class, PROP_SOURCE,
+                                   g_param_spec_string ("source",
+                                                        "Source",
+                                                        "Source URI",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_NICK |
+                                                        G_PARAM_STATIC_BLURB));
+
+  /**
+   * EphyDownload::action:
+   *
+   * Action to take when the download finishes, one of #EphyDownloadAction.
+   */
+  g_object_class_install_property (object_class, PROP_ACTION,
+                                   g_param_spec_enum ("action",
+                                                      "Download action",
+                                                      "Action to take when download finishes",
+                                                      EPHY_TYPE_DOWNLOAD_ACTION_TYPE,
+                                                      EPHY_DOWNLOAD_ACTION_NONE,
+                                                      G_PARAM_READABLE |
+                                                      G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_NICK |
+                                                      G_PARAM_STATIC_BLURB));
+
+  /**
+   * EphyDownload::start-time:
+   *
+   * User time when the download started, useful for launching applications
+   * aware of focus stealing.
+   */
+  g_object_class_install_property (object_class, PROP_START_TIME,
+                                   g_param_spec_uint ("start-time",
+                                                      "Event start time",
+                                                      "Time for focus-stealing prevention.",
+                                                      0, G_MAXUINT32, 0,
+                                                      G_PARAM_READABLE |
+                                                      G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_NICK |
+                                                      G_PARAM_STATIC_BLURB));
+
+  /**
+   * EphyDownload::embed:
+   *
+   * Internal use property, at least for now.
+   */
+  g_object_class_install_property (object_class, PROP_EMBED,
+                                   g_param_spec_object ("embed",
+                                                        "A EphyEmbed",
+                                                        "Embed that produced this download.",
+                                                        G_TYPE_OBJECT,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_NICK |
+                                                        G_PARAM_STATIC_BLURB));
+
+  /**
+   * EphyDownload::completed:
+   *
+   * The ::completed signal is emitted when @download has finished downloading.
+   **/
+  g_signal_new ("completed",
+                G_OBJECT_CLASS_TYPE (object_class),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (EphyDownloadClass, completed),
+                NULL, NULL,
+                g_cclosure_marshal_VOID__VOID,
+                G_TYPE_NONE,
+                0);
+}
+
+static void
+ephy_download_init (EphyDownload *download)
+{
+  download->priv = EPHY_DOWNLOAD_GET_PRIVATE (download);
+
+  LOG ("EphyDownload initialising %p", download);
+
+  download->priv->download = NULL;
+  download->priv->destination = NULL;
+
+  download->priv->action = EPHY_DOWNLOAD_ACTION_NONE;
+
+  download->priv->start_time = 0;
+
+  download->priv->embed = NULL;
+
+  download->priv->row_ref = NULL;
+  download->priv->widget = NULL;
+}
+/*}}}*/
+
+static void
+download_status_changed_cb (GObject *object,
+                            GParamSpec *pspec,
+                            EphyDownload *download)
+{
+  WebKitDownloadStatus status;
+  EphyDownloadPrivate *priv;
+
+  priv = download->priv;
+
+  status = webkit_download_get_status (priv->download);
+
+  if (status == WEBKIT_DOWNLOAD_STATUS_FINISHED) {
+    do_download_action (download, EPHY_DOWNLOAD_ACTION_NONE);
+    g_signal_emit_by_name (download, "completed");
+  } else if (status == WEBKIT_DOWNLOAD_STATUS_CANCELLED ||
+             status == WEBKIT_DOWNLOAD_STATUS_ERROR) {
+  } else if (status == WEBKIT_DOWNLOAD_STATUS_STARTED) {
+    if (priv->embed != NULL) {
+      GtkListStore *store;
+      GtkTreeIter iter;
+      GtkTreePath *path;
+
+      store = ephy_embed_shell_get_downloads (embed_shell);
+
+      gtk_list_store_insert_with_values (store, &iter, -1, 0, download, -1);
+
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+      priv->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store), path);
+
+      gtk_tree_path_free (path);
+    }
+  }
+}
+
+static gboolean
+download_error_cb (WebKitDownload *download,
+                   gint error_code,
+                   gint error_detail,
+                   char *reason,
+                   gpointer user_data)
+{
+  LOG ("error (%d - %d)! %s", error_code, error_detail, reason);
+
+  return FALSE;
+}
+
+EphyDownload *
+ephy_download_new (void)
+{
+  return g_object_new (EPHY_TYPE_DOWNLOAD, NULL);
+}
+
+static EphyDownload *
+_ephy_download_new (WebKitDownload *download, const char *uri)
+{
+  EphyDownload *ephy_download;
+  WebKitDownload *webkit_download;
+
+  ephy_download = ephy_download_new ();
+  webkit_download = download;
+
+  if (download == NULL) {
+    WebKitNetworkRequest *request;
+
+    request = webkit_network_request_new (uri);
+    webkit_download = webkit_download_new (request);
+
+    g_return_val_if_fail (webkit_download != NULL, NULL);
+    g_object_unref (request);
+  }
+
+  g_signal_connect (webkit_download, "notify::status",
+                    G_CALLBACK (download_status_changed_cb),
+                    ephy_download);
+  g_signal_connect (webkit_download, "error",
+                    G_CALLBACK (download_error_cb),
+                    ephy_download);
+
+  ephy_download->priv->download = g_object_ref (webkit_download);
+  ephy_download->priv->source = g_strdup (webkit_download_get_uri (webkit_download));
+
+  return ephy_download;
+}
+
+EphyDownload *
+ephy_download_new_for_download (WebKitDownload *download)
+{
+  return _ephy_download_new (download, NULL);
+}
+
+EphyDownload *
+ephy_download_new_for_uri (const char *uri)
+{
+  return _ephy_download_new (NULL, uri);
+}
+
+static void
+download_clicked_cb (GtkButton *button,
+                     EphyDownload *download)
+{
+  GtkTreeIter iter;
+  WebKitDownloadStatus status;
+
+  status = webkit_download_get_status (download->priv->download);
+
+  if (status != WEBKIT_DOWNLOAD_STATUS_FINISHED)
+    return;
+
+  do_download_action (download, decide_action_from_mime (download));
+
+  gtk_widget_destroy (download->priv->widget);
+
+  iter = get_iter_for_download (download);
+  gtk_list_store_remove (ephy_embed_shell_get_downloads (embed_shell), &iter);
+
+  g_object_unref (download);
+}
+
+static void
+open_activate_cb (GtkMenuItem *item, EphyDownload *download)
+{
+  do_download_action (download, EPHY_DOWNLOAD_ACTION_OPEN);
+}
+static void
+folder_activate_cb (GtkMenuItem *item, EphyDownload *download)
+{
+  do_download_action (download, EPHY_DOWNLOAD_ACTION_BROWSE_TO);
+}
+static void
+cancel_activate_cb (GtkMenuItem *item, EphyDownload *download)
+{
+  ephy_download_cancel (download);
+}
+
+static void
+download_menu_clicked_cb (GtkWidget *button,
+                          GdkEventButton *event,
+                          EphyDownload *download)
+{
+  WebKitDownloadStatus status;
+  GtkWidget *item;
+  GtkWidget *menu;
+
+  status = webkit_download_get_status (download->priv->download);
+
+  menu = gtk_menu_new ();
+
+  item = gtk_menu_item_new_with_label (_("Open"));
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  g_signal_connect (item, "activate", G_CALLBACK (open_activate_cb), download);
+
+  item = gtk_menu_item_new_with_label (_("Show in folder"));
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  g_signal_connect (item, "activate", G_CALLBACK (folder_activate_cb), download);
+
+  item = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  item = gtk_menu_item_new_with_label (_("Cancel"));
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  g_signal_connect (item, "activate", G_CALLBACK (cancel_activate_cb), download);
+
+  gtk_widget_show_all (menu);
+
+  gtk_menu_attach_to_widget (GTK_MENU (menu), button, NULL);
+  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time);
+}
+
+GtkWidget *
+ephy_download_get_widget (EphyDownload *download)
+{
+  EphyDownloadPrivate *priv;
+
+  priv = download->priv;
+
+  if (priv->widget == NULL) {
+    GtkWidget *grid;
+    GtkWidget *hbox;
+    GtkWidget *icon;
+    GtkWidget *text;
+    GtkWidget *progress;
+    GtkWidget *button;
+    GtkWidget *menu;
+
+    char *dest;
+    GdkPixbuf *pixbuf;
+
+    pixbuf = get_icon_from_download (priv->download);
+    dest = g_filename_display_basename (priv->destination);
+
+    grid = gtk_grid_new ();
+    hbox = gtk_hbox_new (FALSE, 0);
+
+    icon = gtk_image_new_from_pixbuf (pixbuf);
+    progress = gtk_progress_bar_new ();
+    text = gtk_label_new (dest);
+
+    gtk_grid_attach (GTK_GRID (grid), icon, 0, 0, 1, 2);
+    gtk_grid_attach (GTK_GRID (grid), text, 1, 0, 1, 1);
+    gtk_grid_attach (GTK_GRID (grid), progress, 1, 1, 1, 1);
+
+    g_object_unref (pixbuf);
+    g_free (dest);
+
+    g_object_bind_property (priv->download, "progress",
+                            progress, "fraction", G_BINDING_SYNC_CREATE);
+
+    button = gtk_button_new ();
+    menu = gtk_button_new ();
+
+    gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+    gtk_button_set_relief (GTK_BUTTON (menu), GTK_RELIEF_NONE);
+
+    gtk_container_add (GTK_CONTAINER (button), grid);
+    gtk_container_add (GTK_CONTAINER (menu), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
+
+    gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+    gtk_box_pack_end (GTK_BOX (hbox), menu, FALSE, TRUE, 0);
+
+    g_signal_connect (button, "clicked", G_CALLBACK (download_clicked_cb), download);
+    g_signal_connect (menu, "button-press-event", G_CALLBACK (download_menu_clicked_cb), download);
+
+    gtk_widget_show_all (hbox);
+    priv->widget = hbox;
+
+    g_object_add_weak_pointer (G_OBJECT (priv->widget), (gpointer *) &priv->widget);
+
+    g_object_set_data (G_OBJECT (priv->widget), "download", download);
+  }
+
+  return priv->widget;
+}
diff --git a/embed/ephy-download.h b/embed/ephy-download.h
new file mode 100644
index 0000000..2e3c043
--- /dev/null
+++ b/embed/ephy-download.h
@@ -0,0 +1,115 @@
+/* vim: set sw=2 ts=2 sts=2 et: */
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * ephy-download.h
+ * This file is part of Epiphany
+ *
+ * Copyright © 2011 - Igalia S.L.
+ *
+ * Epiphany is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _EPHY_DOWNLOAD_H
+#define _EPHY_DOWNLOAD_H
+
+#include <glib-object.h>
+#include <webkit/webkit.h>
+
+#include "ephy-embed.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_DOWNLOAD ephy_download_get_type()
+
+#define EPHY_DOWNLOAD(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+  EPHY_TYPE_DOWNLOAD, EphyDownload))
+
+#define EPHY_DOWNLOAD_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+  EPHY_TYPE_DOWNLOAD, EphyDownloadClass))
+
+#define EPHY_IS_DOWNLOAD(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+  EPHY_TYPE_DOWNLOAD))
+
+#define EPHY_IS_DOWNLOAD_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+  EPHY_TYPE_DOWNLOAD))
+
+#define EPHY_DOWNLOAD_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+  EPHY_TYPE_DOWNLOAD, EphyDownloadClass))
+
+typedef struct _EphyDownload EphyDownload;
+typedef struct _EphyDownloadClass EphyDownloadClass;
+typedef struct _EphyDownloadPrivate EphyDownloadPrivate;
+
+struct _EphyDownload
+{
+  GObject parent;
+
+  EphyDownloadPrivate *priv;
+};
+
+struct _EphyDownloadClass
+{
+  GObjectClass parent_class;
+
+  void (* completed) (EphyDownload *download);
+};
+
+typedef enum
+{
+  EPHY_DOWNLOAD_ACTION_NONE,
+  EPHY_DOWNLOAD_ACTION_BROWSE_TO,
+  EPHY_DOWNLOAD_ACTION_OPEN
+} EphyDownloadActionType;
+
+GType ephy_download_get_type (void) G_GNUC_CONST;
+
+EphyDownload *ephy_download_new (void);
+EphyDownload *ephy_download_new_for_uri (const char *uri);
+EphyDownload *ephy_download_new_for_download (WebKitDownload *download);
+
+
+gboolean ephy_download_start (EphyDownload *download);
+void ephy_download_pause (EphyDownload *download);
+void ephy_download_cancel (EphyDownload *download);
+
+void ephy_download_set_destination_uri (EphyDownload *download,
+                                    const char *destination);
+void ephy_download_set_auto_destination (EphyDownload *download);
+
+void ephy_download_set_action (EphyDownload *download,
+                               EphyDownloadActionType action);
+
+WebKitDownload *ephy_download_get_webkit_download (EphyDownload *download);
+const char *ephy_download_get_destination_uri (EphyDownload *download);
+const char *ephy_download_get_source_uri (EphyDownload *download);
+
+EphyDownloadActionType ephy_download_get_action (EphyDownload *download);
+
+guint32 ephy_download_get_start_time (EphyDownload *download);
+
+GtkWidget *ephy_download_get_widget (EphyDownload *download);
+
+void ephy_download_set_embed (EphyDownload *download, EphyEmbed *embed);
+EphyEmbed *ephy_download_get_embed (EphyDownload *download);
+
+G_END_DECLS
+
+#endif /* _EPHY_DOWNLOAD_H */
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 3fcf7ee..40cf520 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -28,6 +28,7 @@
 #include "downloader-view.h"
 #include "ephy-adblock-manager.h"
 #include "ephy-debug.h"
+#include "ephy-download.h"
 #include "ephy-embed-shell.h"
 #include "ephy-embed-single.h"
 #include "ephy-encodings.h"
@@ -51,6 +52,7 @@ struct _EphyEmbedShellPrivate
 {
 	EphyHistory *global_history;
 	DownloaderView *downloader_view;
+	GtkListStore *downloads;
 	EphyFaviconCache *favicon_cache;
 	EphyEmbedSingle *embed_single;
 	EphyEncodings *encodings;
@@ -94,6 +96,14 @@ ephy_embed_shell_dispose (GObject *object)
 		priv->downloader_view = NULL;
 	}
 
+	if (priv->downloads != NULL)
+	{
+		LOG ("Destroying downloads hash table");
+		/* FIXME: needs freeing of members */
+		g_object_unref (priv->downloads);
+		priv->downloads = NULL;
+	}
+
 	if (priv->favicon_cache != NULL)
 	{
 		LOG ("Unref favicon cache");
@@ -563,6 +573,22 @@ ephy_embed_shell_get_print_settings (EphyEmbedShell *shell)
 	return priv->print_settings;
 }
 
+GtkListStore *
+ephy_embed_shell_get_downloads (EphyEmbedShell *shell)
+{
+	EphyEmbedShellPrivate *priv;
+
+	g_return_val_if_fail (EPHY_IS_EMBED_SHELL (shell), NULL);
+	priv = shell->priv;
+
+	if (priv->downloads == NULL)
+	{
+		priv->downloads = gtk_list_store_new (1, G_TYPE_POINTER);
+	}
+
+	return priv->downloads;
+}
+
 
 static void
 object_notify_cb (EphyEmbedShell *shell, GObject *object)
diff --git a/embed/ephy-embed-shell.h b/embed/ephy-embed-shell.h
index c58df97..45c9d3d 100644
--- a/embed/ephy-embed-shell.h
+++ b/embed/ephy-embed-shell.h
@@ -91,6 +91,8 @@ void		   ephy_embed_shell_set_print_settings	(EphyEmbedShell *shell,
 		
 GtkPrintSettings  *ephy_embed_shell_get_print_settings	(EphyEmbedShell *shell);
 
+GtkListStore *ephy_embed_shell_get_downloads (EphyEmbedShell *shell);
+
 /* Private API */
 void	       _ephy_embed_shell_track_object		(EphyEmbedShell *shell,
 							 GObject        *object);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8cba757..1b5aa15 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,5 @@
 noinst_PROGRAMS = \
+	test-ephy-download \
 	test-ephy-embed-persist \
 	test-ephy-embed-single \
 	test-ephy-location-entry \
@@ -33,6 +34,9 @@ LDADD += \
 	$(SEED_LIBS)
 endif
 
+test_ephy_download_SOURCES = \
+	ephy-download.c
+
 test_ephy_embed_persist_SOURCES = \
 	ephy-embed-persist.c
 
diff --git a/tests/ephy-download.c b/tests/ephy-download.c
new file mode 100644
index 0000000..69517eb
--- /dev/null
+++ b/tests/ephy-download.c
@@ -0,0 +1,211 @@
+/* vim: set sw=2 ts=2 sts=2 et: */
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * ephy-download.c
+ * This file is part of Epiphany
+ *
+ * Copyright © 2011 - Igalia S.L.
+ *
+ * Epiphany is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+#include "ephy-debug.h"
+#include "ephy-download.h"
+#include "ephy-embed-prefs.h"
+#include "ephy-file-helpers.h"
+#include "ephy-shell.h"
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define HTML_STRING "testing-embed-persist"
+SoupURI *base_uri;
+
+static char *
+get_uri_for_path (const char *path)
+{
+  SoupURI *uri;
+  char *uri_string;
+
+  uri = soup_uri_new_with_base (base_uri, path);
+  uri_string = soup_uri_to_string (uri, FALSE);
+  soup_uri_free (uri);
+
+  return uri_string;
+}
+
+static void
+server_callback (SoupServer *server,
+                 SoupMessage *msg,
+                 const char *path,
+                 GHashTable *query,
+                 SoupClientContext *context,
+                 gpointer data)
+{
+  soup_message_set_status (msg, SOUP_STATUS_OK);
+
+  if (g_str_equal (path, "/cancelled"))
+    soup_message_set_status (msg, SOUP_STATUS_CANT_CONNECT);
+
+  soup_message_body_append (msg->response_body, SOUP_MEMORY_STATIC,
+                            HTML_STRING, strlen (HTML_STRING));
+
+  soup_message_body_complete (msg->response_body);
+}
+
+typedef struct {
+  GMainLoop *loop;
+  EphyDownload *download;
+  char *destination;
+  char *source;
+} Fixture;
+
+static void
+fixture_setup (Fixture *fixture, gconstpointer data)
+{
+  char *tmp_filename;
+  char *dest_file;
+
+  tmp_filename = ephy_file_tmp_filename ("ephy-download-XXXXXX", NULL);
+  dest_file = g_build_filename (ephy_file_tmp_dir (), tmp_filename, NULL);
+
+  fixture->source = get_uri_for_path ("/default");
+  fixture->download = ephy_download_new_for_uri (fixture->source);
+  fixture->destination = g_filename_to_uri (dest_file, NULL, NULL);
+  fixture->loop = g_main_loop_new (NULL, TRUE);
+
+  ephy_download_set_destination_uri (fixture->download, fixture->destination);
+
+  g_free (tmp_filename);
+  g_free (dest_file);
+}
+
+static void
+fixture_teardown (Fixture *fixture, gconstpointer data)
+{
+  g_free (fixture->destination);
+  g_free (fixture->source);
+
+  g_object_unref (fixture->download);
+
+  g_main_loop_unref (fixture->loop);
+}
+
+static gboolean
+test_file_was_downloaded (EphyDownload *download)
+{
+  char *filename;
+  gboolean ret;
+
+  filename = g_filename_from_uri (ephy_download_get_destination_uri (download),
+                                  NULL, NULL);
+
+  ret = g_file_test (filename, G_FILE_TEST_EXISTS);
+  g_free (filename);
+
+  return ret;
+}
+
+static void
+completed_cb (EphyDownload *download,
+              Fixture *fixture)
+{
+  g_assert (test_file_was_downloaded (download));
+  g_main_loop_quit (fixture->loop);
+}
+
+static void
+test_ephy_download_new (Fixture *fixture, gconstpointer data)
+{
+  g_assert (EPHY_IS_DOWNLOAD (fixture->download));
+}
+
+static void
+test_ephy_download_new_for_uri (Fixture *fixture, gconstpointer data)
+{
+  EphyDownload *download;
+
+  download = ephy_download_new_for_uri (fixture->source);
+
+  g_assert (EPHY_IS_DOWNLOAD (download));
+
+  g_assert_cmpstr (fixture->source, ==, ephy_download_get_source_uri (download));
+
+  g_signal_connect (G_OBJECT (download), "completed",
+                    G_CALLBACK (completed_cb), fixture);
+
+  g_assert (ephy_download_start (download) == TRUE);
+
+  g_main_loop_run (fixture->loop);
+}
+
+static void
+test_ephy_download_start (Fixture *fixture, gconstpointer data)
+{
+  g_signal_connect (G_OBJECT (fixture->download), "completed",
+                    G_CALLBACK (completed_cb), fixture);
+
+  g_assert (ephy_download_start (fixture->download) == TRUE);
+
+  g_main_loop_run (fixture->loop);
+}
+
+int
+main (int argc, char *argv[])
+{
+  int ret;
+  SoupServer *server;
+
+  gtk_test_init (&argc, &argv);
+  g_thread_init (NULL);
+
+  ephy_debug_init ();
+  ephy_embed_prefs_init ();
+  _ephy_shell_create_instance ();
+
+  if (!ephy_file_helpers_init (NULL, TRUE, FALSE, NULL)) {
+    g_debug ("Something wrong happened with ephy_file_helpers_init()");
+    return -1;
+  }
+
+  server = soup_server_new (SOUP_SERVER_PORT, 0, NULL);
+  soup_server_run_async (server);
+
+  base_uri = soup_uri_new ("http://127.0.0.1/";);
+  soup_uri_set_port (base_uri, soup_server_get_port (server));
+
+  soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+
+  g_test_add ("/embed/ephy-download/new",
+              Fixture, NULL, fixture_setup,
+              test_ephy_download_new, fixture_teardown);
+  g_test_add ("/embed/ephy-download/new_for_uri",
+              Fixture, NULL, fixture_setup,
+              test_ephy_download_new_for_uri, fixture_teardown);
+  g_test_add ("/embed/ephy-download/start",
+              Fixture, NULL, fixture_setup,
+              test_ephy_download_start, fixture_teardown);
+
+  ret = g_test_run ();
+
+  g_object_unref (ephy_shell);
+  ephy_file_helpers_shutdown ();
+
+  return ret;
+}



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