[epiphany] web-extension: Implement pre-filled forms in WebKit2



commit 3b5a81725a5fea3f90c028e6d1f831584baaa586
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Thu Mar 14 09:48:26 2013 +0100

    web-extension: Implement pre-filled forms in WebKit2
    
    https://bugzilla.gnome.org/show_bug.cgi?id=684439

 configure.ac                               |    5 +-
 embed/ephy-embed-shell.c                   |   67 +++++-
 embed/ephy-web-view.c                      |   66 ++++-
 embed/web-extension/Makefile.am            |    6 +
 embed/web-extension/ephy-embed-form-auth.c |  104 +++++++
 embed/web-extension/ephy-embed-form-auth.h |   61 ++++
 embed/web-extension/ephy-web-extension.c   |  416 +++++++++++++++++++++++++---
 src/ephy-main.c                            |    2 +
 8 files changed, 675 insertions(+), 52 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 0729716..a9518b9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -136,7 +136,10 @@ PKG_CHECK_MODULES([DEPENDENCIES], [
                  avahi-client >= $AVAHI_REQUIRED
                  ])
 
-PKG_CHECK_MODULES(WEB_EXTENSION, [$WEBKIT_GTK_PC_NAME >= $WEBKIT_GTK_REQUIRED])
+PKG_CHECK_MODULES(WEB_EXTENSION, [
+                  $WEBKIT_GTK_PC_NAME >= $WEBKIT_GTK_REQUIRED
+                  libsecret-1 >= $LIBSECRET_REQUIRED
+                  ])
 AC_SUBST(WEB_EXTENSION_CFLAGS)
 AC_SUBST(WEB_EXTENSION_LIBS)
 
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 42ba8ed..b775075 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -65,6 +65,7 @@ struct _EphyEmbedShellPrivate
 #ifdef HAVE_WEBKIT2
   GDBusProxy *web_extension;
   guint web_extension_watch_name_id;
+  guint web_extension_form_auth_save_signal_id;
 #endif
 };
 
@@ -75,6 +76,8 @@ enum
   PREPARE_CLOSE,
   RESTORED_WINDOW,
   WEB_VIEW_CREATED,
+  FORM_AUTH_DATA_SAVE_REQUESTED,
+
   LAST_SIGNAL
 };
 
@@ -105,11 +108,16 @@ ephy_embed_shell_dispose (GObject *object)
   g_clear_object (&priv->global_history_service);
   g_clear_object (&priv->embed_single);
 #ifdef HAVE_WEBKIT2
-  g_clear_object (&priv->web_extension);
   if (priv->web_extension_watch_name_id > 0) {
     g_bus_unwatch_name (priv->web_extension_watch_name_id);
     priv->web_extension_watch_name_id = 0;
   }
+  if (priv->web_extension_form_auth_save_signal_id > 0) {
+    g_dbus_connection_signal_unsubscribe (g_dbus_proxy_get_connection (priv->web_extension),
+                                          priv->web_extension_form_auth_save_signal_id);
+    priv->web_extension_form_auth_save_signal_id = 0;
+  }
+  g_clear_object (&priv->web_extension);
 #else
   g_clear_object (&priv->adblock_manager);
 #endif
@@ -134,7 +142,26 @@ ephy_embed_shell_finalize (GObject *object)
 
 #if HAVE_WEBKIT2
 static void
-web_extension_proxy_created_cb (GDBusConnection *connection,
+web_extension_form_auth_save_requested (GDBusConnection *connection,
+                                        const char *sender_name,
+                                        const char *object_path,
+                                        const char *interface_name,
+                                        const char *signal_name,
+                                        GVariant *parameters,
+                                        EphyEmbedShell *shell)
+{
+  guint request_id;
+  guint64 page_id;
+  const char *hostname;
+  const char *username;
+
+  g_variant_get (parameters, "(ut&s&s)", &request_id, &page_id, &hostname, &username);
+  g_signal_emit (shell, signals[FORM_AUTH_DATA_SAVE_REQUESTED], 0,
+                 request_id, page_id, hostname, username);
+}
+
+static void
+web_extension_proxy_created_cb (GDBusProxy *proxy,
                                 GAsyncResult *result,
                                 EphyEmbedShell *shell)
 {
@@ -144,6 +171,18 @@ web_extension_proxy_created_cb (GDBusConnection *connection,
   if (!shell->priv->web_extension) {
     g_warning ("Error creating web extension proxy: %s\n", error->message);
     g_error_free (error);
+  } else {
+    shell->priv->web_extension_form_auth_save_signal_id =
+      g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (shell->priv->web_extension),
+                                          NULL,
+                                          EPHY_WEB_EXTENSION_INTERFACE,
+                                         "FormAuthDataSaveConfirmationRequired",
+                                          EPHY_WEB_EXTENSION_OBJECT_PATH,
+                                          NULL,
+                                          G_DBUS_SIGNAL_FLAGS_NONE,
+                                          (GDBusSignalCallback)web_extension_form_auth_save_requested,
+                                          shell,
+                                          NULL);
   }
 }
 
@@ -510,6 +549,30 @@ ephy_embed_shell_class_init (EphyEmbedShellClass *klass)
                   G_TYPE_NONE, 1,
                   EPHY_TYPE_WEB_VIEW);
 
