[epiphany] web-extension: Use the new JavaScriptCore GLib API instead of DOM API
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany] web-extension: Use the new JavaScriptCore GLib API instead of DOM API
- Date: Thu, 19 Apr 2018 08:11:35 +0000 (UTC)
commit 85f6037a8d6785b6b2a5a4bfd83ea595d35aeb52
Author: Carlos Garcia Campos <cgarcia igalia com>
Date: Mon Mar 12 17:21:45 2018 +0100
web-extension: Use the new JavaScriptCore GLib API instead of DOM API
https://bugzilla.gnome.org/show_bug.cgi?id=794395
embed/ephy-embed-shell.c | 12 +-
embed/ephy-embed-utils.c | 34 +-
embed/ephy-embed-utils.h | 2 -
embed/ephy-web-extension-proxy.c | 186 ---
embed/ephy-web-extension-proxy.h | 27 -
embed/ephy-web-view.c | 134 ++-
embed/web-extension/ephy-embed-form-auth.c | 152 ---
embed/web-extension/ephy-embed-form-auth.h | 51 -
embed/web-extension/ephy-web-dom-utils.c | 804 ------------
embed/web-extension/ephy-web-dom-utils.h | 61 -
embed/web-extension/ephy-web-extension.c | 1293 +++++---------------
embed/web-extension/meson.build | 12 +-
.../resources/epiphany-web-extension.gresource.xml | 6 +
embed/web-extension/resources/js/ephy.js | 602 +++++++++
lib/ephy-permissions-manager.c | 61 +
lib/ephy-permissions-manager.h | 5 +
lib/sync/ephy-password-manager.c | 149 +++
lib/sync/ephy-password-manager.h | 4 +
meson.build | 2 +-
src/prefs-dialog.c | 2 +-
20 files changed, 1201 insertions(+), 2398 deletions(-)
---
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 65ef092..fb3767f 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -203,7 +203,7 @@ web_extension_form_auth_data_message_received_cb (WebKitUserContentManager *mana
GVariant *variant;
gchar *message_str;
- message_str = ephy_embed_utils_get_js_result_as_string (message);
+ message_str = jsc_value_to_string (webkit_javascript_result_get_js_value (message));
variant = g_variant_parse (G_VARIANT_TYPE ("(utss)"), message_str, NULL, NULL, NULL);
g_free (message_str);
@@ -223,7 +223,7 @@ web_extension_sensitive_form_focused_message_received_cb (WebKitUserContentManag
GVariant *variant;
char *message_str;
- message_str = ephy_embed_utils_get_js_result_as_string (message);
+ message_str = jsc_value_to_string (webkit_javascript_result_get_js_value (message));
variant = g_variant_parse (G_VARIANT_TYPE ("(tb)"), message_str, NULL, NULL, NULL);
g_free (message_str);
@@ -317,7 +317,7 @@ web_extension_overview_message_received_cb (WebKitUserContentManager *manager,
EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
char *url_to_remove;
- url_to_remove = ephy_embed_utils_get_js_result_as_string (message);
+ url_to_remove = jsc_value_to_string (webkit_javascript_result_get_js_value (message));
priv->hiding_overview_item++;
ephy_history_service_set_url_hidden (priv->global_history_service,
@@ -341,7 +341,7 @@ web_extension_tls_error_page_message_received_cb (WebKitUserContentManager *mana
{
guint64 page_id;
- page_id = ephy_embed_utils_get_js_result_as_number (message);
+ page_id = jsc_value_to_double (webkit_javascript_result_get_js_value (message));
g_signal_emit (shell, signals[ALLOW_TLS_CERTIFICATE], 0, page_id);
}
@@ -352,7 +352,7 @@ web_extension_unsafe_browsing_error_page_message_received_cb (WebKitUserContentM
{
guint64 page_id;
- page_id = ephy_embed_utils_get_js_result_as_number (message);
+ page_id = jsc_value_to_double (webkit_javascript_result_get_js_value (message));
g_signal_emit (shell, signals[ALLOW_UNSAFE_BROWSING], 0, page_id);
}
@@ -363,7 +363,7 @@ web_extension_about_apps_message_received_cb (WebKitUserContentManager *manager,
{
char *app_id;
- app_id = ephy_embed_utils_get_js_result_as_string (message);
+ app_id = jsc_value_to_string (webkit_javascript_result_get_js_value (message));
ephy_web_application_delete (app_id);
g_free (app_id);
}
diff --git a/embed/ephy-embed-utils.c b/embed/ephy-embed-utils.c
index 5a20b9e..ea2e0db 100644
--- a/embed/ephy-embed-utils.c
+++ b/embed/ephy-embed-utils.c
@@ -28,8 +28,8 @@
#include "ephy-settings.h"
#include "ephy-string.h"
-#include <JavaScriptCore/JavaScript.h>
#include <glib/gi18n.h>
+#include <jsc/jsc.h>
#include <libsoup/soup.h>
#include <string.h>
@@ -370,38 +370,6 @@ ephy_embed_utils_urls_have_same_origin (const char *a_url,
return retval;
}
-char *
-ephy_embed_utils_get_js_result_as_string (WebKitJavascriptResult *js_result)
-{
- JSValueRef js_value;
- JSStringRef js_string;
- size_t max_size;
- char *retval = NULL;
-
- js_value = webkit_javascript_result_get_value (js_result);
- js_string = JSValueToStringCopy (webkit_javascript_result_get_global_context (js_result),
- js_value, NULL);
- max_size = JSStringGetMaximumUTF8CStringSize (js_string);
- if (max_size) {
- retval = g_malloc (max_size);
- JSStringGetUTF8CString (js_string, retval, max_size);
- }
- JSStringRelease (js_string);
-
- return retval;
-}
-
-double
-ephy_embed_utils_get_js_result_as_number (WebKitJavascriptResult *js_result)
-{
- JSValueRef js_value;
-
- js_value = webkit_javascript_result_get_value (js_result);
-
- return JSValueToNumber (webkit_javascript_result_get_global_context (js_result),
- js_value, NULL);
-}
-
void
ephy_embed_utils_shutdown (void)
{
diff --git a/embed/ephy-embed-utils.h b/embed/ephy-embed-utils.h
index 3224876..3f99b7d 100644
--- a/embed/ephy-embed-utils.h
+++ b/embed/ephy-embed-utils.h
@@ -48,8 +48,6 @@ gboolean ephy_embed_utils_is_no_show_address (const char *add
char *ephy_embed_utils_get_title_from_address (const char *address);
gboolean ephy_embed_utils_urls_have_same_origin (const char *a_url,
const char *b_url);
-char *ephy_embed_utils_get_js_result_as_string (WebKitJavascriptResult *js_result);
-double ephy_embed_utils_get_js_result_as_number (WebKitJavascriptResult *js_result);
void ephy_embed_utils_shutdown (void);
G_END_DECLS
diff --git a/embed/ephy-web-extension-proxy.c b/embed/ephy-web-extension-proxy.c
index be0cf97..7d2dc4e 100644
--- a/embed/ephy-web-extension-proxy.c
+++ b/embed/ephy-web-extension-proxy.c
@@ -207,192 +207,6 @@ ephy_web_extension_proxy_form_auth_data_save_confirmation_response (EphyWebExten
NULL, NULL);
}
-static void
-has_modified_forms_cb (GDBusProxy *proxy,
- GAsyncResult *result,
- GTask *task)
-{
- GVariant *return_value;
- gboolean retval = FALSE;
-
- return_value = g_dbus_proxy_call_finish (proxy, result, NULL);
- if (return_value) {
- g_variant_get (return_value, "(b)", &retval);
- g_variant_unref (return_value);
- }
-
- g_task_return_boolean (task, retval);
- g_object_unref (task);
-}
-
-void
-ephy_web_extension_proxy_web_page_has_modified_forms (EphyWebExtensionProxy *web_extension,
- guint64 page_id,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GTask *task;
-
- g_assert (EPHY_IS_WEB_EXTENSION_PROXY (web_extension));
-
- task = g_task_new (web_extension, cancellable, callback, user_data);
-
- if (web_extension->proxy) {
- g_dbus_proxy_call (web_extension->proxy,
- "HasModifiedForms",
- g_variant_new ("(t)", page_id),
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- web_extension->cancellable,
- (GAsyncReadyCallback)has_modified_forms_cb,
- g_object_ref (task));
- } else {
- g_task_return_boolean (task, FALSE);
- }
-
- g_object_unref (task);
-}
-
-gboolean
-ephy_web_extension_proxy_web_page_has_modified_forms_finish (EphyWebExtensionProxy *web_extension,
- GAsyncResult *result,
- GError **error)
-{
- g_assert (g_task_is_valid (result, web_extension));
-
- return g_task_propagate_boolean (G_TASK (result), error);
-}
-
-static void
-get_best_web_app_icon_cb (GDBusProxy *proxy,
- GAsyncResult *result,
- GTask *task)
-{
- GVariant *retval;
- GError *error = NULL;
-
- retval = g_dbus_proxy_call_finish (proxy, result, &error);
- if (!retval) {
- g_task_return_error (task, error);
- } else {
- g_task_return_pointer (task, retval, (GDestroyNotify)g_variant_unref);
- }
- g_object_unref (task);
-}
-
-void
-ephy_web_extension_proxy_get_best_web_app_icon (EphyWebExtensionProxy *web_extension,
- guint64 page_id,
- const char *base_uri,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GTask *task;
-
- g_assert (EPHY_IS_WEB_EXTENSION_PROXY (web_extension));
-
- task = g_task_new (web_extension, cancellable, callback, user_data);
-
- if (web_extension->proxy) {
- g_dbus_proxy_call (web_extension->proxy,
- "GetBestWebAppIcon",
- g_variant_new ("(ts)", page_id, base_uri),
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- web_extension->cancellable,
- (GAsyncReadyCallback)get_best_web_app_icon_cb,
- g_object_ref (task));
- } else {
- g_task_return_boolean (task, FALSE);
- }
-
- g_object_unref (task);
-}
-
-gboolean
-ephy_web_extension_proxy_get_best_web_app_icon_finish (EphyWebExtensionProxy *web_extension,
- GAsyncResult *result,
- char **icon_uri,
- char **icon_color,
- GError **error)
-{
- GVariant *variant;
- GTask *task = G_TASK (result);
-
- g_assert (g_task_is_valid (result, web_extension));
-
- variant = g_task_propagate_pointer (task, error);
- if (!variant)
- return FALSE;
-
- g_variant_get (variant, "(ss)", icon_uri, icon_color);
- g_variant_unref (variant);
-
- return TRUE;
-}
-
-static void
-get_web_app_title_cb (GDBusProxy *proxy,
- GAsyncResult *result,
- GTask *task)
-{
- GVariant *retval;
- GError *error = NULL;
-
- retval = g_dbus_proxy_call_finish (proxy, result, &error);
- if (!retval) {
- g_task_return_error (task, error);
- } else {
- char *title;
-
- g_variant_get (retval, "(s)", &title);
- g_task_return_pointer (task, title, (GDestroyNotify)g_free);
- g_variant_unref (retval);
- }
- g_object_unref (task);
-}
-
-void
-ephy_web_extension_proxy_get_web_app_title (EphyWebExtensionProxy *web_extension,
- guint64 page_id,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GTask *task;
-
- g_assert (EPHY_IS_WEB_EXTENSION_PROXY (web_extension));
-
- task = g_task_new (web_extension, cancellable, callback, user_data);
-
- if (web_extension->proxy) {
- g_dbus_proxy_call (web_extension->proxy,
- "GetWebAppTitle",
- g_variant_new ("(t)", page_id),
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- web_extension->cancellable,
- (GAsyncReadyCallback)get_web_app_title_cb,
- g_object_ref (task));
- } else {
- g_task_return_pointer (task, NULL, NULL);
- }
-
- g_object_unref (task);
-}
-
-char *
-ephy_web_extension_proxy_get_web_app_title_finish (EphyWebExtensionProxy *web_extension,
- GAsyncResult *result,
- GError **error)
-{
- g_assert (g_task_is_valid (result, web_extension));
-
- return g_task_propagate_pointer (G_TASK (result), error);
-}
-
void
ephy_web_extension_proxy_history_set_urls (EphyWebExtensionProxy *web_extension,
GList *urls)
diff --git a/embed/ephy-web-extension-proxy.h b/embed/ephy-web-extension-proxy.h
index 131cb8e..1802246 100644
--- a/embed/ephy-web-extension-proxy.h
+++ b/embed/ephy-web-extension-proxy.h
@@ -32,33 +32,6 @@ EphyWebExtensionProxy *ephy_web_extension_proxy_new
void ephy_web_extension_proxy_form_auth_data_save_confirmation_response
(EphyWebExtensionProxy *web_extension,
guint
request_id,
gboolean
response);
-void ephy_web_extension_proxy_web_page_has_modified_forms
(EphyWebExtensionProxy *web_extension,
- guint64
page_id,
- GCancellable
*cancellable,
-
GAsyncReadyCallback callback,
- gpointer
user_data);
-gboolean ephy_web_extension_proxy_web_page_has_modified_forms_finish
(EphyWebExtensionProxy *web_extension,
- GAsyncResult
*result,
- GError
**error);
-void ephy_web_extension_proxy_get_best_web_app_icon
(EphyWebExtensionProxy *web_extension,
- guint64
page_id,
- const char
*base_uri,
- GCancellable
*cancellable,
-
GAsyncReadyCallback callback,
- gpointer
user_data);
-gboolean ephy_web_extension_proxy_get_best_web_app_icon_finish
(EphyWebExtensionProxy *web_extension,
- GAsyncResult
*result,
- char
**icon_uri,
- char
**icon_color,
- GError
**error);
-void ephy_web_extension_proxy_get_web_app_title
(EphyWebExtensionProxy *web_extension,
- guint64
page_id,
- GCancellable
*cancellable,
-
GAsyncReadyCallback callback,
- gpointer
user_data);
-char *ephy_web_extension_proxy_get_web_app_title_finish
(EphyWebExtensionProxy *web_extension,
- GAsyncResult
*result,
- GError
**error);
void ephy_web_extension_proxy_history_set_urls
(EphyWebExtensionProxy *web_extension,
GList
*urls);
void ephy_web_extension_proxy_history_set_url_thumbnail
(EphyWebExtensionProxy *web_extension,
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index 0e114bb..dc73b4f 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -3093,13 +3093,19 @@ ephy_web_view_set_should_bypass_safe_browsing (EphyWebView *view,
}
static void
-has_modified_forms_cb (EphyWebExtensionProxy *web_extension,
- GAsyncResult *result,
- GTask *task)
+has_modified_forms_cb (WebKitWebView *view,
+ GAsyncResult *result,
+ GTask *task)
{
- gboolean retval;
+ WebKitJavascriptResult *js_result;
+ gboolean retval = FALSE;
+
+ js_result = webkit_web_view_run_javascript_finish (view, result, NULL);
+ if (js_result) {
+ retval = jsc_value_to_boolean (webkit_javascript_result_get_js_value (js_result));
+ webkit_javascript_result_unref (js_result);
+ }
- retval = ephy_web_extension_proxy_web_page_has_modified_forms_finish (web_extension, result, NULL);
g_task_return_boolean (task, retval);
g_object_unref (task);
}
@@ -3129,18 +3135,11 @@ ephy_web_view_has_modified_forms (EphyWebView *view,
g_assert (EPHY_IS_WEB_VIEW (view));
task = g_task_new (view, cancellable, callback, user_data);
-
- if (view->web_extension) {
- ephy_web_extension_proxy_web_page_has_modified_forms (view->web_extension,
- webkit_web_view_get_page_id (WEBKIT_WEB_VIEW
(view)),
- cancellable,
- (GAsyncReadyCallback)has_modified_forms_cb,
- g_object_ref (task));
- } else {
- g_task_return_boolean (task, FALSE);
- }
-
- g_object_unref (task);
+ webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (view),
+ "Ephy.hasModifiedForms();",
+ cancellable,
+ (GAsyncReadyCallback)has_modified_forms_cb,
+ task);
}
gboolean
@@ -3168,22 +3167,35 @@ get_best_web_app_icon_async_data_free (GetBestWebAppIconAsyncData *data)
}
static void
-get_best_web_app_icon_cb (EphyWebExtensionProxy *web_extension,
- GAsyncResult *result,
- GTask *task)
+get_best_web_app_icon_cb (WebKitWebView *view,
+ GAsyncResult *result,
+ GTask *task)
{
- char *uri = NULL;
- char *color = NULL;
+ WebKitJavascriptResult *js_result;
GError *error = NULL;
- if (!ephy_web_extension_proxy_get_best_web_app_icon_finish (web_extension, result, &uri, &color, &error)) {
- g_task_return_error (task, error);
- } else {
- GetBestWebAppIconAsyncData *data = g_slice_new0 (GetBestWebAppIconAsyncData);
- data->icon_uri = uri;
- data->icon_color = color;
+ js_result = webkit_web_view_run_javascript_finish (view, result, &error);
+ if (js_result) {
+ JSCValue *js_value, *js_uri, *js_color;
+ GetBestWebAppIconAsyncData *data;
+
+ data = g_slice_new0 (GetBestWebAppIconAsyncData);
+ js_value = webkit_javascript_result_get_js_value (js_result);
+ g_assert (jsc_value_is_object (js_value));
+
+ js_uri = jsc_value_object_get_property (js_value, "url");
+ data->icon_uri = jsc_value_to_string (js_uri);
+ g_object_unref (js_uri);
+
+ js_color = jsc_value_object_get_property (js_value, "icon");
+ data->icon_color = jsc_value_is_null (js_color) || jsc_value_is_undefined (js_color) ? NULL :
jsc_value_to_string (js_color);
+ g_object_unref (js_color);
+
g_task_return_pointer (task, data, (GDestroyNotify)get_best_web_app_icon_async_data_free);
- }
+ webkit_javascript_result_unref (js_result);
+ } else
+ g_task_return_error (task, error);
+
g_object_unref (task);
}
@@ -3193,24 +3205,21 @@ ephy_web_view_get_best_web_app_icon (EphyWebView *view,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ WebKitWebView *wk_view;
GTask *task;
+ char *script;
g_assert (EPHY_IS_WEB_VIEW (view));
+ wk_view = WEBKIT_WEB_VIEW (view);
task = g_task_new (view, cancellable, callback, user_data);
-
- if (view->web_extension) {
- ephy_web_extension_proxy_get_best_web_app_icon (view->web_extension,
- webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view)),
- webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view)),
- cancellable,
- (GAsyncReadyCallback)get_best_web_app_icon_cb,
- g_object_ref (task));
- } else {
- g_task_return_boolean (task, FALSE);
- }
-
- g_object_unref (task);
+ script = g_strdup_printf ("Ephy.getWebAppIcon(\"%s\");", webkit_web_view_get_uri (wk_view));
+ webkit_web_view_run_javascript (wk_view,
+ script,
+ cancellable,
+ (GAsyncReadyCallback)get_best_web_app_icon_cb,
+ task);
+ g_free (script);
}
gboolean
@@ -3243,18 +3252,26 @@ ephy_web_view_get_best_web_app_icon_finish (EphyWebView *view,
}
static void
-get_web_app_title_cb (EphyWebExtensionProxy *web_extension,
- GAsyncResult *result,
- GTask *task)
+get_web_app_title_cb (WebKitWebView *view,
+ GAsyncResult *result,
+ GTask *task)
{
- char *retval;
+ WebKitJavascriptResult *js_result;
GError *error = NULL;
- retval = ephy_web_extension_proxy_get_web_app_title_finish (web_extension, result, &error);
- if (!retval)
- g_task_return_error (task, error);
- else
+ js_result = webkit_web_view_run_javascript_finish (view, result, &error);
+ if (js_result) {
+ JSCValue *js_value;
+ char *retval = NULL;
+
+ js_value = webkit_javascript_result_get_js_value (js_result);
+ if (!jsc_value_is_null (js_value) && !jsc_value_is_undefined (js_value))
+ retval = jsc_value_to_string (js_value);
g_task_return_pointer (task, retval, (GDestroyNotify)g_free);
+ webkit_javascript_result_unref (js_result);
+ } else
+ g_task_return_error (task, error);
+
g_object_unref (task);
}
@@ -3269,18 +3286,11 @@ ephy_web_view_get_web_app_title (EphyWebView *view,
g_assert (EPHY_IS_WEB_VIEW (view));
task = g_task_new (view, cancellable, callback, user_data);
-
- if (view->web_extension) {
- ephy_web_extension_proxy_get_web_app_title (view->web_extension,
- webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view)),
- cancellable,
- (GAsyncReadyCallback)get_web_app_title_cb,
- g_object_ref (task));
- } else {
- g_task_return_pointer (task, NULL, NULL);
- }
-
- g_object_unref (task);
+ webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (view),
+ "Ephy.getWebAppTitle();",
+ cancellable,
+ (GAsyncReadyCallback)get_web_app_title_cb,
+ task);
}
char *
diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c
index 018c989..3987cd6 100644
--- a/embed/web-extension/ephy-web-extension.c
+++ b/embed/web-extension/ephy-web-extension.c
@@ -24,7 +24,6 @@
#include "ephy-dbus-names.h"
#include "ephy-dbus-util.h"
#include "ephy-debug.h"
-#include "ephy-embed-form-auth.h"
#include "ephy-file-helpers.h"
#include "ephy-password-manager.h"
#include "ephy-permissions-manager.h"
@@ -34,11 +33,11 @@
#include "ephy-sync-utils.h"
#include "ephy-uri-helpers.h"
#include "ephy-uri-tester.h"
-#include "ephy-web-dom-utils.h"
#include "ephy-web-overview.h"
#include <gio/gio.h>
#include <gtk/gtk.h>
+#include <jsc/jsc.h>
#include <libsoup/soup.h>
#include <string.h>
#include <webkit2/webkit-web-extension.h>
@@ -67,20 +66,6 @@ static const char introspection_xml[] =
" <signal name='PageCreated'>"
" <arg type='t' name='page_id' direction='out'/>"
" </signal>"
- " <method name='HasModifiedForms'>"
- " <arg type='t' name='page_id' direction='in'/>"
- " <arg type='b' name='has_modified_forms' direction='out'/>"
- " </method>"
- " <method name='GetWebAppTitle'>"
- " <arg type='t' name='page_id' direction='in'/>"
- " <arg type='s' name='title' direction='out'/>"
- " </method>"
- " <method name='GetBestWebAppIcon'>"
- " <arg type='t' name='page_id' direction='in'/>"
- " <arg type='s' name='base_uri' direction='in'/>"
- " <arg type='s' name='uri' direction='out'/>"
- " <arg type='s' name='color' direction='out'/>"
- " </method>"
" <method name='FormAuthDataSaveConfirmationResponse'>"
" <arg type='u' name='request_id' direction='in'/>"
" <arg type='b' name='should_store' direction='in'/>"
@@ -199,6 +184,52 @@ web_page_send_request (WebKitWebPage *web_page,
return FALSE;
}
+typedef struct {
+ char *origin;
+ char *target_origin;
+ char *username;
+ char *password;
+ char *username_field_name;
+ char *password_field_name;
+ gboolean is_new;
+} SaveAuthRequest;
+
+static SaveAuthRequest *
+save_auth_request_new (const char *origin,
+ const char *target_origin,
+ const char *username,
+ const char *password,
+ const char *username_field_name,
+ const char *password_field_name,
+ gboolean is_new)
+{
+ SaveAuthRequest *request;
+
+ request = g_new (SaveAuthRequest, 1);
+ request->origin = g_strdup (origin);
+ request->target_origin = g_strdup (target_origin);
+ request->username = g_strdup (username);
+ request->password = g_strdup (password);
+ request->username_field_name = g_strdup (username_field_name);
+ request->password_field_name = g_strdup (password_field_name);
+ request->is_new = is_new;
+
+ return request;
+}
+
+static void
+save_auth_request_free (SaveAuthRequest *request)
+{
+ g_free (request->origin);
+ g_free (request->target_origin);
+ g_free (request->username);
+ g_free (request->password);
+ g_free (request->username_field_name);
+ g_free (request->password_field_name);
+
+ g_free (request);
+}
+
static GHashTable *
ephy_web_extension_get_form_auth_data_save_requests (EphyWebExtension *extension)
{
@@ -207,7 +238,7 @@ ephy_web_extension_get_form_auth_data_save_requests (EphyWebExtension *extension
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
- (GDestroyNotify)g_object_unref);
+ (GDestroyNotify)save_auth_request_free);
}
return extension->form_auth_data_save_requests;
@@ -221,266 +252,36 @@ form_auth_data_save_request_new_id (void)
return ++form_auth_data_save_request_id;
}
-static void
-store_password (EphyEmbedFormAuth *form_auth)
-{
- SoupURI *uri;
- char *uri_str;
- char *origin;
- char *username_field_name = NULL;
- char *password_field_name = NULL;
- const char *username = NULL;
- const char *password = NULL;
- const char *target_origin;
- gboolean password_updated;
- WebKitDOMNode *username_node;
- EphyWebExtension *extension = ephy_web_extension_get ();
-
- g_assert (extension->password_manager);
-
- username_node = ephy_embed_form_auth_get_username_node (form_auth);
- if (username_node) {
- g_object_get (username_node,
- "name", &username_field_name,
- NULL);
- username = ephy_embed_form_auth_get_username (form_auth);
- }
- g_object_get (ephy_embed_form_auth_get_password_node (form_auth),
- "name", &password_field_name,
- NULL);
- password = ephy_embed_form_auth_get_password (form_auth);
-
- uri = ephy_embed_form_auth_get_uri (form_auth);
- uri_str = soup_uri_to_string (uri, FALSE);
- origin = ephy_uri_to_security_origin (uri_str);
- target_origin = ephy_embed_form_auth_get_target_origin (form_auth);
- password_updated = ephy_embed_form_auth_get_password_updated (form_auth);
- ephy_password_manager_save (extension->password_manager,
- origin,
- target_origin,
- username,
- password,
- username_field_name,
- password_field_name,
- !password_updated);
-
- g_free (uri_str);
- g_free (origin);
- g_free (username_field_name);
- g_free (password_field_name);
-}
-
-static void
-request_decision_on_storing (EphyEmbedFormAuth *form_auth)
+static char *
+save_auth_requester (guint64 page_id,
+ const char *origin,
+ const char *target_origin,
+ const char *username,
+ const char *password,
+ const char *username_field_name,
+ const char *password_field_name,
+ gboolean is_new)
{
- char *username_field_value = NULL;
- guint request_id;
- SoupURI *uri;
- WebKitDOMNode *username_node;
- WebKitDOMDOMWindow *dom_window = NULL;
GVariant *variant;
- char *message = NULL;
- char *uri_string = NULL;
- char *origin = NULL;
-
- dom_window = webkit_dom_document_get_default_view (ephy_embed_form_auth_get_owner_document (form_auth));
- if (dom_window == NULL)
- goto out;
-
- uri = ephy_embed_form_auth_get_uri (form_auth);
- if (uri == NULL)
- goto out;
-
- uri_string = soup_uri_to_string (uri, FALSE);
- origin = ephy_uri_to_security_origin (uri_string);
- if (origin == NULL)
- goto out;
+ guint request_id;
+ char *retval;
request_id = form_auth_data_save_request_new_id ();
-
- username_node = ephy_embed_form_auth_get_username_node (form_auth);
- if (username_node)
- g_object_get (username_node, "value", &username_field_value, NULL);
-
variant = g_variant_new ("(utss)",
request_id,
- ephy_embed_form_auth_get_page_id (form_auth),
+ page_id,
origin,
- username_field_value ? username_field_value : "");
- g_free (username_field_value);
+ username ? username : "");
- message = g_variant_print (variant, FALSE);
+ retval = g_variant_print (variant, FALSE);
g_variant_unref (variant);
- if (webkit_dom_dom_window_webkit_message_handlers_post_message (dom_window, "formAuthData", message)) {
- g_hash_table_insert (ephy_web_extension_get_form_auth_data_save_requests (ephy_web_extension_get ()),
- GINT_TO_POINTER (request_id),
- g_object_ref (form_auth));
- } else {
- g_warning ("Error sending formAuthData message");
- }
-
-out:
- if (dom_window != NULL)
- g_object_unref (dom_window);
- if (form_auth != NULL)
- g_object_unref (form_auth);
- if (message != NULL)
- g_free (message);
- if (uri_string != NULL)
- g_free (uri_string);
- if (origin != NULL)
- g_free (origin);
-}
-
-static void
-should_store_cb (GList *records,
- gpointer user_data)
-{
- EphyEmbedFormAuth *form_auth = EPHY_EMBED_FORM_AUTH (user_data);
- EphyWebExtension *web_extension;
- EphyPermission permission;
- SoupURI *uri;
- char *uri_string;
- const char *password;
- char *origin = NULL;
-
- uri = ephy_embed_form_auth_get_uri (form_auth);
- uri_string = soup_uri_to_string (uri, FALSE);
- if (uri_string == NULL)
- return;
- origin = ephy_uri_to_security_origin (uri_string);
- if (origin == NULL)
- goto out;
-
- web_extension = ephy_web_extension_get ();
- permission = ephy_permissions_manager_get_permission (web_extension->permissions_manager,
- EPHY_PERMISSION_TYPE_SAVE_PASSWORD,
- origin);
-
- if (permission == EPHY_PERMISSION_DENY) {
- LOG ("User/password storage permission previously denied. Not asking about storing.");
- goto out;
- }
-
- /* We never ask the user in web applications. */
- if (permission == EPHY_PERMISSION_UNDECIDED && ephy_dot_dir_is_web_application ())
- permission = EPHY_PERMISSION_PERMIT;
-
- password = ephy_embed_form_auth_get_password (form_auth);
- if (password == NULL || strlen (password) == 0)
- goto out;
-
- if (records && records->data) {
- EphyPasswordRecord *record = EPHY_PASSWORD_RECORD (records->data);
- const char *stored_username = ephy_password_record_get_username (record);
- const char *stored_password = ephy_password_record_get_password (record);
- const char *username = ephy_embed_form_auth_get_username (form_auth);
-
- /* FIXME: We use only the first result, for now; We need to do
- * something smarter here */
- if (g_strcmp0 (stored_username, username) == 0 &&
- g_strcmp0 (stored_password, password) == 0) {
- LOG ("User/password already stored. Not asking about storing.");
- } else if (permission == EPHY_PERMISSION_PERMIT) {
- LOG ("User/password not yet stored. Storing.");
- ephy_embed_form_auth_set_password_updated (form_auth, TRUE);
- store_password (form_auth);
- } else {
- LOG ("User/password not yet stored. Asking about storing.");
- ephy_embed_form_auth_set_password_updated (form_auth, TRUE);
- request_decision_on_storing (g_object_ref (form_auth));
- }
-
- } else {
- LOG ("No result on query; asking whether we should store.");
- ephy_embed_form_auth_set_password_updated (form_auth, FALSE);
- request_decision_on_storing (g_object_ref (form_auth));
- }
-
-out:
- if (origin != NULL)
- g_free (origin);
- g_free (uri_string);
- g_object_unref (form_auth);
- g_list_free_full (records, g_object_unref);
-}
-
-static void
-handle_form_submission (WebKitWebPage *web_page,
- WebKitDOMHTMLFormElement *dom_form)
-{
- EphyWebExtension *extension = ephy_web_extension_get ();
- EphyEmbedFormAuth *form_auth;
- SoupURI *uri;
- char *target_origin;
- WebKitDOMNode *username_node = NULL;
- WebKitDOMNode *password_node = NULL;
- char *username_field_name = NULL;
- char *username_field_value = NULL;
- char *password_field_name = NULL;
- char *password_field_value = NULL;
- char *uri_str;
- char *origin;
- char *form_action;
+ g_hash_table_insert (ephy_web_extension_get_form_auth_data_save_requests (ephy_web_extension_get ()),
+ GINT_TO_POINTER (request_id), save_auth_request_new (origin, target_origin, username,
+ password, username_field_name,
+ password_field_name, is_new));
- if (!extension->password_manager)
- return;
-
- if (!ephy_web_dom_utils_find_form_auth_elements (dom_form,
- &username_node,
- &password_node,
- AUTH_CACHE_SUBMIT))
- return;
-
- if (username_node) {
- g_object_get (username_node,
- "value", &username_field_value,
- NULL);
- }
- g_object_get (password_node,
- "value", &password_field_value,
- NULL);
-
- form_action = webkit_dom_html_form_element_get_action (dom_form);
- if (form_action == NULL)
- form_action = g_strdup (webkit_web_page_get_uri (web_page));
- target_origin = ephy_uri_to_security_origin (form_action);
-
- /* EphyEmbedFormAuth takes ownership of the nodes */
- form_auth = ephy_embed_form_auth_new (web_page,
- target_origin,
- username_node,
- password_node,
- username_field_value,
- password_field_value);
- uri = ephy_embed_form_auth_get_uri (form_auth);
- soup_uri_set_query (uri, NULL);
-
- if (username_node)
- 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);
- origin = ephy_uri_to_security_origin (uri_str);
-
- ephy_password_manager_query (extension->password_manager,
- NULL,
- origin,
- target_origin,
- username_field_value,
- username_field_name,
- password_field_name,
- should_store_cb,
- form_auth);
-
- g_free (form_action);
- g_free (target_origin);
- g_free (username_field_name);
- g_free (username_field_value);
- g_free (password_field_name);
- g_free (password_field_value);
- g_free (uri_str);
- g_free (origin);
+ return retval;
}
static void
@@ -493,596 +294,57 @@ web_page_will_submit_form (WebKitWebPage *web_page,
GPtrArray *text_field_values)
{
gboolean form_submit_handled;
+ JSCContext *js_context;
+ JSCValue *js_ephy;
+ JSCValue *js_form;
+ JSCValue *js_requester;
+ JSCValue *js_result;
form_submit_handled =
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dom_form),
"ephy-form-submit-handled"));
-
- if (!form_submit_handled) {
- g_object_set_data (G_OBJECT (dom_form),
- "ephy-form-submit-handled",
- GINT_TO_POINTER (TRUE));
- handle_form_submission (web_page, dom_form);
- }
-}
-
-static void
-fill_form_cb (GList *records,
- gpointer user_data)
-{
- EphyEmbedFormAuth *form_auth = EPHY_EMBED_FORM_AUTH (user_data);
- WebKitDOMHTMLInputElement *username_node;
- WebKitDOMHTMLInputElement *password_node;
- const char *username;
- const char *password;
-
- if (!(records && records->data)) {
- LOG ("No result");
- return;
- }
-
- username = ephy_password_record_get_username (EPHY_PASSWORD_RECORD (records->data));
- password = ephy_password_record_get_password (EPHY_PASSWORD_RECORD (records->data));
- username_node = WEBKIT_DOM_HTML_INPUT_ELEMENT (ephy_embed_form_auth_get_username_node (form_auth));
- password_node = WEBKIT_DOM_HTML_INPUT_ELEMENT (ephy_embed_form_auth_get_password_node (form_auth));
-
- LOG ("Found: user %s pass (hidden)", username_node ? username : "(none)");
- if (username_node) {
- g_object_set_data (G_OBJECT (username_node), "ephy-is-auto-filling", GINT_TO_POINTER (TRUE));
- webkit_dom_html_input_element_set_auto_filled (username_node, TRUE);
- webkit_dom_html_input_element_set_editing_value (username_node, username);
- g_object_set_data (G_OBJECT (username_node), "ephy-is-auto-filling", GINT_TO_POINTER (FALSE));
- }
- webkit_dom_html_input_element_set_auto_filled (password_node, TRUE);
- webkit_dom_html_input_element_set_editing_value (password_node, password);
-
- g_list_free_full (records, g_object_unref);
-}
-
-static void
-pre_fill_form (EphyEmbedFormAuth *form_auth)
-{
- SoupURI *uri;
- char *uri_str;
- char *origin;
- char *username = NULL;
- char *username_field_name = NULL;
- char *password_field_name = NULL;
- const char *target_origin;
- WebKitDOMNode *username_node;
- WebKitDOMNode *password_node;
- EphyWebExtension *extension;
-
- uri = ephy_embed_form_auth_get_uri (form_auth);
- if (!uri)
- return;
-
- extension = ephy_web_extension_get ();
- if (!extension->password_manager)
+ if (form_submit_handled)
return;
- username_node = ephy_embed_form_auth_get_username_node (form_auth);
- if (username_node) {
- g_object_get (username_node, "name", &username_field_name, NULL);
- g_object_get (username_node, "value", &username, NULL);
- }
- password_node = ephy_embed_form_auth_get_password_node (form_auth);
- if (password_node)
- g_object_get (password_node, "name", &password_field_name, NULL);
-
- /* The username node is empty, so pre-fill with the default. */
- if (!g_strcmp0 (username, ""))
- g_clear_pointer (&username, g_free);
-
- uri_str = soup_uri_to_string (uri, FALSE);
- origin = ephy_uri_to_security_origin (uri_str);
- target_origin = ephy_embed_form_auth_get_target_origin (form_auth);
-
- ephy_password_manager_query (extension->password_manager,
- NULL,
- origin,
- target_origin,
- username,
- username_field_name,
- password_field_name,
- fill_form_cb,
- form_auth);
-
- g_free (uri_str);
- g_free (origin);
- g_free (username);
- g_free (username_field_name);
- g_free (password_field_name);
-}
-
-static void
-remove_user_choices (WebKitDOMDocument *document)
-{
- WebKitDOMHTMLElement *body;
- WebKitDOMElement *user_choices;
-
- body = webkit_dom_document_get_body (document);
-
- user_choices = webkit_dom_document_get_element_by_id (document, "ephy-user-choices-container");
- if (user_choices) {
- webkit_dom_node_remove_child (WEBKIT_DOM_NODE (body),
- WEBKIT_DOM_NODE (user_choices),
- NULL);
- }
-}
-
-static gboolean
-user_chosen_cb (WebKitDOMNode *li,
- WebKitDOMEvent *dom_event,
- WebKitDOMNode *username_node)
-{
- WebKitDOMElement *anchor;
- const char *username;
-
- anchor = webkit_dom_element_get_first_element_child (WEBKIT_DOM_ELEMENT (li));
-
- username = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (anchor));
- webkit_dom_html_input_element_set_value (WEBKIT_DOM_HTML_INPUT_ELEMENT (username_node), username);
-
- remove_user_choices (webkit_dom_node_get_owner_document (li));
-
- return TRUE;
-}
-
-GtkStyleContext *global_entry_context = NULL;
-static GtkStyleContext *
-get_entry_style_context (void)
-{
- GtkWidgetPath *path;
-
- if (global_entry_context)
- return global_entry_context;
-
- path = gtk_widget_path_new ();
- gtk_widget_path_append_type (path, GTK_TYPE_ENTRY);
- gtk_widget_path_iter_set_object_name (path, 0, "entry");
-
- global_entry_context = gtk_style_context_new ();
- gtk_style_context_set_path (global_entry_context, path);
- gtk_widget_path_free (path);
-
- return global_entry_context;
-}
-
-static char *
-get_selected_bgcolor (void)
-{
- GdkRGBA color;
- gtk_style_context_set_state (get_entry_style_context (),
- GTK_STATE_FLAG_SELECTED);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- gtk_style_context_get_background_color (get_entry_style_context (),
- GTK_STATE_FLAG_SELECTED,
- &color);
-#pragma GCC diagnostic pop
- return gdk_rgba_to_string (&color);
-}
-
-static char *
-get_selected_fgcolor (void)
-{
- GdkRGBA color;
- gtk_style_context_set_state (get_entry_style_context (),
- GTK_STATE_FLAG_SELECTED);
- gtk_style_context_get_color (get_entry_style_context (),
- GTK_STATE_FLAG_SELECTED,
- &color);
- return gdk_rgba_to_string (&color);
-}
-
-static char *
-get_bgcolor (void)
-{
- GdkRGBA color;
- gtk_style_context_set_state (get_entry_style_context (),
- GTK_STATE_FLAG_NORMAL);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- gtk_style_context_get_background_color (get_entry_style_context (),
- GTK_STATE_FLAG_NORMAL,
- &color);
-#pragma GCC diagnostic pop
- return gdk_rgba_to_string (&color);
-}
-
-static char *
-get_fgcolor (void)
-{
- GdkRGBA color;
- gtk_style_context_set_state (get_entry_style_context (),
- GTK_STATE_FLAG_NORMAL);
- gtk_style_context_get_color (get_entry_style_context (),
- GTK_STATE_FLAG_NORMAL,
- &color);
- return gdk_rgba_to_string (&color);
-}
-
-static char *
-get_user_choice_style (gboolean selected)
-{
- char *style_attribute;
- char *color;
-
-
- color = selected ? get_selected_bgcolor () : get_bgcolor ();
-
- style_attribute = g_strdup_printf ("list-style-type: none ! important;"
- "background-image: none ! important;"
- "padding: 3px 6px ! important;"
- "margin: 0px;"
- "background-color: %s;", color);
-
- g_free (color);
-
- return style_attribute;
+ g_object_set_data (G_OBJECT (dom_form),
+ "ephy-form-submit-handled",
+ GINT_TO_POINTER (TRUE));
+
+ js_context = webkit_frame_get_js_context (source_frame);
+ js_ephy = jsc_context_get_value (js_context, "Ephy");
+ js_form = webkit_frame_get_js_value_for_dom_object (frame, WEBKIT_DOM_OBJECT (dom_form));
+ js_requester = jsc_value_new_function (js_context,
+ "saveAuthRequester",
+ G_CALLBACK (save_auth_requester), NULL, NULL,
+ G_TYPE_STRING, 8,
+ G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_BOOLEAN);
+ js_result = jsc_value_object_invoke_method (js_ephy,
+ "handleFormSubmission",
+ G_TYPE_UINT64, webkit_web_page_get_id (web_page),
+ JSC_TYPE_VALUE, js_form,
+ JSC_TYPE_VALUE, js_requester,
+ G_TYPE_NONE);
+ g_object_unref (js_result);
+ g_object_unref (js_requester);
+ g_object_unref (js_form);
+ g_object_unref (js_ephy);
+ g_object_unref (js_context);
}
static char *
-get_user_choice_anchor_style (gboolean selected)
-{
- char *style_attribute;
- char *color;
-
- color = selected ? get_selected_fgcolor () : get_fgcolor ();
-
- style_attribute = g_strdup_printf ("font-weight: normal ! important;"
- "font-family: sans ! important;"
- "text-decoration: none ! important;"
- "-webkit-user-modify: read-only ! important;"
- "color: %s;", color);
-
- g_free (color);
-
- return style_attribute;
-}
-
-static void
-show_user_choices (WebKitDOMDocument *document,
- WebKitDOMNode *username_node)
-{
- WebKitDOMNode *body;
- WebKitDOMElement *main_div;
- WebKitDOMElement *ul;
- GList *cached_users;
- gboolean username_node_ever_edited;
- double x, y;
- double input_width;
- char *style_attribute;
- char *username;
-
- g_object_get (username_node,
- "value", &username,
- NULL);
-
- input_width = webkit_dom_element_get_offset_width (WEBKIT_DOM_ELEMENT (username_node));
-
- main_div = webkit_dom_document_create_element (document, "div", NULL);
- webkit_dom_element_set_attribute (main_div, "id", "ephy-user-choices-container", NULL);
-
- ephy_web_dom_utils_get_absolute_bottom_for_element (WEBKIT_DOM_ELEMENT (username_node), &x, &y);
-
- /* 2147483647 is the maximum value browsers will take for z-index.
- * See http://stackoverflow.com/questions/8565821/css-max-z-index-value
- */
- style_attribute = g_strdup_printf ("position: absolute; z-index: 2147483647;"
- "cursor: default;"
- "width: %lfpx;"
- "background-color: white;"
- "box-shadow: 5px 5px 5px black;"
- "border-top: 0;"
- "border-radius: 8px;"
- "-webkit-user-modify: read-only ! important;"
- "left: %lfpx; top: %lfpx;",
- input_width, x, y);
-
- webkit_dom_element_set_attribute (main_div, "style", style_attribute, NULL);
- g_free (style_attribute);
-
- ul = webkit_dom_document_create_element (document, "ul", NULL);
- webkit_dom_element_set_attribute (ul, "tabindex", "-1", NULL);
- webkit_dom_node_append_child (WEBKIT_DOM_NODE (main_div),
- WEBKIT_DOM_NODE (ul),
- NULL);
-
- webkit_dom_element_set_attribute (ul, "style",
- "margin: 0;"
- "padding: 0;",
- NULL);
-
- cached_users = (GList *)g_object_get_data (G_OBJECT (username_node), "ephy-cached-users");
-
- username_node_ever_edited =
- GPOINTER_TO_INT (g_object_get_data (G_OBJECT (username_node),
- "ephy-user-ever-edited"));
-
- for (GList *l = cached_users; l && l->data; l = l->next) {
- const char *user = l->data;
- WebKitDOMElement *li;
- WebKitDOMElement *anchor;
- char *child_style;
- gboolean is_selected;
-
- /* Filter out the available names that do not match, but show all options in
- * case we have been triggered by something other than the user editing the
- * input.
- */
- if (username_node_ever_edited && !g_str_has_prefix (user, username))
- continue;
-
- is_selected = !g_strcmp0 (username, user);
-
- li = webkit_dom_document_create_element (document, "li", NULL);
- webkit_dom_element_set_attribute (li, "tabindex", "-1", NULL);
- webkit_dom_node_append_child (WEBKIT_DOM_NODE (ul),
- WEBKIT_DOM_NODE (li),
- NULL);
-
- child_style = get_user_choice_style (is_selected);
- webkit_dom_element_set_attribute (li, "style", child_style, NULL);
- g_free (child_style);
-
- /* Store the selected node, if any for ease of querying which user
- * is currently selected.
- */
- if (is_selected)
- g_object_set_data (G_OBJECT (main_div), "ephy-user-selected", li);
-
- anchor = webkit_dom_document_create_element (document, "a", NULL);
- webkit_dom_node_append_child (WEBKIT_DOM_NODE (li),
- WEBKIT_DOM_NODE (anchor),
- NULL);
-
- child_style = get_user_choice_anchor_style (is_selected);
- webkit_dom_element_set_attribute (anchor, "style", child_style, NULL);
- g_free (child_style);
-
- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (li), "mousedown",
- G_CALLBACK (user_chosen_cb), TRUE,
- username_node);
-
- webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (anchor),
- user,
- NULL);
- }
-
- g_free (username);
- body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
- webkit_dom_node_append_child (WEBKIT_DOM_NODE (body),
- WEBKIT_DOM_NODE (main_div),
- NULL);
-}
-
-static gboolean
-username_node_changed_cb (WebKitDOMNode *username_node,
- WebKitDOMEvent *dom_event,
- WebKitWebPage *web_page)
-{
- WebKitDOMDocument *document;
-
- document = webkit_web_page_get_dom_document (web_page);
- remove_user_choices (document);
-
- return TRUE;
-}
-
-static gboolean
-username_node_clicked_cb (WebKitDOMNode *username_node,
- WebKitDOMEvent *dom_event,
- WebKitWebPage *web_page)
-{
- WebKitDOMDocument *document;
-
- document = webkit_web_page_get_dom_document (web_page);
- if (webkit_dom_document_get_element_by_id (document, "ephy-user-choices-container"))
- return TRUE;
-
- show_user_choices (document, username_node);
-
- return TRUE;
-}
-
-static void
-clear_password_field (WebKitDOMNode *username_node)
-{
- EphyEmbedFormAuth *form_auth;
- WebKitDOMNode *password_node;
-
- form_auth = (EphyEmbedFormAuth *)g_object_get_data (G_OBJECT (username_node),
- "ephy-form-auth");
-
- password_node = ephy_embed_form_auth_get_password_node (form_auth);
- webkit_dom_html_input_element_set_value (WEBKIT_DOM_HTML_INPUT_ELEMENT (password_node), "");
-}
-
-static void
-pre_fill_password (WebKitDOMNode *username_node)
-{
- EphyEmbedFormAuth *form_auth;
-
- form_auth = (EphyEmbedFormAuth *)g_object_get_data (G_OBJECT (username_node),
- "ephy-form-auth");
-
- pre_fill_form (form_auth);
-}
-
-static gboolean
-username_node_keydown_cb (WebKitDOMNode *username_node,
- WebKitDOMEvent *dom_event,
- WebKitWebPage *web_page)
-{
- WebKitDOMDocument *document;
- WebKitDOMElement *main_div;
- WebKitDOMElement *container;
- WebKitDOMElement *selected = NULL;
- WebKitDOMElement *to_select = NULL;
- WebKitDOMElement *anchor;
- WebKitDOMKeyboardEvent *keyboard_event;
- guint keyval = GDK_KEY_VoidSymbol;
- char *li_style_attribute;
- char *anchor_style_attribute;
- const char *username;
-
- keyboard_event = WEBKIT_DOM_KEYBOARD_EVENT (dom_event);
- document = webkit_web_page_get_dom_document (web_page);
-
- /* U+001B means the Esc key here; we should find a better way of testing which
- * key has been pressed.
- */
- if (!g_strcmp0 (webkit_dom_keyboard_event_get_key_identifier (keyboard_event), "Up"))
- keyval = GDK_KEY_Up;
- else if (!g_strcmp0 (webkit_dom_keyboard_event_get_key_identifier (keyboard_event), "Down"))
- keyval = GDK_KEY_Down;
- else if (!g_strcmp0 (webkit_dom_keyboard_event_get_key_identifier (keyboard_event), "U+001B")) {
- remove_user_choices (document);
- return TRUE;
- } else
- return TRUE;
-
- main_div = webkit_dom_document_get_element_by_id (document, "ephy-user-choices-container");
-
- if (!main_div) {
- show_user_choices (document, username_node);
- return TRUE;
- }
-
- /* Grab the selected node. */
- selected = WEBKIT_DOM_ELEMENT (g_object_get_data (G_OBJECT (main_div), "ephy-user-selected"));
-
- /* Fetch the ul. */
- container = webkit_dom_element_get_first_element_child (main_div);
-
- /* We have a previous selection already, so perform any selection relative to
- * it.
- */
- if (selected) {
- if (keyval == GDK_KEY_Up)
- to_select = webkit_dom_element_get_previous_element_sibling (selected);
- else if (keyval == GDK_KEY_Down)
- to_select = webkit_dom_element_get_next_element_sibling (selected);
- }
-
- if (!to_select) {
- if (keyval == GDK_KEY_Up)
- to_select = webkit_dom_element_get_last_element_child (container);
- else if (keyval == GDK_KEY_Down)
- to_select = webkit_dom_element_get_first_element_child (container);
- }
-
- /* Unselect the selected node. */
- if (selected) {
- li_style_attribute = get_user_choice_style (FALSE);
- webkit_dom_element_set_attribute (selected, "style", li_style_attribute, NULL);
- g_free (li_style_attribute);
-
- anchor = webkit_dom_element_get_first_element_child (selected);
-
- anchor_style_attribute = get_user_choice_anchor_style (FALSE);
- webkit_dom_element_set_attribute (anchor, "style", anchor_style_attribute, NULL);
- g_free (anchor_style_attribute);
- }
-
- /* Selected the new node. */
- if (to_select) {
- g_object_set_data (G_OBJECT (main_div), "ephy-user-selected", to_select);
-
- li_style_attribute = get_user_choice_style (TRUE);
- webkit_dom_element_set_attribute (to_select, "style", li_style_attribute, NULL);
- g_free (li_style_attribute);
-
- anchor = webkit_dom_element_get_first_element_child (to_select);
-
- anchor_style_attribute = get_user_choice_anchor_style (TRUE);
- webkit_dom_element_set_attribute (anchor, "style", anchor_style_attribute, NULL);
- g_free (anchor_style_attribute);
-
- username = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (anchor));
- webkit_dom_html_input_element_set_value (WEBKIT_DOM_HTML_INPUT_ELEMENT (username_node), username);
-
- pre_fill_password (username_node);
- } else
- clear_password_field (username_node);
-
- webkit_dom_event_prevent_default (dom_event);
-
- return TRUE;
-}
-
-static gboolean
-username_node_input_cb (WebKitDOMNode *username_node,
- WebKitDOMEvent *dom_event,
- WebKitWebPage *web_page)
+sensitive_form_message_serializer (guint64 page_id,
+ gboolean is_insecure_action)
{
- WebKitDOMDocument *document;
- WebKitDOMElement *main_div;
-
- if (g_object_get_data (G_OBJECT (username_node), "ephy-is-auto-filling"))
- return TRUE;
-
- g_object_set_data (G_OBJECT (username_node), "ephy-user-ever-edited", GINT_TO_POINTER (TRUE));
- document = webkit_web_page_get_dom_document (web_page);
- remove_user_choices (document);
- show_user_choices (document, username_node);
-
- /* Check if a username has been selected, otherwise clear password field. */
- main_div = webkit_dom_document_get_element_by_id (document, "ephy-user-choices-container");
- if (g_object_get_data (G_OBJECT (main_div), "ephy-user-selected"))
- pre_fill_password (username_node);
- else
- clear_password_field (username_node);
-
- return TRUE;
-}
-
-static gboolean
-sensitive_form_focused_cb (WebKitDOMHTMLFormElement *form,
- WebKitDOMEvent *dom_event,
- WebKitWebPage *web_page)
-{
- WebKitDOMDOMWindow *dom_window;
GVariant *variant;
char *message;
- char *action;
- gboolean insecure_action;
- dom_window = webkit_dom_document_get_default_view (webkit_web_page_get_dom_document (web_page));
- if (dom_window == NULL)
- return FALSE;
-
- action = webkit_dom_html_form_element_get_action (form);
- /* The goal here is to detect insecure forms on secure pages. The action need
- * not necessarily contain any protocol, so just assume the form is secure
- * unless it's clearly not. Insecure forms on insecure pages will be detected
- * in the UI process. Note that basically no websites should actually be dumb
- * enough to trip this, but no doubt they exist somewhere.... */
- insecure_action = action != NULL && g_str_has_prefix (action, "http://");
-
- variant = g_variant_new ("(tb)",
- webkit_web_page_get_id (web_page),
- insecure_action);
+ variant = g_variant_new ("(tb)", page_id, is_insecure_action);
message = g_variant_print (variant, FALSE);
-
- if (!webkit_dom_dom_window_webkit_message_handlers_post_message (dom_window, "sensitiveFormFocused",
message))
- g_warning ("Error sending sensitiveFormFocused message");
-
- g_free (action);
- g_free (message);
- g_object_unref (dom_window);
g_variant_unref (variant);
- /* Hoping FALSE means "propagate" because there is absolutely no documentation for this. */
- return FALSE;
-}
-
-static void
-form_destroyed_cb (gpointer form_auth, GObject *form)
-{
- g_object_unref (form_auth);
+ return message;
}
static void
@@ -1090,99 +352,45 @@ web_page_form_controls_associated (WebKitWebPage *web_page,
GPtrArray *elements,
EphyWebExtension *extension)
{
- WebKitDOMDocument *document = NULL;
+ WebKitFrame *frame;
+ GPtrArray *form_controls;
+ JSCContext *js_context;
+ JSCValue *js_ephy;
+ JSCValue *js_serializer;
+ JSCValue *js_result;
+ gboolean remember_passwords;
guint i;
- document = webkit_web_page_get_dom_document (web_page);
+ frame = webkit_web_page_get_main_frame (web_page);
+ js_context = webkit_frame_get_js_context (frame);
+ form_controls = g_ptr_array_new_with_free_func (g_object_unref);
for (i = 0; i < elements->len; ++i) {
- WebKitDOMElement *element;
- WebKitDOMHTMLFormElement *form;
- WebKitDOMNode *username_node = NULL;
- WebKitDOMNode *password_node = NULL;
-
- element = WEBKIT_DOM_ELEMENT (g_ptr_array_index (elements, i));
- if (!WEBKIT_DOM_IS_HTML_FORM_ELEMENT (element))
- continue;
-
- form = WEBKIT_DOM_HTML_FORM_ELEMENT (element);
-
- if (ephy_web_dom_utils_form_contains_sensitive_element (form)) {
- LOG ("Sensitive form element detected, hooking sensitive form focused callback");
- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (form), "focus",
- G_CALLBACK (sensitive_form_focused_cb), TRUE,
- web_page);
- }
+ WebKitDOMObject *element = WEBKIT_DOM_OBJECT (g_ptr_array_index (elements, i));
- if (!extension->password_manager ||
- !g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_REMEMBER_PASSWORDS))
- continue;
-
- /* 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,
- AUTH_CACHE_AUTOFILL)) {
- EphyEmbedFormAuth *form_auth;
- GList *cached_users;
- const char *uri;
- char *origin;
- char *form_action;
- char *target_origin;
-
- uri = webkit_web_page_get_uri (web_page);
-
- form_action = webkit_dom_html_form_element_get_action (form);
- if (form_action == NULL)
- form_action = g_strdup (uri);
- target_origin = ephy_uri_to_security_origin (form_action);
-
- LOG ("Hooking and pre-filling a form");
-
- /* EphyEmbedFormAuth takes ownership of the nodes */
- form_auth = ephy_embed_form_auth_new (web_page,
- target_origin,
- username_node,
- password_node,
- NULL,
- NULL);
-
- /* Plug in the user autocomplete */
- origin = ephy_uri_to_security_origin (uri);
- cached_users = ephy_password_manager_get_cached_users (extension->password_manager, origin);
-
- if (cached_users && cached_users->next && username_node) {
- LOG ("More than 1 password saved, hooking menu for choosing which on focus");
- g_object_set_data (G_OBJECT (username_node), "ephy-cached-users", cached_users);
- g_object_set_data (G_OBJECT (username_node), "ephy-form-auth", form_auth);
- g_object_set_data (G_OBJECT (username_node), "ephy-document", document);
- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (username_node), "input",
- G_CALLBACK (username_node_input_cb), TRUE,
- web_page);
- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (username_node), "keydown",
- G_CALLBACK (username_node_keydown_cb), FALSE,
- web_page);
- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (username_node), "mouseup",
- G_CALLBACK (username_node_clicked_cb), FALSE,
- web_page);
- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (username_node), "change",
- G_CALLBACK (username_node_changed_cb), FALSE,
- web_page);
- webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (username_node), "blur",
- G_CALLBACK (username_node_changed_cb), FALSE,
- web_page);
- } else
- LOG ("No items or a single item in cached_users, not hooking menu for choosing.");
-
- pre_fill_form (form_auth);
-
- g_free (origin);
- g_free (form_action);
- g_free (target_origin);
- g_object_weak_ref (G_OBJECT (form), form_destroyed_cb, form_auth);
- } else
- LOG ("No pre-fillable/hookable form found");
+ g_ptr_array_add (form_controls, webkit_frame_get_js_value_for_dom_object (frame, element));
}
+
+ js_ephy = jsc_context_get_value (js_context, "Ephy");
+ js_serializer = jsc_value_new_function (js_context,
+ "sensitiveFormMessageSerializer",
+ G_CALLBACK (sensitive_form_message_serializer), NULL, NULL,
+ G_TYPE_STRING, 2,
+ G_TYPE_UINT64, G_TYPE_BOOLEAN);
+ remember_passwords = extension->password_manager &&
+ g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_REMEMBER_PASSWORDS);
+ js_result = jsc_value_object_invoke_method (js_ephy,
+ "formControlsAssociated",
+ G_TYPE_UINT64, webkit_web_page_get_id (web_page),
+ G_TYPE_PTR_ARRAY, form_controls,
+ JSC_TYPE_VALUE, js_serializer,
+ G_TYPE_BOOLEAN, remember_passwords,
+ G_TYPE_NONE);
+ g_object_unref (js_result);
+ g_ptr_array_unref (form_controls);
+ g_object_unref (js_serializer);
+ g_object_unref (js_ephy);
+ g_object_unref (js_context);
}
static void
@@ -1204,19 +412,21 @@ web_page_context_menu (WebKitWebPage *web_page,
WebKitWebHitTestResult *hit_test_result,
gpointer user_data)
{
- char *string;
+ char *string = NULL;
GVariantBuilder builder;
- WebKitDOMDocument *document = webkit_web_page_get_dom_document (web_page);
- WebKitDOMDOMWindow *window = webkit_dom_document_get_default_view (document);
- WebKitDOMDOMSelection *selection = webkit_dom_dom_window_get_selection (window);
+ WebKitFrame *frame;
+ JSCContext *js_context;
+ JSCValue *js_value;
- g_object_unref (window);
+ frame = webkit_web_page_get_main_frame (web_page);
+ js_context = webkit_frame_get_js_context (frame);
- if (!selection)
- return FALSE;
+ js_value = jsc_context_evaluate (js_context, "window.getSelection().toString();", -1);
+ if (!jsc_value_is_null (js_value) && !jsc_value_is_undefined (js_value))
+ string = jsc_value_to_string (js_value);
+ g_object_unref (js_value);
- string = ephy_web_dom_utils_get_selection_as_string (selection);
- g_object_unref (selection);
+ g_object_unref (js_context);
if (!string || *string == '\0') {
g_free (string);
@@ -1309,19 +519,6 @@ ephy_web_extension_page_created_cb (EphyWebExtension *extension,
extension);
}
-static WebKitWebPage *
-get_webkit_web_page_or_return_dbus_error (GDBusMethodInvocation *invocation,
- WebKitWebExtension *web_extension,
- guint64 page_id)
-{
- WebKitWebPage *web_page = webkit_web_extension_get_page (web_extension, page_id);
- if (!web_page) {
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
- "Invalid page ID: %"G_GUINT64_FORMAT, page_id);
- }
- return web_page;
-}
-
static void
handle_method_call (GDBusConnection *connection,
const char *sender,
@@ -1337,62 +534,8 @@ handle_method_call (GDBusConnection *connection,
if (g_strcmp0 (interface_name, EPHY_WEB_EXTENSION_INTERFACE) != 0)
return;
- if (g_strcmp0 (method_name, "HasModifiedForms") == 0) {
- WebKitWebPage *web_page;
- WebKitDOMDocument *document;
- guint64 page_id;
- gboolean has_modifed_forms;
-
- g_variant_get (parameters, "(t)", &page_id);
- web_page = get_webkit_web_page_or_return_dbus_error (invocation, extension->extension, page_id);
- if (!web_page)
- return;
-
- document = webkit_web_page_get_dom_document (web_page);
- has_modifed_forms = ephy_web_dom_utils_has_modified_forms (document);
-
- g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", has_modifed_forms));
- } else if (g_strcmp0 (method_name, "GetWebAppTitle") == 0) {
- WebKitWebPage *web_page;
- WebKitDOMDocument *document;
- char *title = NULL;
- guint64 page_id;
-
- g_variant_get (parameters, "(t)", &page_id);
- web_page = get_webkit_web_page_or_return_dbus_error (invocation, extension->extension, page_id);
- if (!web_page)
- return;
-
- document = webkit_web_page_get_dom_document (web_page);
- title = ephy_web_dom_utils_get_application_title (document);
-
- g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", title ? title : ""));
- } else if (g_strcmp0 (method_name, "GetBestWebAppIcon") == 0) {
- WebKitWebPage *web_page;
- WebKitDOMDocument *document;
- const char *base_uri = NULL;
- char *uri = NULL;
- char *color = NULL;
- guint64 page_id;
-
- g_variant_get (parameters, "(t&s)", &page_id, &base_uri);
- web_page = get_webkit_web_page_or_return_dbus_error (invocation, extension->extension, page_id);
- if (!web_page)
- return;
-
- if (base_uri == NULL || *base_uri == '\0') {
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
- "Base URI cannot be NULL or empty");
- return;
- }
-
- document = webkit_web_page_get_dom_document (web_page);
- ephy_web_dom_utils_get_best_icon (document, base_uri, &uri, &color);
-
- g_dbus_method_invocation_return_value (invocation,
- g_variant_new ("(ss)", uri ? uri : "", color ? color : ""));
- } else if (g_strcmp0 (method_name, "FormAuthDataSaveConfirmationResponse") == 0) {
- EphyEmbedFormAuth *form_auth;
+ if (g_strcmp0 (method_name, "FormAuthDataSaveConfirmationResponse") == 0) {
+ SaveAuthRequest *request;
guint request_id;
gboolean should_store;
GHashTable *requests;
@@ -1401,12 +544,20 @@ handle_method_call (GDBusConnection *connection,
g_variant_get (parameters, "(ub)", &request_id, &should_store);
- form_auth = g_hash_table_lookup (requests, GINT_TO_POINTER (request_id));
- if (!form_auth)
+ request = g_hash_table_lookup (requests, GINT_TO_POINTER (request_id));
+ if (!request)
return;
- if (should_store)
- store_password (form_auth);
+ if (should_store) {
+ ephy_password_manager_save (extension->password_manager,
+ request->origin,
+ request->target_origin,
+ request->username,
+ request->password,
+ request->username_field_name,
+ request->password_field_name,
+ request->is_new);
+ }
g_hash_table_remove (requests, GINT_TO_POINTER (request_id));
} else if (g_strcmp0 (method_name, "HistorySetURLs") == 0) {
if (extension->overview_model) {
@@ -1651,6 +802,125 @@ authorize_authenticated_peer_cb (GDBusAuthObserver *observer,
return ephy_dbus_peer_is_authorized (credentials);
}
+static void
+js_log (const char *message)
+{
+ LOG ("%s", message);
+}
+
+static void
+js_auto_fill (JSCValue *js_element,
+ const char *value)
+{
+ WebKitDOMNode *node;
+ WebKitDOMElement *element;
+
+ node = webkit_dom_node_for_js_value (js_element);
+ element = WEBKIT_DOM_ELEMENT (node);
+
+ webkit_dom_element_html_input_element_set_auto_filled (element, TRUE);
+ webkit_dom_element_html_input_element_set_editing_value (element, value);
+}
+
+static gboolean
+js_is_edited (JSCValue *js_element)
+{
+ WebKitDOMNode *node = webkit_dom_node_for_js_value (js_element);
+
+ return webkit_dom_element_html_input_element_is_user_edited (WEBKIT_DOM_ELEMENT (node));
+}
+
+static void
+js_exception_handler (JSCContext *context,
+ JSCException *exception)
+{
+ JSCValue *js_console;
+ JSCValue *js_result;
+
+ js_console = jsc_context_get_value (context, "console");
+ js_result = jsc_value_object_invoke_method (js_console, "error", JSC_TYPE_EXCEPTION, exception,
G_TYPE_NONE);
+ g_object_unref (js_result);
+ g_object_unref (js_console);
+
+ g_warning ("JavaScriptException: %s", jsc_exception_get_message (exception));
+
+ jsc_context_throw_exception (context, exception);
+}
+
+static void
+window_object_cleared_cb (WebKitScriptWorld *world,
+ WebKitWebPage *page,
+ WebKitFrame *frame,
+ EphyWebExtension *extension)
+{
+ JSCContext *js_context;
+ GBytes *bytes;
+ const char* data;
+ gsize data_size;
+ JSCValue *js_ephy;
+ JSCValue *js_function;
+ JSCValue *result;
+
+ if (!webkit_frame_is_main_frame (frame))
+ return;
+
+ js_context = webkit_frame_get_js_context_for_script_world (frame, world);
+ jsc_context_push_exception_handler (js_context, (JSCExceptionHandler)js_exception_handler, NULL, NULL);
+
+ bytes = g_resources_lookup_data ("/org/gnome/epiphany-web-extension/js/ephy.js",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+ data = g_bytes_get_data (bytes, &data_size);
+ result = jsc_context_evaluate_with_source_uri (js_context, data, data_size,
"resource:///org/gnome/epiphany-web-extension/js/ephy.js");
+ g_bytes_unref (bytes);
+ g_object_unref (result);
+
+ js_ephy = jsc_context_get_value (js_context, "Ephy");
+
+ js_function = jsc_value_new_function (js_context,
+ "log",
+ G_CALLBACK (js_log), NULL, NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+ jsc_value_object_set_property (js_ephy, "log", js_function);
+ g_object_unref (js_function);
+
+ ephy_permissions_manager_export_to_js_context (extension->permissions_manager,
+ js_context,
+ js_ephy);
+
+ if (extension->password_manager) {
+ ephy_password_manager_export_to_js_context (extension->password_manager,
+ js_context,
+ js_ephy);
+
+ js_function = jsc_value_new_function (js_context,
+ "autoFill",
+ G_CALLBACK (js_auto_fill), NULL, NULL,
+ G_TYPE_NONE, 2,
+ JSC_TYPE_VALUE, G_TYPE_STRING);
+ jsc_value_object_set_property (js_ephy, "autoFill", js_function);
+ g_object_unref (js_function);
+ }
+
+ js_function = jsc_value_new_function (js_context,
+ "isWebApplication",
+ G_CALLBACK (ephy_dot_dir_is_web_application), NULL, NULL,
+ G_TYPE_BOOLEAN, 0,
+ G_TYPE_NONE);
+ jsc_value_object_set_property (js_ephy, "isWebApplication", js_function);
+ g_object_unref (js_function);
+
+ js_function = jsc_value_new_function (js_context,
+ "isEdited",
+ G_CALLBACK (js_is_edited), NULL, NULL,
+ G_TYPE_BOOLEAN, 1,
+ JSC_TYPE_VALUE);
+ jsc_value_object_set_property (js_ephy, "isEdited", js_function);
+ g_object_unref (js_function);
+
+ g_object_unref (js_ephy);
+ g_object_unref (js_context);
+}
+
void
ephy_web_extension_initialize (EphyWebExtension *extension,
WebKitWebExtension *wk_extension,
@@ -1668,6 +938,11 @@ ephy_web_extension_initialize (EphyWebExtension *extension,
extension->initialized = TRUE;
+ g_signal_connect (webkit_script_world_get_default (),
+ "window-object-cleared",
+ G_CALLBACK (window_object_cleared_cb),
+ extension);
+
extension->extension = g_object_ref (wk_extension);
if (!is_private_profile) {
extension->password_manager = ephy_password_manager_new ();
diff --git a/embed/web-extension/meson.build b/embed/web-extension/meson.build
index 242c3bb..06368d9 100644
--- a/embed/web-extension/meson.build
+++ b/embed/web-extension/meson.build
@@ -1,11 +1,17 @@
+resource_files = files('resources/epiphany-web-extension.gresource.xml')
+resources = gnome.compile_resources('epiphany-web-extension-resources',
+ resource_files,
+ c_name: 'epiphany_web_extension',
+ source_dir: 'resources'
+)
+
web_extension_sources = [
- 'ephy-embed-form-auth.c',
'ephy-uri-tester.c',
- 'ephy-web-dom-utils.c',
'ephy-web-extension.c',
'ephy-web-extension-main.c',
'ephy-web-overview.c',
- 'ephy-web-overview-model.c'
+ 'ephy-web-overview-model.c',
+ resources
]
web_extension_deps = [
diff --git a/embed/web-extension/resources/epiphany-web-extension.gresource.xml
b/embed/web-extension/resources/epiphany-web-extension.gresource.xml
new file mode 100644
index 0000000..657d576
--- /dev/null
+++ b/embed/web-extension/resources/epiphany-web-extension.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/epiphany-web-extension">
+ <file compressed="true">js/ephy.js</file>
+ </gresource>
+</gresources>
diff --git a/embed/web-extension/resources/js/ephy.js b/embed/web-extension/resources/js/ephy.js
new file mode 100644
index 0000000..b4c3f68
--- /dev/null
+++ b/embed/web-extension/resources/js/ephy.js
@@ -0,0 +1,602 @@
+var Ephy = {};
+
+Ephy.formControlsAssociated = function(pageID, forms, serializer, rememberPasswords)
+{
+ Ephy.formManagers = [];
+
+ for (let i = 0; i < forms.length; i++) {
+ if (!(forms[i] instanceof HTMLFormElement))
+ continue;
+ let formManager = new Ephy.FormManager(pageID, forms[i]);
+ formManager.handleSensitiveElement(serializer);
+ if (rememberPasswords)
+ formManager.preFillForms();
+ Ephy.formManagers.push(formManager);
+ }
+}
+
+Ephy.handleFormSubmission = function(pageID, form, formAuthRequester)
+{
+ let formManager = null;
+ for (let i = 0; i < Ephy.formManagers.length; i++) {
+ let manager = Ephy.formManagers[i];
+ if (manager.pageID() == pageID && manager.form() == form) {
+ formManager = manager;
+ break;
+ }
+ }
+
+ if (!formManager) {
+ formManager = new Ephy.FormManager(pageID, form);
+ Ephy.formManagers.push(formManager);
+ }
+
+ formManager.handleFormSubmission(formAuthRequester);
+}
+
+Ephy.hasModifiedForms = function()
+{
+ for (let i = 0; i < document.forms.length; i++) {
+ let form = document.forms[i];
+ let modifiedInputElement = false;
+ for (let j = 0; j < form.elements.length; j++) {
+ let element = form.elements[j];
+ if (!Ephy.isEdited(element))
+ continue;
+
+ if (element instanceof HTMLTextAreaElement)
+ return !!element.value;
+
+ if (element instanceof HTMLInputElement) {
+ // A small heuristic here. If there's only one input element
+ // modified and it does not have a lot of text the user is
+ // likely not very interested in saving this work, so do
+ // nothing (eg, google search input).
+ if (element.value.length > 50)
+ return true;
+ if (modifiedInputElement)
+ return true;
+ modifiedInputElement = true;
+ }
+ }
+ }
+}
+
+Ephy.getWebAppTitle = function()
+{
+ let metas = document.getElementsByTagName('meta');
+ for (let i = 0; i < metas.length; i++) {
+ let meta = metas[i];
+ if (meta.name == 'application-name')
+ return meta.content;
+
+ // og:site_name is read from the property attribute (standard), but is
+ // commonly seen on the web in the name attribute. Both are supported.
+ if (meta.getAttribute('property') == 'og:site_name' || meta.name == 'og:site_name')
+ return meta.content;
+ }
+ return null;
+}
+
+Ephy.getWebAppIcon = function(baseURL)
+{
+ // FIXME: This function could be improved considerably. See the first two answers at:
+ //
http://stackoverflow.com/questions/21991044/how-to-get-high-resolution-website-logo-favicon-for-a-given-url
+ //
+ // Also check out: https://www.slightfuture.com/webdev/gnome-web-app-icons
+ let iconURL = null;
+ let appleTouchIconURL = null;
+ let largestIconSize = 0;
+ let links = document.getElementsByTagName('link');
+ for (let i = 0; i < links.length; i++) {
+ let link = links[i];
+ if (link.rel == 'icon' || link.rel == 'shortcut icon' || link.rel == 'icon shortcut' || link.rel ==
'shortcut-icon') {
+ let sizes = link.getAttribute('sizes');
+ if (!sizes)
+ continue;
+
+ if (sizes == 'any') {
+ // "any" means a vector, and thus it will always be the largest icon.
+ iconURL = link.href;
+ break;
+ }
+
+ let sizesList = size.split(' ');
+ for (let j = 0; j < sizesList.length; j++) {
+ let size = sizesList[j].toLowerCase().split('x');
+
+ // Only accept square icons.
+ if (size.length != 2 || size[0] != size[1])
+ continue;
+
+ // Only accept icons of 96 px (smallest GNOME HIG app icon) or larger.
+ // It's better to defer to other icon discovery methods if smaller
+ // icons are returned here.
+ if (size[0] >= 92 && size[0] > largestIconSize) {
+ iconURL = link.href;
+ largestIconSize = size[0];
+ }
+ }
+ } else if (link.rel == 'apple-touch-icon' || link.rel == 'apple-touch-icon-precomposed') {
+ // TODO: support more than one possible icon.
+ // apple-touch-icon is best touch-icon candidate.
+ if (link.rel == 'apple-touch-icon' || !appleTouchIconURL)
+ appleTouchIconURL = link.href;
+ // TODO: Try to retrieve /apple-touch-icon.png, and return it if it exist.
+ }
+ }
+
+ // HTML icon.
+ if (iconURL)
+ return { 'url' : new URL(iconURL, baseURL).href, 'color' : null };
+
+ let iconColor = null;
+ let ogpIcon = null;
+ let metas = document.getElementsByTagName('meta');
+ for (let i = 0; i < metas.length; i++) {
+ let meta = metas[i];
+ // FIXME: Ought to also search browserconfig.xml
+ // See: http://stackoverflow.com/questions/24625305/msapplication-tileimage-favicon-backup
+ if (meta.name == 'msapplication-TileImage')
+ iconURL = meta.content;
+ else if (meta.name == 'msapplication-TileColor')
+ iconColor = meta.content;
+ else if (meta.getAttribute('property') == 'og:image' || meta.getAttribute('itemprop') == 'image')
+ ogpIcon = meta.content;
+ }
+
+ // msapplication icon.
+ if (iconURL)
+ return { 'url' : new URL(iconURL, baseURL).href, 'color' : iconColor };
+
+ // Apple touch icon.
+ if (appleTouchIconURL)
+ return { 'url' : new URL(appleTouchIconURL, baseURL).href, 'color' : null };
+
+ // ogp icon.
+ if (ogpIcon)
+ return { 'url' : new URL(ogpIcon, baseURL).href, 'color' : null };
+
+ // Last ditch effort: just fallback to the default favicon location.
+ return { 'url' : new URL('./favicon.ico', baseURL).href, 'color' : null };
+}
+
+Ephy.PreFillUserMenu = class PreFillUserMenu
+{
+ constructor(manager, userElement, users, passwordElement)
+ {
+ this._manager = manager;
+ this._userElement = userElement;
+ this._users = users;
+ this._passwordElement = passwordElement;
+ this._selected = null;
+ this._wasEdited = false;
+
+ this._userElement.addEventListener('input', this._onInput.bind(this), true);
+ this._userElement.addEventListener('mouseup', this._onMouseUp.bind(this), false);
+ this._userElement.addEventListener('keydown', this._onKeyDown.bind(this), false);
+ this._userElement.addEventListener('change', this._removeMenu, false);
+ this._userElement.addEventListener('blur', this._removeMenu, false);
+ }
+
+ // Private
+
+ _onInput(event)
+ {
+ if (this._manager.isAutoFilling(this._userElement))
+ return;
+
+ this._wasEdited = true;
+ this._removeMenu();
+ this._showMenu(false);
+ }
+
+ _onMouseUp(event)
+ {
+ if (document.getElementById('ephy-user-choices-container'))
+ return;
+
+ this._showMenu(!this._wasEdited);
+ }
+
+ _onKeyDown(event)
+ {
+ if (event.key == 'Escape') {
+ this._removeMenu();
+ return;
+ }
+
+ if (event.key != 'ArrowDown' && event.key != 'ArrowUp')
+ return;
+
+ let container = document.getElementById('ephy-user-choices-container');
+ if (!container) {
+ this._showMenu(!this._wasEdited);
+ return;
+ }
+
+ let newSelect = null;
+ if (this._selected)
+ newSelect = event.key != 'ArrowUp' ? this._selected.previousSibling : this._selected.nextSibling;
+
+ if (!newSelect)
+ newSelect = event.key != 'ArrowUp' ? container.firstElementChild.lastElementChild :
container.firstElementChild.firstElementChild;
+
+ if (newSelect) {
+ this._selected = newSelect;
+ this._userElement.value = this._selected.firstElementChild.textContent;
+ this._manager.preFill();
+ } else {
+ this._passwordElement.value = '';
+ }
+
+ event.preventDefault();
+ }
+
+ _showMenu(showAll)
+ {
+ let mainDiv = document.createElement('div');
+ mainDiv.id = 'ephy-user-choices-container';
+
+ let elementRect = this._userElement.getBoundingClientRect();
+
+ // 2147483647 is the maximum value browsers will take for z-index.
+ // See http://stackoverflow.com/questions/8565821/css-max-z-index-value
+ mainDiv.style.cssText = 'position: absolute;' +
+ 'z-index: 2147483647;' +
+ 'cursor: default;' +
+ 'background-color: white;' +
+ 'box-shadow: 5px 5px 5px black;' +
+ 'border-top: 0px;' +
+ 'border-radius: 8px;' +
+ '-webkit-user-modify: read-only ! important;';
+ mainDiv.style.width = this._userElement.offsetWidth;
+ mainDiv.style.left = elementRect.left + document.body.scrollLeft;
+ mainDiv.style.top = elementRect.top + document.body.scrollTop;
+
+ let ul = document.createElement('ul');
+ ul.style.cssText = 'margin: 0; padding: 0;';
+ ul.tabindex = -1;
+ mainDiv.appendChild(ul);
+
+ this._selected = null;
+ for (let i = 0; i < this._users.length; i++) {
+ let user = this._users[i];
+ if (!showAll && !user.startsWith(this._userElement.value))
+ continue;
+
+ let li = document.createElement('li');
+ li.style.cssText = 'list-style-type: none ! important;' +
+ 'background-image: none ! important;' +
+ 'padding: 3px 6px ! important;' +
+ 'margin: 0px;';
+ // FIXME: selection colors.
+ li.tabindex = -1;
+ ul.appendChild(li);
+
+ if (user == this._userElement.value)
+ this._selected = li;
+
+ let anchor = document.createElement('a');
+ anchor.style.cssText = 'font-weight: normal ! important;' +
+ 'font-family: sans ! important;' +
+ 'text-decoration: none ! important;' +
+ '-webkit-user-modify: read-only ! important;';
+ // FIXME: selection colors.
+ anchor.textContent = user;
+ li.appendChild(anchor);
+
+ const self = this;
+ li.addEventListener('mousedown', function (event) {
+ self._userElement.value = user;
+ self._selected = li;
+ self._removeMenu();
+ self._manager.preFill();
+ }, true);
+ }
+
+ document.body.appendChild(mainDiv);
+
+ if (!this._selected)
+ this._passwordElement.value = '';
+ }
+
+ _removeMenu()
+ {
+ let menu = document.getElementById('ephy-user-choices-container');
+ if (menu)
+ menu.parentNode.removeChild(menu);
+ }
+};
+
+Ephy.FormManager = class FormManager
+{
+ constructor(pageID, form)
+ {
+ this._pageID = pageID;
+ this._form = form;
+ this._sensitiveElementMessageSerializer = null;
+ this._formAuth = null;
+ this._preFillUserMenu = null;
+ this._elementBeingAutoFilled = null;
+ }
+
+ // Public
+
+ pageID()
+ {
+ return this._pageID;
+ }
+
+ form()
+ {
+ return this._form;
+ }
+
+ handleSensitiveElement(serializer)
+ {
+ if (!this._containsSensitiveElement())
+ return;
+
+ Ephy.log('Sensitive form element detected, hooking sensitive form focused callback');
+ this._sensitiveElementMessageSerializer = serializer;
+ this._form.addEventListener('focus', this._sensitiveElementFocused.bind(this), true);
+ }
+
+ isAutoFilling(element)
+ {
+ return this._elementBeingAutoFilled === element;
+ }
+
+ preFillForms()
+ {
+ this._formAuth = this._findFormAuthElements(true);
+ if (!this._formAuth || !this._formAuth.passwordNode) {
+ Ephy.log('No pre-fillable/hookable form found');
+ this._formAuth = null;
+ return;
+ }
+
+ this._formAuth.url = new URL(String(window.location));
+ try {
+ this._formAuth.targetURL = new URL(this._form.action);
+ } catch(err) {
+ this._formAuth.targetURL = this._formAuth.url;
+ }
+
+ Ephy.log('Hooking and pre-filling a form');
+
+ if (this._formAuth.usernameNode) {
+ let users = Ephy.passwordManager.cachedUsers(this._formAuth.url.origin);
+ if (users.length > 1) {
+ Ephy.log('More than one password saved, hooking menu for choosing which on focus');
+ this._preFillUserMenu = new Ephy.PreFillUserMenu(this, this._formAuth.usernameNode, users,
this._formAuth.passwordNode);
+ } else {
+ Ephy.log('Single item in cached_users, not hooking menu for choosing.');
+ }
+ } else {
+ Ephy.log('No items in cached_users, not hooking menu for choosing.');
+ }
+
+ this.preFill();
+ }
+
+ preFill()
+ {
+ const self = this;
+ Ephy.passwordManager.query(
+ this._formAuth.url.origin,
+ this._formAuth.targetURL.origin,
+ this._formAuth.usernameNode && this._formAuth.usernameNode.value ?
this._formAuth.usernameNode.value : null,
+ this._formAuth.usernameNode ? this._formAuth.usernameNode.name : null,
+ this._formAuth.passwordNode.name ? this._formAuth.passwordNode.name : null).then(function
(authInfo) {
+ if (!authInfo) {
+ Ephy.log('No result');
+ return;
+ }
+
+ Ephy.log('Found: user ' + authInfo.username + ' pass (hidden)');
+
+ if (self._formAuth.usernameNode && authInfo.username) {
+ self._elementBeingAutoFilled = self._formAuth.usernameNode;
+ Ephy.autoFill(self._formAuth.usernameNode, authInfo.username);
+ self._elementBeingAutoFilled = null;
+ }
+
+ if (authInfo.password) {
+ self._elementBeingAutoFilled = self._formAuth.passwordNode;
+ Ephy.autoFill(self._formAuth.passwordNode, authInfo.password);
+ self._elementBeingAutoFilled = null;
+ }
+ }
+ );
+ }
+
+ handleFormSubmission(saveAuthRequester)
+ {
+ if (!this._formAuth)
+ return;
+
+ this._formAuth = this._findFormAuthElements(false);
+ if (!this._formAuth || !this._formAuth.passwordNode) {
+ this._formAuth = null;
+ return;
+ }
+
+ if (!this._formAuth.passwordNode.value)
+ return;
+
+ this._formAuth.url = new URL(String(window.location));
+ try {
+ this._formAuth.targetURL = new URL(this._form.action);
+ } catch {
+ this._formAuth.targetURL = this._formAuth.url;
+ }
+
+ let permission = Ephy.permissionsManager.permission(Ephy.PermissionType.SAVE_PASSWORD,
this._formAuth.url.origin);
+ if (permission == Ephy.Permission.DENY) {
+ Ephy.log('User/password storage permission previously denied. Not asking about storing.');
+ return;
+ }
+
+ if (permission == Ephy.Permission.UNDECIDED && Ephy.isWebApplication())
+ permission = Ephy.Permission.PERMIT;
+
+ const self = this;
+ Ephy.passwordManager.query(
+ this._formAuth.url.origin,
+ this._formAuth.targetURL.origin,
+ this._formAuth.usernameNode && this._formAuth.usernameNode.value ?
this._formAuth.usernameNode.value : null,
+ this._formAuth.usernameNode ? this._formAuth.usernameNode.name : null,
+ this._formAuth.passwordNode.name ? this._formAuth.passwordNode.name : null).then(function
(authInfo) {
+ if (authInfo) {
+ if (authInfo.username == self._formAuth.usernameNode.value &&
+ authInfo.password == self._formAuth.passwordNode.value) {
+ Ephy.log('User/password already stored. Not asking about storing.');
+ return;
+ }
+
+ if (permission == Ephy.Permission.PERMIT) {
+ Ephy.log('User/password not yet stored. Storing.');
+ Ephy.passwordManager.save(self._formAuth.url.origin, self._formAuth.targetURL.origin,
+ self._formAuth.usernameNode &&
self._formAuth.usernameNode.value ? self._formAuth.usernameNode.value : null,
+ self._formAuth.passwordNode.value ?
self._formAuth.passwordNode.value : null,
+ self._formAuth.usernameNode ?
self._formAuth.usernameNode.name : null,
+ self._formAuth.passwordNode.name ?
self._formAuth.passwordNode.name : null,
+ false);
+ return;
+ }
+
+ Ephy.log('User/password not yet stored. Asking about storing.');
+ } else {
+ Ephy.log('No result on query; asking whether we should store.');
+ }
+
+ window.webkit.messageHandlers.formAuthData.postMessage(saveAuthRequester(self._pageID,
+ self._formAuth.url.origin, self._formAuth.targetURL.origin,
+ self._formAuth.usernameNode && self._formAuth.usernameNode.value ?
self._formAuth.usernameNode.value : null,
+ self._formAuth.passwordNode.value ? self._formAuth.passwordNode.value : null,
+ self._formAuth.usernameNode ? self._formAuth.usernameNode.name : null,
+ self._formAuth.passwordNode.name ? self._formAuth.passwordNode.name : null,
+ authInfo == null));
+ }
+ );
+ }
+
+ // Private
+
+ _containsSensitiveElement()
+ {
+ for (let i = 0; i < this._form.elements.length; i++) {
+ let element = this._form.elements[i];
+ if (element instanceof HTMLInputElement) {
+ if (element.type === 'password' || element.type === 'adminpw')
+ return true;
+ }
+ }
+ return false;
+ }
+
+ _sensitiveElementFocused(event)
+ {
+ let isInsecureAction = this._form.action.startsWith('http://');
+
window.webkit.messageHandlers.sensitiveFormFocused.postMessage(this._sensitiveElementMessageSerializer(this._pageID,
isInsecureAction));
+ }
+
+ _findPasswordFields(forAutofill)
+ {
+ let passwordFields = [];
+ for (let i = 0; i < this._form.elements.length; i++) {
+ let element = this._form.elements[i];
+ if (element instanceof HTMLInputElement && element.type === 'password') {
+ // We only want to process forms with 1-3 fields. A common
+ // case is to have a "change password" form with 3 fields:
+ // Old password, New password, Confirm new password.
+ // Forms with more than 3 password fields are unlikely,
+ // and we don't know how to process them, so reject them
+ if (passwordFields.length == 3)
+ return null;
+ passwordFields.push({ 'element' : element, 'index' : i });
+ }
+ }
+ return passwordFields;
+ }
+
+ _findFormAuthElements(forAutofill)
+ {
+ let passwordNodes = this._findPasswordFields(forAutofill);
+ if (!passwordNodes || !passwordNodes.length)
+ return null;
+
+ // Start at the first found password field and search backwards.
+ // Assume the first eligible field to contain username.
+ let usernameNode = null;
+ let firstPasswordNodeData = passwordNodes[0];
+ for (let i = firstPasswordNodeData.index; i >= 0; i--) {
+ let element = this._form.elements[i];
+ if (element instanceof HTMLInputElement) {
+ if (element.type === 'text' || element.type === 'email' ||
+ element.type === 'tel' || element.type === 'url' ||
+ element.type === 'number') {
+ usernameNode = element;
+ break;
+ }
+ }
+ }
+
+ // Choose password field that contains the password that we want to store
+ // To do that, we compare the field values. We can only do this when user
+ // submits login data, because otherwise all the fields are empty. In that
+ // case just pick the first field.
+ let passwordNodeIndex = 0;
+ if (!forAutofill && passwordNodes.length != 1) {
+ // Get values of all password fields.
+ let passwords = [];
+ for (let i = passwordNodes.length - 1; i >= 0; i--)
+ passwords[i] = passwordNodes[i].element.value;
+
+ if (passwordNodes.length == 2) {
+ // If there are two password fields, assume that the form has either
+ // Password and Confirm password fields, or Old password and New password.
+ // That can be guessed by comparing values in the fields. If they are
+ // different, we assume that the second password is "new" and use it.
+ // If they match, then just take the first field.
+ if (passwords[0] == passwords[1]) {
+ // Password / Confirm password.
+ passwordNodeIndex = 0;
+ } else {
+ // Old password / New password.
+ passwordNodeIndex = 1;
+ }
+ } else if (passwordNodes.length == 3) {
+ // This is probably a complete Old password, New password, Confirm
+ // new password case. Here we assume that if two fields have the same
+ // value, then it's the new password and we should take it. A special
+ // case is when all 3 passwords are different. We don't know what to
+ // do in this case, so just reject the form.
+ if (passwords[0] == passwords[1] && passwords[1] == passwords[2]) {
+ // All values are same.
+ passwordNodeIndex = 0;
+ } else if (passwords[0] == passwords[1]) {
+ // New password / Confirm new password / Old password.
+ passwordNodeIndex = 0;
+ } else if (passwords[0] == passwords[2]) {
+ // New password / Old password / Confirm new password.
+ passwordNodeIndex = 0;
+ } else if (passwords[1] == passwords[2]) {
+ // Old password / New password / Confirm new password.
+ passwordNodeIndex = 1;
+ } else {
+ // All values are different. Reject the form.
+ passwordNodeIndex = -1;
+ }
+ }
+ }
+
+ let passwordNode = null;
+ if (passwordNodeIndex >= 0)
+ passwordNode = passwordNodes[passwordNodeIndex].element;
+
+ return { 'usernameNode' : usernameNode, 'passwordNode' : passwordNode };
+ }
+};
diff --git a/lib/ephy-permissions-manager.c b/lib/ephy-permissions-manager.c
index 101156a..3751626 100644
--- a/lib/ephy-permissions-manager.c
+++ b/lib/ephy-permissions-manager.c
@@ -417,3 +417,64 @@ ephy_permissions_manager_get_denied_origins (EphyPermissionsManager *manager,
{
return ephy_permissions_manager_get_matching_origins (manager, type, FALSE);
}
+
+static EphyPermission
+js_permissions_manager_get_permission (EphyPermissionsManager *manager,
+ EphyPermissionType type,
+ const char *origin)
+{
+ return ephy_permissions_manager_get_permission (manager, type, origin);
+}
+
+void
+ephy_permissions_manager_export_to_js_context (EphyPermissionsManager *manager,
+ JSCContext *js_context,
+ JSCValue *js_namespace)
+{
+ JSCClass *js_class;
+ JSCValue *js_enum, *js_enum_value;
+ JSCValue *js_permissions_manager;
+
+ js_class = jsc_context_register_class (js_context, "PermissionsManager", NULL, NULL, NULL);
+ jsc_class_add_method (js_class,
+ "permission",
+ G_CALLBACK (js_permissions_manager_get_permission), NULL, NULL,
+ G_TYPE_INT, 2,
+ G_TYPE_INT, G_TYPE_STRING);
+
+ js_permissions_manager = jsc_value_new_object (js_context, manager, js_class);
+ jsc_value_object_set_property (js_namespace, "permissionsManager", js_permissions_manager);
+ g_object_unref (js_permissions_manager);
+
+ js_enum = jsc_value_new_object (js_context, NULL, NULL);
+ js_enum_value = jsc_value_new_number (js_context, EPHY_PERMISSION_UNDECIDED);
+ jsc_value_object_set_property (js_enum, "UNDECIDED", js_enum_value);
+ g_object_unref (js_enum_value);
+ js_enum_value = jsc_value_new_number (js_context, EPHY_PERMISSION_DENY);
+ jsc_value_object_set_property (js_enum, "DENY", js_enum_value);
+ g_object_unref (js_enum_value);
+ js_enum_value = jsc_value_new_number (js_context, EPHY_PERMISSION_PERMIT);
+ jsc_value_object_set_property (js_enum, "PERMIT", js_enum_value);
+ g_object_unref (js_enum_value);
+ jsc_value_object_set_property (js_namespace, "Permission", js_enum);
+ g_object_unref (js_enum);
+
+ js_enum = jsc_value_new_object (js_context, NULL, NULL);
+ js_enum_value = jsc_value_new_number (js_context, EPHY_PERMISSION_TYPE_SHOW_NOTIFICATIONS);
+ jsc_value_object_set_property (js_enum, "SHOW_NOTIFICATIONS", js_enum_value);
+ g_object_unref (js_enum_value);
+ js_enum_value = jsc_value_new_number (js_context, EPHY_PERMISSION_TYPE_SAVE_PASSWORD);
+ jsc_value_object_set_property (js_enum, "SAVE_PASSWORD", js_enum_value);
+ g_object_unref (js_enum_value);
+ js_enum_value = jsc_value_new_number (js_context, EPHY_PERMISSION_TYPE_ACCESS_LOCATION);
+ jsc_value_object_set_property (js_enum, "ACCESS_LOCATION", js_enum_value);
+ g_object_unref (js_enum_value);
+ js_enum_value = jsc_value_new_number (js_context, EPHY_PERMISSION_TYPE_ACCESS_MICROPHONE);
+ jsc_value_object_set_property (js_enum, "ACCESS_MICROPHONE", js_enum_value);
+ g_object_unref (js_enum_value);
+ js_enum_value = jsc_value_new_number (js_context, EPHY_PERMISSION_TYPE_ACCESS_WEBCAM);
+ jsc_value_object_set_property (js_enum, "ACCESS_WEBCAM", js_enum_value);
+ g_object_unref (js_enum_value);
+ jsc_value_object_set_property (js_namespace, "PermissionType", js_enum);
+ g_object_unref (js_enum);
+}
diff --git a/lib/ephy-permissions-manager.h b/lib/ephy-permissions-manager.h
index eeeb8b1..a9f1a60 100644
--- a/lib/ephy-permissions-manager.h
+++ b/lib/ephy-permissions-manager.h
@@ -22,6 +22,7 @@
#pragma once
#include <glib-object.h>
+#include <jsc/jsc.h>
G_BEGIN_DECLS
@@ -58,4 +59,8 @@ GList *ephy_permissions_manager_get_permitted_origins (EphyPerm
GList *ephy_permissions_manager_get_denied_origins (EphyPermissionsManager *manager,
EphyPermissionType type);
+void ephy_permissions_manager_export_to_js_context (EphyPermissionsManager *manager,
+ JSCContext *js_context,
+ JSCValue
*js_namespace);
+
G_END_DECLS
diff --git a/lib/sync/ephy-password-manager.c b/lib/sync/ephy-password-manager.c
index 250a416..8efd1f4 100644
--- a/lib/sync/ephy-password-manager.c
+++ b/lib/sync/ephy-password-manager.c
@@ -1106,3 +1106,152 @@ ephy_synchronizable_manager_iface_init (EphySynchronizableManagerInterface *ifac
iface->save = synchronizable_manager_save;
iface->merge = synchronizable_manager_merge;
}
+
+typedef struct {
+ EphyPasswordManager *password_manager;
+ const char *origin;
+ const char *target_origin;
+ const char *username;
+ const char *username_field;
+ const char *password_field;
+} PasswordManagerPromiseData;
+
+static void
+js_password_manager_query_finished_cb (GList *records,
+ JSCValue *resolve)
+{
+ JSCContext *js_context;
+ JSCValue *result, *value;
+ EphyPasswordRecord *record;
+
+ record = records && records->data ? EPHY_PASSWORD_RECORD (records->data) : NULL;
+ js_context = jsc_value_get_context (resolve);
+
+ if (record) {
+ JSCValue *property;
+
+ result = jsc_value_new_object (js_context, NULL, NULL);
+
+ property = jsc_value_new_string (js_context, ephy_password_record_get_username (record));
+ jsc_value_object_set_property (result, "username", property);
+ g_object_unref (property);
+
+ property = jsc_value_new_string (js_context, ephy_password_record_get_password (record));
+ jsc_value_object_set_property (result, "password", property);
+ g_object_unref (property);
+ } else {
+ result = jsc_value_new_null (js_context);
+ }
+
+ value = jsc_value_function_call (resolve, JSC_TYPE_VALUE, result, G_TYPE_NONE);
+ g_object_unref (value);
+ g_object_unref (result);
+
+ g_list_free_full (records, g_object_unref);
+ g_object_unref (resolve);
+}
+
+static void
+js_password_manager_query_resolve (JSCValue *resolve,
+ JSCValue *reject,
+ PasswordManagerPromiseData *data)
+{
+ ephy_password_manager_query (data->password_manager,
+ NULL,
+ data->origin,
+ data->target_origin,
+ data->username,
+ data->username_field,
+ data->password_field,
+ (EphyPasswordManagerQueryCallback)js_password_manager_query_finished_cb,
+ g_object_ref (resolve));
+}
+
+static JSCValue *
+js_password_manager_query (EphyPasswordManager *self,
+ const char *origin,
+ const char *target_origin,
+ const char *username,
+ const char *username_field,
+ const char *password_field)
+{
+ JSCContext *js_context = jsc_context_get_current ();
+ JSCValue *executor, *promise, *retval;
+ PasswordManagerPromiseData data = { self, origin, target_origin, username, username_field, password_field
};
+
+ executor = jsc_value_new_function (js_context, NULL,
+ G_CALLBACK (js_password_manager_query_resolve),
+ &data, NULL,
+ G_TYPE_NONE, 2,
+ JSC_TYPE_VALUE,
+ JSC_TYPE_VALUE);
+ promise = jsc_context_get_value (js_context, "Promise");
+ retval = jsc_value_constructor_call (promise, JSC_TYPE_VALUE, executor, G_TYPE_NONE);
+ g_object_unref (executor);
+ g_object_unref (promise);
+
+ return retval;
+}
+
+static void
+js_password_manager_save (EphyPasswordManager *self,
+ const char *origin,
+ const char *target_origin,
+ const char *username,
+ const char *password,
+ const char *username_field,
+ const char *password_field,
+ gboolean is_new)
+{
+ ephy_password_manager_save (self, origin, target_origin, username, password, username_field,
password_field, is_new);
+}
+
+static GPtrArray *
+js_password_manager_cached_users (EphyPasswordManager *self,
+ const char *origin)
+{
+ JSCContext *js_context = jsc_context_get_current ();
+ GPtrArray *retval;
+ GList *cached_users, *l;
+
+ cached_users = ephy_password_manager_get_cached_users (self, origin);
+ retval = g_ptr_array_new_with_free_func (g_object_unref);
+
+ for (l = cached_users; l && l->data; l = g_list_next (l))
+ g_ptr_array_add (retval, jsc_value_new_string (js_context, l->data));
+
+ return retval;
+}
+
+void
+ephy_password_manager_export_to_js_context (EphyPasswordManager *self,
+ JSCContext *js_context,
+ JSCValue *js_namespace)
+{
+ JSCClass *js_class;
+ JSCValue *js_password_manager;
+
+ js_class = jsc_context_register_class (js_context, "PasswordManager", NULL, NULL, NULL);
+ jsc_class_add_method (js_class,
+ "query",
+ G_CALLBACK (js_password_manager_query), NULL, NULL,
+ JSC_TYPE_VALUE, 5,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING);
+ jsc_class_add_method (js_class,
+ "save",
+ G_CALLBACK (js_password_manager_save), NULL, NULL,
+ G_TYPE_NONE, 7,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_BOOLEAN);
+ jsc_class_add_method (js_class,
+ "cachedUsers",
+ G_CALLBACK (js_password_manager_cached_users), NULL, NULL,
+ G_TYPE_PTR_ARRAY, 1,
+ G_TYPE_STRING);
+
+ js_password_manager = jsc_value_new_object (js_context, self, js_class);
+ jsc_value_object_set_property (js_namespace, "passwordManager", js_password_manager);
+ g_object_unref (js_password_manager);
+}
diff --git a/lib/sync/ephy-password-manager.h b/lib/sync/ephy-password-manager.h
index 969651e..2f79b82 100644
--- a/lib/sync/ephy-password-manager.h
+++ b/lib/sync/ephy-password-manager.h
@@ -23,6 +23,7 @@
#include "ephy-password-record.h"
#include <glib-object.h>
+#include <jsc/jsc.h>
#include <libsecret/secret.h>
G_BEGIN_DECLS
@@ -68,5 +69,8 @@ void ephy_password_manager_query (EphyPasswor
void ephy_password_manager_forget (EphyPasswordManager *self,
const char *id);
void ephy_password_manager_forget_all (EphyPasswordManager *self);
+void ephy_password_manager_export_to_js_context (EphyPasswordManager *self,
+ JSCContext *js_context,
+ JSCValue *js_namespace);
G_END_DECLS
diff --git a/meson.build b/meson.build
index 26a6fd4..375b941 100644
--- a/meson.build
+++ b/meson.build
@@ -64,7 +64,7 @@ endif
glib_requirement = '>= 2.52.0'
gtk_requirement = '>= 3.22.13'
nettle_requirement = '>= 3.2'
-webkitgtk_requirement = '>= 2.19.4'
+webkitgtk_requirement = '>= 2.21.1'
cairo_dep = dependency('cairo', version: '>= 1.2')
gcr_dep = dependency('gcr-3', version: '>= 3.5.5')
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index 90a6c50..eb866cf 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -463,7 +463,7 @@ sync_message_from_fxa_content_cb (WebKitUserContentManager *manager,
char *error_msg = NULL;
gboolean is_error = FALSE;
- message = ephy_embed_utils_get_js_result_as_string (result);
+ message = jsc_value_to_string (webkit_javascript_result_get_js_value (result));
if (!message) {
g_warning ("Failed to get JavaScript result as string");
is_error = TRUE;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]