diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2013-03-14 16:48:26 +0800 |
---|---|---|
committer | Carlos Garcia Campos <carlosgc@gnome.org> | 2013-03-15 03:48:38 +0800 |
commit | 3b5a81725a5fea3f90c028e6d1f831584baaa586 (patch) | |
tree | cf919810444fa5161853cc86693d145069ff37a2 | |
parent | 070338dcd6fc15990326a9e74087a2c0fa1bc1c4 (diff) | |
download | gsoc2013-epiphany-3b5a81725a5fea3f90c028e6d1f831584baaa586.tar gsoc2013-epiphany-3b5a81725a5fea3f90c028e6d1f831584baaa586.tar.gz gsoc2013-epiphany-3b5a81725a5fea3f90c028e6d1f831584baaa586.tar.bz2 gsoc2013-epiphany-3b5a81725a5fea3f90c028e6d1f831584baaa586.tar.lz gsoc2013-epiphany-3b5a81725a5fea3f90c028e6d1f831584baaa586.tar.xz gsoc2013-epiphany-3b5a81725a5fea3f90c028e6d1f831584baaa586.tar.zst gsoc2013-epiphany-3b5a81725a5fea3f90c028e6d1f831584baaa586.zip |
web-extension: Implement pre-filled forms in WebKit2
https://bugzilla.gnome.org/show_bug.cgi?id=684439
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | embed/ephy-embed-shell.c | 67 | ||||
-rw-r--r-- | embed/ephy-web-view.c | 66 | ||||
-rw-r--r-- | embed/web-extension/Makefile.am | 6 | ||||
-rw-r--r-- | embed/web-extension/ephy-embed-form-auth.c | 104 | ||||
-rw-r--r-- | embed/web-extension/ephy-embed-form-auth.h | 61 | ||||
-rw-r--r-- | embed/web-extension/ephy-web-extension.c | 416 | ||||
-rw-r--r-- | src/ephy-main.c | 2 |
8 files changed, 675 insertions, 52 deletions
diff --git a/configure.ac b/configure.ac index 07297166e..a9518b9b7 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 42ba8ed90..b775075fc 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 423c35e17..7bbd67439 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 f6fb6036f..4fc539cc2 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 000000000..9eda22064 --- /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 000000000..6dfa92124 --- /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 42918909c..d47c7f860 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 83cace3de..a78991790 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 */ |