+  /*
+   * EphyEmbedShell::form-auth-data-save-requested:
+   * @shell: the #EphyEmbedShell
+   * @request_id: the identifier of the request
+   * @page_id: the identifier of the web page
+   * @hostname: the hostname
+   * @username: the username
+   *
+   * Emitted when a web page requests confirmation to save
+   * the form authentication data for the given @hostname and
+   * @username
+   **/
+  signals[FORM_AUTH_DATA_SAVE_REQUESTED] =
+    g_signal_new ("form-auth-data-save-requested",
+                  EPHY_TYPE_EMBED_SHELL,
+                  G_SIGNAL_RUN_FIRST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE, 4,
+                  G_TYPE_UINT,
+                  G_TYPE_UINT64,
+                  G_TYPE_STRING,
+                  G_TYPE_STRING);
+
   g_type_class_add_private (object_class, sizeof (EphyEmbedShellPrivate));
 }
 
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index 423c35e..7bbd674 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -509,9 +509,6 @@ ephy_web_view_dispose (GObject *object)
   G_OBJECT_CLASS (ephy_web_view_parent_class)->dispose (object);
 }
 
-#ifdef HAVE_WEBKIT2
-/* TODO: DOM bindings */
-#else
 static GtkWidget *
 ephy_web_view_create_form_auth_save_confirmation_info_bar (EphyWebView *web_view,
                                                            const char *hostname,
@@ -562,6 +559,7 @@ ephy_web_view_create_form_auth_save_confirmation_info_bar (EphyWebView *web_view
   return info_bar;
 }
 
+#ifndef HAVE_WEBKIT2
 typedef struct {
   WebKitDOMNode *username_node;
   WebKitDOMNode *password_node;
@@ -2139,16 +2137,6 @@ load_changed_cb (WebKitWebView *web_view,
     if (priv->is_blank || !webkit_web_view_get_title (web_view))
       ephy_web_view_set_title (view, NULL);
 
-#if 0
-    /* TODO: DOM bindings */
-    if (!EPHY_EMBED_SHELL_MODE_HAS_PRIVATE_PROFILE(ephy_embed_shell_get_mode (ephy_embed_shell_get_default 
())) &&
-        g_settings_get_boolean (EPHY_SETTINGS_MAIN,
-                                EPHY_PREFS_REMEMBER_PASSWORDS))
-      _ephy_web_view_hook_into_forms (view);
-
-    _ephy_web_view_hook_into_links (view);
-#endif
-
     /* Ensure we load the icon for this web view, if available. */
     _ephy_web_view_update_icon (view);
 
@@ -2616,6 +2604,51 @@ zoom_changed_cb (WebKitWebView *web_view,
 }
 
 #ifdef HAVE_WEBKIT2
+static void
+form_auth_data_save_confirmation_response (GtkInfoBar *info_bar,
+                                           gint response_id,
+                                           gpointer user_data)
+{
+  GDBusProxy *web_extension;
+  guint request_id = GPOINTER_TO_INT (user_data);
+
+  gtk_widget_destroy (GTK_WIDGET (info_bar));
+
+  web_extension = ephy_embed_shell_get_web_extension_proxy (ephy_embed_shell_get_default ());
+  if (!web_extension)
+    return;
+
+  g_dbus_proxy_call (web_extension,
+                     "FormAuthDataSaveConfirmationResponse",
+                     g_variant_new ("(ub)", request_id, response_id == GTK_RESPONSE_YES),
+                     G_DBUS_CALL_FLAGS_NONE,
+                     -1, NULL, NULL, NULL);
+}
+
+static void
+form_auth_data_save_requested (EphyEmbedShell *shell,
+                               guint request_id,
+                               guint64 page_id,
+                               const char *hostname,
+                               const char *username,
+                               WebKitWebView *web_view)
+{
+  GtkWidget *info_bar;
+
+  if (webkit_web_view_get_page_id (web_view) != page_id)
+    return;
+
+  info_bar = ephy_web_view_create_form_auth_save_confirmation_info_bar (EPHY_WEB_VIEW (web_view),
+                                                                        hostname, username);
+  g_signal_connect (info_bar, "response",
+                    G_CALLBACK (form_auth_data_save_confirmation_response),
+                    GINT_TO_POINTER (request_id));
+
+  gtk_widget_show (info_bar);
+}
+#endif
+
+#ifdef HAVE_WEBKIT2
 /* TODO: WebKitWebResource::send-request */
 #else
 static void
@@ -2775,6 +2808,13 @@ ephy_web_view_init (EphyWebView *web_view)
   g_signal_connect (web_view, "ge_popup_blocked",
                     G_CALLBACK (ge_popup_blocked_cb),
                     NULL);
+
+#ifdef HAVE_WEBKIT2
+  g_signal_connect (ephy_embed_shell_get_default (), "form-auth-data-save-requested",
+                    G_CALLBACK (form_auth_data_save_requested),
+                    web_view);
+#endif
+
 #ifdef HAVE_WEBKIT2
   /* TODO: WebKitWebResource::send-request */
 #else
diff --git a/embed/web-extension/Makefile.am b/embed/web-extension/Makefile.am
index f6fb603..4fc539c 100644
--- a/embed/web-extension/Makefile.am
+++ b/embed/web-extension/Makefile.am
@@ -4,14 +4,20 @@ webextensiondir = \
        $(libdir)/epiphany/$(EPIPHANY_MAJOR)/web-extensions
 
 libephywebextension_la_SOURCES = \
+       ephy-embed-form-auth.c \
+       ephy-embed-form-auth.h \
        ephy-web-extension.c \
        ephy-web-extension.h \
        $(top_srcdir)/embed/uri-tester.c \
        $(top_srcdir)/embed/uri-tester.h \
        $(top_srcdir)/lib/ephy-debug.c \
        $(top_srcdir)/lib/ephy-debug.h \
+       $(top_srcdir)/lib/ephy-form-auth-data.c \
+       $(top_srcdir)/lib/ephy-form-auth-data.h \
        $(top_srcdir)/lib/ephy-settings.c \
        $(top_srcdir)/lib/ephy-settings.h \
+       $(top_srcdir)/lib/ephy-string.c \
+       $(top_srcdir)/lib/ephy-string.h \
        $(top_srcdir)/lib/ephy-web-dom-utils.c \
        $(top_srcdir)/lib/ephy-web-dom-utils.h
 
diff --git a/embed/web-extension/ephy-embed-form-auth.c b/embed/web-extension/ephy-embed-form-auth.c
new file mode 100644
index 0000000..9eda220
--- /dev/null
+++ b/embed/web-extension/ephy-embed-form-auth.c
@@ -0,0 +1,104 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2013 Igalia S.L.
+ *
+ *  This program 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, or (at your option)
+ *  any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <config.h>
+#include "ephy-embed-form-auth.h"
+
+struct _EphyEmbedFormAuthPrivate
+{
+  guint64 page_id;
+  SoupURI *uri;
+  WebKitDOMNode *username_node;
+  WebKitDOMNode *password_node;
+};
+
+G_DEFINE_TYPE (EphyEmbedFormAuth, ephy_embed_form_auth, G_TYPE_OBJECT)
+
+static void
+ephy_embed_form_auth_finalize (GObject *object)
+{
+  EphyEmbedFormAuthPrivate *priv = EPHY_EMBED_FORM_AUTH (object)->priv;
+
+  if (priv->uri)
+    soup_uri_free (priv->uri);
+  g_clear_object (&priv->username_node);
+  g_clear_object (&priv->password_node);
+
+  G_OBJECT_CLASS (ephy_embed_form_auth_parent_class)->finalize (object);
+}
+
+static void
+ephy_embed_form_auth_init (EphyEmbedFormAuth *form_auth)
+{
+  form_auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (form_auth, EPHY_TYPE_EMBED_FORM_AUTH, 
EphyEmbedFormAuthPrivate);
+}
+
+static void
+ephy_embed_form_auth_class_init (EphyEmbedFormAuthClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ephy_embed_form_auth_finalize;
+  g_type_class_add_private (object_class, sizeof (EphyEmbedFormAuthPrivate));
+}
+
+EphyEmbedFormAuth *
+ephy_embed_form_auth_new (WebKitWebPage *web_page,
+                          WebKitDOMNode *username_node,
+                          WebKitDOMNode *password_node)
+{
+  EphyEmbedFormAuth *form_auth;
+
+  g_return_val_if_fail (WEBKIT_DOM_IS_NODE (username_node), NULL);
+  g_return_val_if_fail (WEBKIT_DOM_IS_NODE (password_node), NULL);
+
+  form_auth = EPHY_EMBED_FORM_AUTH (g_object_new (EPHY_TYPE_EMBED_FORM_AUTH, NULL));
+
+  form_auth->priv->page_id = webkit_web_page_get_id (web_page);
+  form_auth->priv->uri = soup_uri_new (webkit_web_page_get_uri (web_page));
+  form_auth->priv->username_node = username_node;
+  form_auth->priv->password_node = password_node;
+
+  return form_auth;
+}
+
+WebKitDOMNode *
+ephy_embed_form_auth_get_username_node (EphyEmbedFormAuth *form_auth)
+{
+  return form_auth->priv->username_node;
+}
+
+WebKitDOMNode *
+ephy_embed_form_auth_get_password_node (EphyEmbedFormAuth *form_auth)
+{
+  return form_auth->priv->password_node;
+}
+
+SoupURI *
+ephy_embed_form_auth_get_uri (EphyEmbedFormAuth *form_auth)
+{
+  return form_auth->priv->uri;
+}
+
+guint64
+ephy_embed_form_auth_get_page_id (EphyEmbedFormAuth *form_auth)
+{
+  return form_auth->priv->page_id;
+}
diff --git a/embed/web-extension/ephy-embed-form-auth.h b/embed/web-extension/ephy-embed-form-auth.h
new file mode 100644
index 0000000..6dfa921
--- /dev/null
+++ b/embed/web-extension/ephy-embed-form-auth.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2013 Igalia S.L.
+ *
+ *  This program 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, or (at your option)
+ *  any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef EPHY_EMBED_FORM_AUTH_H
+#define EPHY_EMBED_FORM_AUTH_H
+
+#include <glib-object.h>
+#include <libsoup/soup.h>
+#include <webkit2/webkit-web-extension.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_EMBED_FORM_AUTH     (ephy_embed_form_auth_get_type ())
+#define EPHY_EMBED_FORM_AUTH(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), EPHY_TYPE_EMBED_FORM_AUTH, 
EphyEmbedFormAuth))
+#define EPHY_IS_EMBED_FORM_AUTH(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EPHY_TYPE_EMBED_FORM_AUTH))
+
+typedef struct _EphyEmbedFormAuthClass   EphyEmbedFormAuthClass;
+typedef struct _EphyEmbedFormAuth        EphyEmbedFormAuth;
+typedef struct _EphyEmbedFormAuthPrivate EphyEmbedFormAuthPrivate;
+
+struct _EphyEmbedFormAuth
+{
+  GObject parent;
+
+  EphyEmbedFormAuthPrivate *priv;
+};
+
+struct _EphyEmbedFormAuthClass
+{
+  GObjectClass parent_class;
+};
+
+GType              ephy_embed_form_auth_get_type          (void);
+EphyEmbedFormAuth *ephy_embed_form_auth_new               (WebKitWebPage     *web_page,
+                                                           WebKitDOMNode     *username_node,
+                                                           WebKitDOMNode     *password_node);
+WebKitDOMNode     *ephy_embed_form_auth_get_username_node (EphyEmbedFormAuth *form_auth);
+WebKitDOMNode     *ephy_embed_form_auth_get_password_node (EphyEmbedFormAuth *form_auth);
+SoupURI           *ephy_embed_form_auth_get_uri           (EphyEmbedFormAuth *form_auth);
+guint64            ephy_embed_form_auth_get_page_id       (EphyEmbedFormAuth *form_auth);
+
+G_END_DECLS
+
+#endif /* EPHY_EMBED_FORM_AUTH_H */
diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c
index 4291890..d47c7f8 100644
--- a/embed/web-extension/ephy-web-extension.c
+++ b/embed/web-extension/ephy-web-extension.c
@@ -22,15 +22,22 @@
 #include "ephy-web-extension.h"
 
 #include "ephy-debug.h"
+#include "ephy-embed-form-auth.h"
+#include "ephy-form-auth-data.h"
 #include "ephy-prefs.h"
 #include "ephy-settings.h"
 #include "ephy-web-dom-utils.h"
 #include "uri-tester.h"
 
 #include <gio/gio.h>
+#include <libsoup/soup.h>
 #include <webkit2/webkit-web-extension.h>
 
+
+// FIXME: These global variables should be freed somehow.
 static UriTester *uri_tester;
+static EphyFormAuthDataCache *form_auth_data_cache;
+static GDBusConnection *dbus_connection;
 
 static const char introspection_xml[] =
   "<node>"
@@ -50,10 +57,361 @@ static const char introspection_xml[] =
   "   <arg type='s' name='uri' direction='out'/>"
   "   <arg type='s' name='color' direction='out'/>"
   "  </method>"
+  "  <signal name='FormAuthDataSaveConfirmationRequired'>"
+  "   <arg type='u' name='request_id' direction='out'/>"
+  "   <arg type='t' name='page_id' direction='out'/>"
+  "   <arg type='s' name='hostname' direction='out'/>"
+  "   <arg type='s' name='username' direction='out'/>"
+  "  </signal>"
+  "  <method name='FormAuthDataSaveConfirmationResponse'>"
+  "   <arg type='u' name='request_id' direction='in'/>"
+  "   <arg type='b' name='should_store' direction='in'/>"
+  "  </method>"
   " </interface>"
   "</node>";
 
-static WebKitWebPage*
+
+static gboolean
+web_page_send_request (WebKitWebPage *web_page,
+                       WebKitURIRequest *request,
+                       WebKitURIResponse *redirected_response,
+                       gpointer user_data)
+{
+  const char *request_uri;
+  const char *page_uri;
+
+  /* FIXME: Instead of checking the setting here, connect to the signal
+   * or not depending on the setting.
+   */
+  if (!g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ENABLE_ADBLOCK))
+      return FALSE;
+
+  request_uri = webkit_uri_request_get_uri (request);
+  page_uri = webkit_web_page_get_uri (web_page);
+
+  /* Always load the main resource. */
+  if (g_strcmp0 (request_uri, page_uri) == 0)
+    return FALSE;
+
+  return uri_tester_test_uri (uri_tester, request_uri, page_uri, AD_URI_CHECK_TYPE_OTHER);
+}
+
+static GHashTable *
+get_form_auth_data_save_requests (void)
+{
+  static GHashTable *form_auth_data_save_requests = NULL;
+
+  if (!form_auth_data_save_requests) {
+    form_auth_data_save_requests =
+      g_hash_table_new_full (g_direct_hash,
+                             g_direct_equal,
+                             NULL,
+                             (GDestroyNotify)g_object_unref);
+  }
+
+  return form_auth_data_save_requests;
+}
+
+static guint
+form_auth_data_save_request_new_id (void)
+{
+  static guint form_auth_data_save_request_id = 0;
+
+  return ++form_auth_data_save_request_id;
+}
+
+static void
+store_password (EphyEmbedFormAuth *form_auth)
+{
+  SoupURI *uri;
+  char *uri_str;
+  char *username_field_name = NULL;
+  char *username_field_value = NULL;
+  char *password_field_name = NULL;
+  char *password_field_value = NULL;
+
+  g_object_get (ephy_embed_form_auth_get_username_node (form_auth),
+                "name", &username_field_name,
+                "value", &username_field_value,
+                NULL);
+  g_object_get (ephy_embed_form_auth_get_password_node (form_auth),
+                "name", &password_field_name,
+                "value", &password_field_value,
+                NULL);
+
+  uri = ephy_embed_form_auth_get_uri (form_auth);
+  uri_str = soup_uri_to_string (uri, FALSE);
+  ephy_form_auth_data_store (uri_str,
+                             username_field_name,
+                             password_field_name,
+                             username_field_value,
+                             password_field_value,
+                             NULL, NULL);
+  g_free (uri_str);
+
+  /* Update internal caching */
+  ephy_form_auth_data_cache_add (form_auth_data_cache,
+                                 uri->host,
+                                 username_field_name,
+                                 password_field_name,
+                                 username_field_value);
+
+  g_free (username_field_name);
+  g_free (username_field_value);
+  g_free (password_field_name);
+  g_free (password_field_value);
+}
+
+static void
+request_decision_on_storing (EphyEmbedFormAuth *form_auth)
+{
+  char *username_field_value = NULL;
+  guint request_id;
+  SoupURI *uri;
+  GError *error = NULL;
+
+  if (!dbus_connection) {
+    g_object_unref (form_auth);
+    return;
+  }
+
+  request_id = form_auth_data_save_request_new_id ();
+  uri = ephy_embed_form_auth_get_uri (form_auth);
+  g_object_get (ephy_embed_form_auth_get_username_node (form_auth),
+                "value", &username_field_value, NULL);
+
+  g_dbus_connection_emit_signal (dbus_connection,
+                                 NULL,
+                                 EPHY_WEB_EXTENSION_OBJECT_PATH,
+                                 EPHY_WEB_EXTENSION_INTERFACE,
+                                 "FormAuthDataSaveConfirmationRequired",
+                                 g_variant_new ("(utss)",
+                                                request_id,
+                                                ephy_embed_form_auth_get_page_id (form_auth),
+                                                uri ? uri->host : "",
+                                                username_field_value ? username_field_value : ""),
+                                 &error);
+  if (error) {
+    g_warning ("Error emitting signal FormAuthDataSaveConfirmationRequired: %s\n", error->message);
+    g_error_free (error);
+  } else {
+    g_hash_table_insert (get_form_auth_data_save_requests (),
+                         GINT_TO_POINTER (request_id),
+                         g_object_ref (form_auth));
+  }
+
+  g_free (username_field_value);
+  g_object_unref (form_auth);
+}
+
+static void
+should_store_cb (const char *username,
+                 const char *password,
+                 gpointer user_data)
+{
+  EphyEmbedFormAuth *form_auth = EPHY_EMBED_FORM_AUTH (user_data);
+
+  if (username && password) {
+    char *username_field_value = NULL;
+    char *password_field_value = NULL;
+
+    g_object_get (ephy_embed_form_auth_get_username_node (form_auth),
+                  "value", &username_field_value, NULL);
+    g_object_get (ephy_embed_form_auth_get_password_node (form_auth),
+                  "value", &password_field_value, NULL);
+
+    /* FIXME: We use only the first result, for now; We need to do
+     * something smarter here */
+    if (g_str_equal (username, username_field_value) &&
+        g_str_equal (password, password_field_value)) {
+      LOG ("User/password already stored. Not asking about storing.");
+    } else {
+      LOG ("User/password not yet stored. Asking about storing.");
+      request_decision_on_storing (g_object_ref (form_auth));
+    }
+
+    g_free (username_field_value);
+    g_free (password_field_value);
+  } else {
+    LOG ("No result on query; asking whether we should store.");
+    request_decision_on_storing (g_object_ref (form_auth));
+  }
+}
+
+static gboolean
+form_submitted_cb (WebKitDOMHTMLFormElement *dom_form,
+                   WebKitDOMEvent *dom_event,
+                   WebKitWebPage *web_page)
+{
+  EphyEmbedFormAuth *form_auth;
+  SoupURI *uri;
+  WebKitDOMNode *username_node = NULL;
+  WebKitDOMNode *password_node = NULL;
+  char *username_field_name = NULL;
+  char *password_field_name = NULL;
+  char *uri_str;
+
+  if (!ephy_web_dom_utils_find_form_auth_elements (dom_form, &username_node, &password_node))
+    return TRUE;
+
+  /* EphyEmbedFormAuth takes ownership of the nodes */
+  form_auth = ephy_embed_form_auth_new (web_page, username_node, password_node);
+  uri = ephy_embed_form_auth_get_uri (form_auth);
+  soup_uri_set_query (uri, NULL);
+
+  g_object_get (username_node, "name", &username_field_name, NULL);
+  g_object_get (password_node, "name", &password_field_name, NULL);
+  uri_str = soup_uri_to_string (uri, FALSE);
+
+  ephy_form_auth_data_query (uri_str,
+                             username_field_name,
+                             password_field_name,
+                             should_store_cb,
+                             form_auth,
+                             (GDestroyNotify)g_object_unref);
+
+  g_free (username_field_name);
+  g_free (password_field_name);
+  g_free (uri_str);
+
+  return TRUE;
+}
+
+static void
+fill_form_cb (const char *username,
+              const char *password,
+              gpointer user_data)
+{
+  EphyEmbedFormAuth *form_auth = EPHY_EMBED_FORM_AUTH (user_data);
+
+  if (username == NULL && password == NULL) {
+    LOG ("No result");
+    return;
+  }
+
+  LOG ("Found: user %s pass (hidden)", username);
+  g_object_set (ephy_embed_form_auth_get_username_node (form_auth),
+                "value", username, NULL);
+  g_object_set (ephy_embed_form_auth_get_password_node (form_auth),
+                "value", password, NULL);
+}
+
+static gint
+ephy_form_auth_data_compare (EphyFormAuthData *form_data,
+                             EphyEmbedFormAuth *form_auth)
+{
+  char *username_field_name;
+  char *password_field_name;
+  gboolean retval;
+
+  g_object_get (ephy_embed_form_auth_get_username_node (form_auth),
+                "name", &username_field_name, NULL);
+  g_object_get (ephy_embed_form_auth_get_password_node (form_auth),
+                "name", &password_field_name, NULL);
+
+  retval = g_strcmp0 (username_field_name, form_data->form_username) == 0 &&
+    g_strcmp0 (password_field_name, form_data->form_password) == 0;
+
+  g_free (username_field_name);
+  g_free (password_field_name);
+
+  return retval ? 0 : 1;
+}
+
+static void
+pre_fill_form (EphyEmbedFormAuth *form_auth)
+{
+  GSList *form_auth_data_list;
+  GSList *l;
+  EphyFormAuthData *form_data;
+  SoupURI *uri;
+  char *uri_str;
+
+  uri = ephy_embed_form_auth_get_uri (form_auth);
+  if (!uri)
+    return;
+
+  form_auth_data_list = ephy_form_auth_data_cache_get_list (form_auth_data_cache, uri->host);
+  l = g_slist_find_custom (form_auth_data_list, form_auth, (GCompareFunc)ephy_form_auth_data_compare);
+  if (!l)
+    return;
+
+  form_data = (EphyFormAuthData *)l->data;
+  uri_str = soup_uri_to_string (uri, FALSE);
+
+  ephy_form_auth_data_query (uri_str,
+                             form_data->form_username,
+                             form_data->form_password,
+                             fill_form_cb,
+                             g_object_ref (form_auth),
+                             (GDestroyNotify)g_object_unref);
+  g_free (uri_str);
+}
+
+static void
+web_page_document_loaded (WebKitWebPage *web_page,
+                          gpointer user_data)
+{
+  WebKitDOMHTMLCollection *forms = NULL;
+  WebKitDOMDocument *document = NULL;
+  gulong forms_n;
+  int i;
+
+  if (!form_auth_data_cache ||
+      !g_settings_get_boolean (EPHY_SETTINGS_MAIN, EPHY_PREFS_REMEMBER_PASSWORDS))
+    return;
+
+  document = webkit_web_page_get_dom_document (web_page);
+  forms = webkit_dom_document_get_forms (document);
+  forms_n = webkit_dom_html_collection_get_length (forms);
+
+  if (forms_n == 0) {
+    LOG ("No forms found.");
+    g_object_unref(forms);
+    return;
+  }
+
+  for (i = 0; i < forms_n; i++) {
+    WebKitDOMHTMLFormElement *form;
+    WebKitDOMNode *username_node = NULL;
+    WebKitDOMNode *password_node = NULL;
+
+    form = WEBKIT_DOM_HTML_FORM_ELEMENT (webkit_dom_html_collection_item (forms, i));
+
+    /* We have a field that may be the user, and one for a password. */
+    if (ephy_web_dom_utils_find_form_auth_elements (form, &username_node, &password_node)) {
+      EphyEmbedFormAuth *form_auth;
+
+      LOG ("Hooking and pre-filling a form");
+
+      /* EphyEmbedFormAuth takes ownership of the nodes */
+      form_auth = ephy_embed_form_auth_new (web_page, username_node, password_node);
+      webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (form), "submit",
+                                                  G_CALLBACK (form_submitted_cb), FALSE,
+                                                  web_page);
+      pre_fill_form (form_auth);
+      g_object_unref (form_auth);
+    } else
+      LOG ("No pre-fillable/hookable form found");
+  }
+
+  g_object_unref(forms);
+}
+
+static void
+web_page_created_callback (WebKitWebExtension *extension,
+                           WebKitWebPage *web_page,
+                           gpointer user_data)
+{
+  g_signal_connect_object (web_page, "send-request",
+                           G_CALLBACK (web_page_send_request),
+                           NULL, 0);
+  g_signal_connect_object (web_page, "document-loaded",
+                           G_CALLBACK (web_page_document_loaded),
+                           NULL, 0);
+}
+
+static WebKitWebPage *
 get_webkit_web_page_or_return_dbus_error (GDBusMethodInvocation *invocation,
                                           WebKitWebExtension *web_extension,
                                           guint64 page_id)
