| Paste number 92095: | new patch |
| Pasted by: | kov |
| When: | 2 years, 1 month ago |
| Share: | Tweet this! | http://paste.lisp.org/+1Z27 |
| Channel: | #webkit-gtk |
| Paste contents: |
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index e045a1d..e023f78 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -26,6 +26,8 @@
#include <gtk/gtk.h>
#include <string.h>
#include <webkit/webkit.h>
+#include <JavaScriptCore/JavaScript.h>
+#include <gnome-keyring.h>
#include "eel-gconf-extensions.h"
#include "ephy-debug.h"
@@ -54,6 +56,11 @@ static void ephy_web_view_init (EphyWebView *gs);
#define RELOAD_DELAY_MAX_TICKS 40 /* RELOAD_DELAY * RELOAD_DELAY_MAX_TICKS = 10 s */
#define EMPTY_PAGE _("Blank page") /* Title for the empty page */
+typedef struct {
+ char *form_username;
+ char *form_password;
+} FormAuthData;
+
struct _EphyWebViewPrivate {
EphyWebViewSecurityLevel security_level;
EphyWebViewDocumentType document_type;
@@ -84,6 +91,7 @@ struct _EphyWebViewPrivate {
GSList *hidden_popups;
GSList *shown_popups;
+ GHashTable *form_auth_data;
};
typedef struct {
@@ -503,6 +511,401 @@ ephy_web_view_dispose (GObject *object)
G_OBJECT_CLASS (ephy_web_view_parent_class)->dispose (object);
}
+
+static char*
+js_value_to_string (JSContextRef js_context,
+ JSValueRef js_value)
+{
+ gssize length;
+ char* buffer;
+ JSStringRef str = JSValueToStringCopy (js_context, js_value, NULL);
+ g_return_val_if_fail (str != NULL, NULL);
+
+ length = JSStringGetLength (str) + 1;
+
+ buffer = g_malloc0 (length);
+ JSStringGetUTF8CString (str, buffer, length);
+ JSStringRelease (str);
+
+ return buffer;
+}
+
+static JSObjectRef
+js_object_get_property_as_object (JSContextRef js_context,
+ JSObjectRef object,
+ const char *attr)
+{
+ JSStringRef attrstr = JSStringCreateWithUTF8CString (attr);
+ JSValueRef val = JSObjectGetProperty (js_context, object, attrstr, NULL);
+
+ JSStringRelease (attrstr);
+ return JSValueToObject (js_context, val, NULL);
+}
+
+static char *
+js_get_element_attribute (JSContextRef js_context,
+ JSObjectRef object,
+ const char *attr)
+{
+ JSStringRef attrstr = JSStringCreateWithUTF8CString (attr);
+ JSObjectRef ga = js_object_get_property_as_object (js_context, object, "getAttribute");
+ JSValueRef args[1], val;
+ char *buffer = NULL;
+
+ args[0] = JSValueMakeString (js_context, attrstr);
+ val = JSObjectCallAsFunction (js_context, ga, object, 1, args, NULL);
+ JSStringRelease (attrstr);
+
+ if (JSValueIsString (js_context, val)) {
+ buffer = js_value_to_string (js_context, val);
+ }
+
+ return buffer;
+}
+
+static GSList*
+js_get_all_forms (JSContextRef js_context)
+{
+ JSObjectRef js_global;
+ JSObjectRef js_object;
+ JSValueRef js_form;
+ guint index = 0;
+ GSList *retval = NULL;
+
+ js_global = JSContextGetGlobalObject (js_context);
+
+ js_object = js_object_get_property_as_object (js_context, js_global, "document");
+ if (!js_object)
+ return NULL;
+
+ js_object = js_object_get_property_as_object (js_context, js_object, "forms");
+ if (!js_object)
+ return NULL;
+
+ js_form = JSObjectGetPropertyAtIndex (js_context, js_object, index++, NULL);
+ while (!JSValueIsUndefined (js_context, js_form)) {
+ retval = g_slist_prepend (retval, (gpointer)js_form);
+ js_form = JSObjectGetPropertyAtIndex (js_context, js_object, index++, NULL);
+ }
+
+ return retval;
+}
+
+static GSList*
+js_get_form_elements (JSContextRef js_context, JSValueRef js_form)
+{
+ JSObjectRef js_object = JSValueToObject (js_context, js_form, NULL);
+ JSStringRef js_name;
+ JSValueRef value;
+ guint num;
+ guint count;
+ GSList *retval = NULL;
+
+ js_object = js_object_get_property_as_object (js_context, js_object, "elements");
+ if (!js_object)
+ return NULL;
+
+ js_name = JSStringCreateWithUTF8CString ("length");
+ value = JSObjectGetProperty (js_context, js_object, js_name, NULL);
+ JSStringRelease (js_name);
+
+ num = (guint)JSValueToNumber (js_context, value, NULL);
+ for (count = 0; count < num; count++) {
+ value = JSObjectGetPropertyAtIndex (js_context, js_object, count, NULL);
+
+ if (!JSValueIsObject (js_context, value))
+ continue;
+
+ retval = g_slist_prepend (retval, (gpointer)value);
+ }
+
+ return retval;
+}
+
+typedef struct {
+ JSContextRef contxext;
+ JSObjectRef object;
+} FillData;
+
+static void
+find_username_and_password_elements (JSContextRef js_context,
+ GSList *elements,
+ JSObjectRef *name_element,
+ JSObjectRef *password_element,
+ gboolean auto_fill,
+ EphyWebView *view)
+{
+ GSList *iter = elements;
+ GSList *l = NULL;
+ SoupURI *uri = NULL;
+
+ g_debug ("URI: %s", webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view)));
+ if (auto_fill) {
+ uri = soup_uri_new (webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view)));
+ if (uri->host)
+ l = g_hash_table_lookup (view->priv->form_auth_data, uri->host);
+ }
+
+ for (; iter; iter = iter->next) {
+ JSObjectRef object;
+ char *type;
+
+ object = JSValueToObject (js_context, (JSValueRef)iter->data, NULL);
+
+ type = js_get_element_attribute (js_context, object, "type");
+ if (!type)
+ continue;
+
+ if (g_str_equal (type, "text")) {
+ /* We found more than one inputs of type text; we won't be
+ * saving here */
+ if (*name_element) {
+ *name_element = NULL;
+ break;
+ }
+
+ *name_element = object;
+ } else if (g_str_equal (type, "password")) {
+ if (*password_element) {
+ *password_element = NULL;
+ break;
+ }
+
+ *password_element = object;
+ }
+
+ if (auto_fill) {
+ GSList *p;
+
+ for (p = l; p; p = p->next) {
+ FormAuthData *data = (FormAuthData*)p->data;
+ if (!g_strcmp0 (js_get_element_attribute (js_context, object, "name"), data->form_username) ||
+ !g_strcmp0 (js_get_element_attribute (js_context, object, "name"), data->form_password)) {
+ char *key_str;
+ GList *results = NULL;
+ JSValueRef prop_value;
+ JSStringRef prop_value_str, prop_name;
+ SoupURI *key = soup_uri_copy (uri);
+ soup_uri_set_query_from_fields (key,
+ "form_username",
+ data->form_username,
+ "form_password",
+ data->form_password,
+ NULL);
+ key_str = soup_uri_to_string (key, FALSE);
+
+#if 0
+ data = g_slice_new (FillData);
+ data->context = js_context;
+ data->object = object;
+#endif
+
+ g_debug ("QUERY %s", key_str);
+ gnome_keyring_find_network_password_sync (NULL,
+ NULL,
+ key_str,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ &results);
+
+ if (!results) {
+ g_debug ("NO RESULTS");
+ return;
+ }
+
+ prop_name = JSStringCreateWithUTF8CString ("value");
+ g_debug ("USER %s PASS %s", ((GnomeKeyringNetworkPasswordData*)results->data)->user, ((GnomeKeyringNetworkPasswordData*)results->data)->password);
+
+ if (!g_strcmp0 (js_get_element_attribute (js_context, object, "name"), data->form_username)) {
+ prop_value_str = JSStringCreateWithUTF8CString (((GnomeKeyringNetworkPasswordData*)results->data)->user);
+ } else {
+ prop_value_str = JSStringCreateWithUTF8CString (((GnomeKeyringNetworkPasswordData*)results->data)->password);
+ }
+
+ prop_value = JSValueMakeString (js_context, prop_value_str);
+ JSObjectSetProperty (js_context, object, prop_name, prop_value, 0, NULL);
+ g_free (key_str);
+ soup_uri_free (key);
+ }
+ }
+ }
+
+ g_free (type);
+ }
+
+ if (auto_fill)
+ soup_uri_free (uri);
+}
+
+static JSValueRef
+do_form_submitted_cb (JSContextRef js_context,
+ JSObjectRef js_function,
+ JSObjectRef js_this,
+ size_t argument_count,
+ const JSValueRef js_arguments[],
+ JSValueRef* js_exception)
+{
+ GSList *elements = js_get_form_elements (js_context, js_this);
+ JSObjectRef name_element = NULL;
+ JSObjectRef password_element = NULL;
+ JSStringRef js_string;
+ JSValueRef js_value;
+ char *name_field_name;
+ char *name_field_value;
+ char *password_field_name;
+ char *password_field_value;
+
+ find_username_and_password_elements (js_context, elements, &name_element, &password_element, FALSE, NULL);
+ g_slist_free (elements);
+
+ if (!name_element || !password_element)
+ return JSValueMakeUndefined (js_context);
+
+ name_field_name = js_get_element_attribute (js_context, name_element, "name");
+ password_field_name = js_get_element_attribute (js_context, password_element, "name");
+
+ js_string = JSStringCreateWithUTF8CString ("value");
+ js_value = JSObjectGetProperty (js_context, name_element, js_string, NULL);
+
+ name_field_value = js_value_to_string (js_context, js_value);
+
+ js_value = JSObjectGetProperty (js_context, password_element, js_string, NULL);
+ JSStringRelease (js_string);
+
+ password_field_value = js_value_to_string (js_context, js_value);
+
+ {
+ SoupURI *fake_uri;
+ char *fake_uri_str;
+ guint32 item_id;
+
+ /* FIXME: replace with call to function Xan is finishing */
+ g_debug ("%s / %s | %s / %s", name_field_name, name_field_value, password_field_name, password_field_value);
+
+ fake_uri = soup_uri_new ("http://twitter.com/");
+ /* Store the form login and password names encoded in the
+ * URL. A bit of an abuse of keyring, but oh well */
+ soup_uri_set_query_from_fields (fake_uri,
+ "form_username",
+ name_field_name,
+ "form_password",
+ password_field_name,
+ NULL);
+ fake_uri_str = soup_uri_to_string (fake_uri, FALSE);
+ gnome_keyring_set_network_password_sync (NULL,
+ name_field_value,
+ NULL,
+ fake_uri_str,
+ NULL,
+ fake_uri->scheme,
+ NULL,
+ fake_uri->port,
+ password_field_value,
+ &item_id);
+ soup_uri_free (fake_uri);
+ g_free (fake_uri_str);
+ }
+
+ g_free (name_field_name);
+ g_free (name_field_value);
+ g_free (password_field_name);
+ g_free (password_field_value);
+
+ return JSValueMakeUndefined (js_context);
+}
+
+static void
+hook_form (JSContextRef js_context, JSValueRef js_form, JSObjectRef form_submitted_cb)
+{
+ JSObjectRef object = JSValueToObject (js_context, js_form, NULL);
+ JSObjectRef add_event_listener = js_object_get_property_as_object (js_context, object, "addEventListener");
+ JSStringRef event_name;
+ JSValueRef args[3], val;
+ JSValueRef js_exception;
+
+ event_name = JSStringCreateWithUTF8CString ("submit");
+ args[0] = JSValueMakeString (js_context, event_name);
+ JSStringRelease (event_name);
+
+ args[1] = form_submitted_cb;
+ args[2] = JSValueMakeBoolean (js_context, TRUE);
+ val = JSObjectCallAsFunction (js_context, add_event_listener, object, 3, args, &js_exception);
+}
+
+static void
+do_hook_into_forms (JSContextRef js_context, JSObjectRef form_submitted_cb, EphyWebView *web_view)
+{
+ GSList *forms;
+
+ for (forms = js_get_all_forms (js_context); forms; forms = forms->next) {
+ JSValueRef form = (JSValueRef)forms->data;
+ GSList *elements = js_get_form_elements (js_context, form);
+ JSObjectRef name_element = NULL;
+ JSObjectRef password_element = NULL;
+
+ find_username_and_password_elements (js_context, elements, &name_element, &password_element, TRUE, web_view);
+
+ g_slist_free (elements);
+
+ /* We have a field that may be the user, and one for a password. */
+ if (name_element && password_element)
+ hook_form (js_context, form, form_submitted_cb);
+ }
+
+ g_slist_free (forms);
+}
+
+static void
+_ephy_web_view_hook_into_forms (EphyWebView *web_view)
+{
+ WebKitWebFrame *web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+ JSGlobalContextRef js_context;
+ JSObjectRef js_global;
+ JSStringRef js_function_name;
+ JSObjectRef js_function_object;
+
+ js_context = webkit_web_frame_get_global_context (web_frame);
+ js_global = JSContextGetGlobalObject (js_context);
+
+ js_function_name = JSStringCreateWithUTF8CString ("_EpiphanyInternalFormSubmitted");
+ js_function_object = JSObjectMakeFunctionWithCallback (js_context,
+ js_function_name,
+ (JSObjectCallAsFunctionCallback)do_form_submitted_cb);
+ JSObjectSetProperty (js_context, js_global, js_function_name, js_function_object, 0, NULL);
+ JSStringRelease (js_function_name);
+
+ do_hook_into_forms (js_context, js_function_object, EPHY_WEB_VIEW (web_view));
+}
+
+static void
+form_auth_data_free (FormAuthData *data)
+{
+ g_free (data->form_username);
+ g_free (data->form_password);
+
+ g_slice_free (FormAuthData, data);
+}
+
+static void
+free_form_auth_data_list (gpointer data)
+{
+ GSList *p, *l = (GSList*)data;
+
+ for (p = l; p; p = p->next)
+ form_auth_data_free ((FormAuthData*)p->data);
+
+ g_slist_free (l);
+}
+
+static void
+remove_form_auth_data (gpointer key, gpointer value, gpointer user_data)
+{
+ if (value)
+ free_form_auth_data_list ((GSList*)value);
+}
+
static void
ephy_web_view_finalize (GObject *object)
{
@@ -518,6 +921,13 @@ ephy_web_view_finalize (GObject *object)
priv->non_search_regex = NULL;
}
+ if (priv->form_auth_data) {
+ g_hash_table_foreach (priv->form_auth_data,
+ (GHFunc)remove_form_auth_data,
+ NULL);
+ g_hash_table_destroy (priv->form_auth_data);
+ }
+
ephy_web_view_popups_manager_reset (EPHY_WEB_VIEW (object));
g_free (priv->address);
@@ -1058,6 +1468,115 @@ mime_type_policy_decision_requested_cb (WebKitWebView *web_view,
}
static void
+load_status_cb (WebKitWebView *web_view,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ WebKitLoadStatus status = webkit_web_view_get_load_status (web_view);
+
+ if (status == WEBKIT_LOAD_FINISHED) {
+ if (!eel_gconf_get_boolean (CONF_PRIVACY_REMEMBER_PASSWORDS))
+ return;
+
+ _ephy_web_view_hook_into_forms (EPHY_WEB_VIEW (web_view));
+ }
+}
+
+static FormAuthData*
+form_auth_data_new (const char *form_username,
+ const char *form_password)
+{
+ FormAuthData *data;
+
+ data = g_slice_new (FormAuthData);
+ data->form_username = g_strdup (form_username);
+ data->form_password = g_strdup (form_password);
+
+ return data;
+}
+
+static void
+get_attr_cb (GnomeKeyringResult result,
+ GnomeKeyringAttributeList *attributes,
+ EphyWebView *view)
+{
+ int i = 0;
+ GnomeKeyringAttribute *attribute;
+ EphyWebViewPrivate *priv = view->priv;
+ char *server = NULL;
+
+ if (result != GNOME_KEYRING_RESULT_OK)
+ return;
+
+ attribute = (GnomeKeyringAttribute*)attributes->data;
+ for (i = 0; i < attributes->len; i++) {
+ if (attribute[i].type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) {
+ if (g_str_equal (attribute[i].name, "server")) {
+ server = g_strdup (attribute[i].value.string);
+ break;
+ }
+ }
+ }
+
+ if (server &&
+ g_strstr_len (server, -1, "form%5Fusername") &&
+ g_strstr_len (server, -1, "form%5Fpassword")) {
+ /* This is a stored login/password from a form, cache the form
+ * names locally so we don't need to hit the keyring daemon all
+ * the time */
+ const char *form_username, *form_password;
+ GHashTable *t;
+ GSList *l;
+ FormAuthData *form_data;
+ SoupURI *uri = soup_uri_new (server);
+ t = soup_form_decode (uri->query);
+ form_username = g_hash_table_lookup (t, "form_username");
+ form_password = g_hash_table_lookup (t, "form_password");
+
+ form_data = form_auth_data_new (form_username, form_password);
+ l = g_hash_table_lookup (priv->form_auth_data,
+ uri->host);
+ g_debug ("APPENDING %s = %s:%s", uri->host, form_data->form_username, form_data->form_password);
+ l = g_slist_append (l, form_data);
+ g_hash_table_replace (priv->form_auth_data,
+ g_strdup (uri->host),
+ l);
+
+ soup_uri_free (uri);
+ g_hash_table_destroy (t);
+ }
+
+ g_free (server);
+}
+
+static void
+store_form_data_cb (GnomeKeyringResult result, GList *l, EphyWebView *view)
+{
+ GList *p;
+
+ if (result != GNOME_KEYRING_RESULT_OK)
+ return;
+
+ for (p = l; p; p = p->next) {
+ guint key_id = GPOINTER_TO_UINT (p->data);
+ gnome_keyring_item_get_attributes (GNOME_KEYRING_DEFAULT,
+ key_id,
+ (GnomeKeyringOperationGetAttributesCallback) get_attr_cb,
+ view,
+ NULL);
+ }
+}
+
+static void
+cache_keyring_form_data (EphyWebView *view)
+{
+ gnome_keyring_list_item_ids (GNOME_KEYRING_DEFAULT,
+ (GnomeKeyringOperationGetListCallback)store_form_data_cb,
+ view,
+ NULL);
+}
+
+static void
ephy_web_view_init (EphyWebView *web_view)
{
EphyWebViewPrivate *priv;
@@ -1088,6 +1607,16 @@ ephy_web_view_init (EphyWebView *web_view)
G_CALLBACK (mime_type_policy_decision_requested_cb),
NULL);
+ g_signal_connect (web_view, "notify::load-status",
+ G_CALLBACK (load_status_cb),
+ NULL);
+
+ priv->form_auth_data = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ cache_keyring_form_data (web_view);
+
g_signal_connect_object (web_view, "icon-loaded",
G_CALLBACK (favicon_cb),
web_view, (GConnectFlags)0);
@@ -2462,3 +2991,5 @@ ephy_web_view_save (EphyWebView *view, const char *uri)
ephy_web_view_save_sub_resources (view, uri, subresources);
}
+
+
diff --git a/embed/ephy-web-view.h b/embed/ephy-web-view.h
index 5ed358b..31a41ae 100644
--- a/embed/ephy-web-view.h
+++ b/embed/ephy-web-view.h
@@ -221,6 +221,8 @@ void ephy_web_view_popups_manager_reset (EphyWebView
void ephy_web_view_save (EphyWebView *view,
const char *uri);
+void ephy_web_view_hook_into_forms (EphyWebView *web_view);
+
G_END_DECLS
#endif
diff --git a/src/ephy-profile-migration.c b/src/ephy-profile-migration.c
index c0c319d..ee558d1 100644
--- a/src/ephy-profile-migration.c
+++ b/src/ephy-profile-migration.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/*
* Copyright © 2009 Xan López
*
@@ -47,7 +48,7 @@
* - Add your function at the end of the 'migrators' array
*/
-#define PROFILE_MIGRATION_VERSION 3
+#define PROFILE_MIGRATION_VERSION 4
typedef void (*EphyProfileMigrator) (void);
@@ -147,7 +148,8 @@ decrypt (const char *data)
}
static void
-parse_and_decrypt_signons (const char *signons)
+parse_and_decrypt_signons (const char *signons,
+ gboolean handle_forms)
{
int version;
gchar **lines;
@@ -248,16 +250,26 @@ parse_and_decrypt_signons (const char *signons)
while (begin + 4 < end) {
char *username = NULL;
char *password = NULL;
+ char *form_username = NULL;
+ char *form_password = NULL;
guint32 item_id;
/* The username */
- begin++; /* Skip username element */
+ if (handle_forms) {
+ form_username = g_strdup (lines[begin++]);
+ } else {
+ begin++; /* Skip username element */
+ }
username = decrypt (lines[begin++]);
/* The password */
/* The element name has a leading '*' */
if (lines[begin][0] == '*') {
- begin++; /* Skip password element */
+ if (handle_forms) {
+ form_password = g_strdup (lines[begin++]);
+ } else {
+ begin++; /* Skip password element */
+ }
password = decrypt (lines[begin++]);
} else {
/* Maybe the file is broken, skip to the next block */
@@ -278,7 +290,35 @@ parse_and_decrypt_signons (const char *signons)
begin++;
}
- if (username && password)
+ if (handle_forms && username && password &&
+ !g_str_equal (form_username, "") &&
+ !g_str_equal (form_password, "*")) {
+ SoupURI *fake_uri;
+ char *fake_uri_str;
+
+ fake_uri = soup_uri_copy (uri);
+ /* Store the form login and password names encoded in the
+ * URL. A bit of an abuse of keyring, but oh well */
+ soup_uri_set_query_from_fields (fake_uri,
+ FORM_USERNAME_KEY,
+ form_username,
+ FORM_PASSWORD_KEY,
+ form_password,
+ NULL);
+ fake_uri_str = soup_uri_to_string (fake_uri, FALSE);
+ gnome_keyring_set_network_password_sync (NULL,
+ username,
+ realm,
+ fake_uri_str,
+ NULL,
+ fake_uri->scheme,
+ NULL,
+ fake_uri->port,
+ password,
+ &item_id);
+ soup_uri_free (fake_uri);
+ g_free (fake_uri_str);
+ } else if (!handle_forms && username && password) {
gnome_keyring_set_network_password_sync (NULL,
username,
realm,
@@ -289,8 +329,12 @@ parse_and_decrypt_signons (const char *signons)
uri->port,
password,
&item_id);
+ }
+
g_free (username);
g_free (password);
+ g_free (form_username);
+ g_free (form_password);
}
soup_uri_free (uri);
@@ -331,7 +375,7 @@ migrate_passwords ()
g_free (dest);
}
- parse_and_decrypt_signons (contents);
+ parse_and_decrypt_signons (contents, FALSE);
/* Save the contents in a backup directory for future data
extraction when we support more features */
@@ -350,12 +394,45 @@ migrate_passwords ()
#endif
}
+static void
+migrate_passwords2 ()
+{
+#ifdef ENABLE_NSS
+ char *dest, *contents;
+ gsize length;
+ GError *error = NULL;
+
+ dest = g_build_filename (ephy_dot_dir (),
+ "gecko-passwords.txt",
+ NULL);
+ if (!g_file_test (dest, G_FILE_TEST_EXISTS)) {
+ g_free (dest);
+ return;
+ }
+
+ if (!ephy_nss_glue_init ())
+ return;
+
+ if (!g_file_get_contents (dest, &contents, &length, &error)) {
+ g_free (dest);
+ }
+
+ parse_and_decrypt_signons (contents, TRUE);
+ g_free (contents);
+
+ ephy_nss_glue_close ();
+#endif
+}
+
const EphyProfileMigrator migrators[] = {
migrate_cookies,
migrate_passwords,
/* Yes, again! Version 2 had some bugs, so we need to run
migrate_passwords again to possibly migrate more passwords*/
- migrate_passwords
+ migrate_passwords,
+ /* Very similar to migrate_passwords, but this migrates
+ * login/passwords for page forms, which we previously ignored */
+ migrate_passwords2
};
#define PROFILE_MIGRATION_FILE ".migrated"
diff --git a/src/ephy-profile-migration.h b/src/ephy-profile-migration.h
index 51114a8..21b6bd2 100644
--- a/src/ephy-profile-migration.h
+++ b/src/ephy-profile-migration.h
@@ -20,6 +20,9 @@
#ifndef EPHY_PROFILE_MIGRATION_H
#define EPHY_PROFILE_MIGRATION_H
+#define FORM_USERNAME_KEY "form_username"
+#define FORM_PASSWORD_KEY "form_password"
+
void _ephy_profile_migrate (void);
#endif
This paste has no annotations.