@@ -136,6 +494,21 @@ handle_method_call (GDBusConnection *connection,
 
     g_dbus_method_invocation_return_value (invocation,
                                            g_variant_new ("(bss)", result, uri ? uri : "", color ? color : 
""));
+  } else if (g_strcmp0 (method_name, "FormAuthDataSaveConfirmationResponse") == 0) {
+    EphyEmbedFormAuth *form_auth;
+    guint request_id;
+    gboolean should_store;
+    GHashTable *requests = get_form_auth_data_save_requests ();
+
+    g_variant_get (parameters, "(ub)", &request_id, &should_store);
+
+    form_auth = g_hash_table_lookup (requests, GINT_TO_POINTER (request_id));
+    if (!form_auth)
+      return;
+
+    if (should_store)
+      store_password (form_auth);
+    g_hash_table_remove (requests, GINT_TO_POINTER (request_id));
   }
 
 }
@@ -154,6 +527,7 @@ bus_acquired_cb (GDBusConnection *connection,
   guint registration_id;
   GError *error = NULL;
   static GDBusNodeInfo *introspection_data = NULL;
+
   if (!introspection_data)
     introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
 
@@ -167,44 +541,12 @@ bus_acquired_cb (GDBusConnection *connection,
   if (!registration_id) {
     g_warning ("Failed to register object: %s\n", error->message);
     g_error_free (error);
+  } else {
+    dbus_connection = connection;
+    g_object_add_weak_pointer (G_OBJECT (connection), (gpointer *)&dbus_connection);
   }
 }
 
-static gboolean
-web_page_send_request (WebKitWebPage *web_page,
-                       WebKitURIRequest *request,
-                       WebKitURIResponse *redirected_response,
-                       gpointer user_data)
-{
-  const char *request_uri;
-  const char *page_uri;
-
-  /* FIXME: Instead of checking the setting here, connect to the signal
-   * or not depending on the setting.
-   */
-  if (!g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ENABLE_ADBLOCK))
-      return FALSE;
-
-  request_uri = webkit_uri_request_get_uri (request);
-  page_uri = webkit_web_page_get_uri (web_page);
-
-  /* Always load the main resource. */
-  if (g_strcmp0 (request_uri, page_uri) == 0)
-    return FALSE;
-
-  return uri_tester_test_uri (uri_tester, request_uri, page_uri, AD_URI_CHECK_TYPE_OTHER);
-}
-
-static void
-web_page_created_callback (WebKitWebExtension *extension,
-                           WebKitWebPage *web_page,
-                           gpointer user_data)
-{
-  g_signal_connect_object (web_page, "send-request",
-                           G_CALLBACK (web_page_send_request),
-                           NULL, 0);
-}
-
 G_MODULE_EXPORT void
 webkit_web_extension_initialize (WebKitWebExtension *extension)
 {
@@ -212,6 +554,8 @@ webkit_web_extension_initialize (WebKitWebExtension *extension)
 
   ephy_debug_init ();
   uri_tester = uri_tester_new (g_getenv ("EPHY_DOT_DIR"));
+  if (!g_getenv ("EPHY_PRIVATE_PROFILE"))
+    form_auth_data_cache = ephy_form_auth_data_cache_new ();
 
   g_signal_connect (extension, "page-created",
                     G_CALLBACK (web_page_created_callback),
diff --git a/src/ephy-main.c b/src/ephy-main.c
index 83cace3..a789917 100644
--- a/src/ephy-main.c
+++ b/src/ephy-main.c
@@ -466,6 +466,8 @@ main (int argc,
   pid_str = g_strdup_printf ("%u", getpid ());
   g_setenv ("EPHY_WEB_EXTENSION_ID", pid_str, TRUE);
   g_setenv ("EPHY_DOT_DIR", ephy_dot_dir (), TRUE);
+  if (private_instance || incognito_mode)
+    g_setenv ("EPHY_PRIVATE_PROFILE", "1", TRUE);
   g_free (pid_str);
 
   /* Set the web extensions dir ASAP before the process is launched */


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