diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2014-01-30 17:02:05 +0100 |
---|---|---|
committer | Carlos Garcia Campos <carlosgc@gnome.org> | 2014-02-05 18:35:46 +0100 |
commit | 31bc1fe6ccbe53dfd14822d44fc9563a32799316 (patch) | |
tree | 533039754f233176d6b199d9d2f4f27edaf13d89 /embed | |
parent | aaf6422a17c7080f98d2d82d95ff6313ca500c0a (diff) | |
download | epiphany-31bc1fe6ccbe53dfd14822d44fc9563a32799316.tar.gz |
Add support for using multiple web processes
There's now a setting to set the process model.
https://bugzilla.gnome.org/show_bug.cgi?id=723302
Diffstat (limited to 'embed')
-rw-r--r-- | embed/Makefile.am | 4 | ||||
-rw-r--r-- | embed/ephy-embed-shell.c | 237 | ||||
-rw-r--r-- | embed/ephy-embed-shell.h | 1 | ||||
-rw-r--r-- | embed/ephy-web-extension-proxy.c | 400 | ||||
-rw-r--r-- | embed/ephy-web-extension-proxy.h | 100 | ||||
-rw-r--r-- | embed/ephy-web-view.c | 249 | ||||
-rw-r--r-- | embed/ephy-web-view.h | 17 | ||||
-rw-r--r-- | embed/web-extension/ephy-web-extension.c | 64 |
8 files changed, 931 insertions, 141 deletions
diff --git a/embed/Makefile.am b/embed/Makefile.am index 5f6fec38f..4e79e2e54 100644 --- a/embed/Makefile.am +++ b/embed/Makefile.am @@ -22,7 +22,8 @@ INST_H_FILES = \ ephy-embed-utils.h \ ephy-find-toolbar.h \ ephy-overview.h \ - ephy-web-view.h + ephy-web-view.h \ + ephy-web-extension-proxy.h BUILT_SOURCES = \ @@ -45,6 +46,7 @@ libephyembed_la_SOURCES = \ ephy-overview.c \ ephy-embed-prefs.c \ ephy-web-view.c \ + ephy-web-extension-proxy.c \ $(INST_H_FILES) \ $(NOINST_H_FILES) diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c index 67dbcea90..42e967f1f 100644 --- a/embed/ephy-embed-shell.c +++ b/embed/ephy-embed-shell.c @@ -34,6 +34,7 @@ #include "ephy-settings.h" #include "ephy-snapshot-service.h" #include "ephy-web-extension.h" +#include "ephy-web-extension-proxy.h" #include <glib/gi18n.h> #include <gtk/gtk.h> @@ -53,9 +54,10 @@ struct _EphyEmbedShellPrivate EphyEmbedShellMode mode; EphyFrecentStore *frecent_store; EphyAboutHandler *about_handler; - GDBusProxy *web_extension; - guint web_extension_watch_name_id; - guint web_extension_form_auth_save_signal_id; + GDBusConnection *bus; + GList *web_extensions; + guint web_extensions_page_created_signal_id; + guint web_extensions_form_auth_save_signal_id; }; enum @@ -63,7 +65,7 @@ enum PREPARE_CLOSE, RESTORED_WINDOW, WEB_VIEW_CREATED, - FORM_AUTH_DATA_SAVE_REQUESTED, + PAGE_CREATED, LAST_SIGNAL }; @@ -98,6 +100,24 @@ ephy_embed_shell_dispose (GObject *object) G_OBJECT_CLASS (ephy_embed_shell_parent_class)->dispose (object); } +static gint +web_extension_compare (EphyWebExtensionProxy *proxy, + const char *name_owner) +{ + return g_strcmp0 (ephy_web_extension_proxy_get_name_owner (proxy), name_owner); +} + +static EphyWebExtensionProxy * +ephy_embed_shell_find_web_extension (EphyEmbedShell *shell, + const char *name_owner) +{ + GList *l; + + l = g_list_find_custom (shell->priv->web_extensions, name_owner, (GCompareFunc)web_extension_compare); + + return l ? EPHY_WEB_EXTENSION_PROXY (l->data) : NULL; +} + static void web_extension_form_auth_save_requested (GDBusConnection *connection, const char *sender_name, @@ -107,85 +127,70 @@ web_extension_form_auth_save_requested (GDBusConnection *connection, GVariant *parameters, EphyEmbedShell *shell) { + EphyWebExtensionProxy *web_extension; guint request_id; guint64 page_id; const char *hostname; const char *username; g_variant_get (parameters, "(ut&s&s)", &request_id, &page_id, &hostname, &username); - g_signal_emit (shell, signals[FORM_AUTH_DATA_SAVE_REQUESTED], 0, - request_id, page_id, hostname, username); + web_extension = ephy_embed_shell_find_web_extension (shell, sender_name); + if (!web_extension) + return; + ephy_web_extension_proxy_form_auth_save_requested (web_extension, request_id, page_id, hostname, username); } static void -web_extension_proxy_created_cb (GDBusProxy *proxy, - GAsyncResult *result, - EphyEmbedShell *shell) -{ - GError *error = NULL; +web_extension_page_created (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + EphyEmbedShell *shell) +{ + EphyWebExtensionProxy *web_extension; + guint64 page_id; - shell->priv->web_extension = g_dbus_proxy_new_finish (result, &error); - if (!shell->priv->web_extension) { - g_warning ("Error creating web extension proxy: %s\n", error->message); - g_error_free (error); - } else { - shell->priv->web_extension_form_auth_save_signal_id = - g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (shell->priv->web_extension), - g_dbus_proxy_get_name (shell->priv->web_extension), - EPHY_WEB_EXTENSION_INTERFACE, - "FormAuthDataSaveConfirmationRequired", - EPHY_WEB_EXTENSION_OBJECT_PATH, - NULL, - G_DBUS_SIGNAL_FLAGS_NONE, - (GDBusSignalCallback)web_extension_form_auth_save_requested, - shell, - NULL); - } -} + g_variant_get (parameters, "(t)", &page_id); -static void -web_extension_appeared_cb (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - EphyEmbedShell *shell) -{ - g_dbus_proxy_new (connection, - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - name, - EPHY_WEB_EXTENSION_OBJECT_PATH, - EPHY_WEB_EXTENSION_INTERFACE, - NULL, - (GAsyncReadyCallback)web_extension_proxy_created_cb, - shell); + web_extension = ephy_embed_shell_find_web_extension (shell, sender_name); + if (!web_extension) + return; + g_signal_emit (shell, signals[PAGE_CREATED], 0, page_id, web_extension); } static void -web_extension_vanished_cb (GDBusConnection *connection, - const gchar *name, - EphyEmbedShell *shell) +web_extension_destroyed (EphyEmbedShell *shell, + GObject *web_extension) { - g_clear_object (&shell->priv->web_extension); + shell->priv->web_extensions = g_list_remove (shell->priv->web_extensions, web_extension); } static void -ephy_embed_shell_watch_web_extension (EphyEmbedShell *shell) +ephy_embed_shell_watch_web_extension (EphyEmbedShell *shell, + const char *web_extension_id) { + EphyWebExtensionProxy *web_extension; char *service_name; - service_name = g_strdup_printf ("%s-%u", EPHY_WEB_EXTENSION_SERVICE_NAME, getpid ()); - shell->priv->web_extension_watch_name_id = - g_bus_watch_name (G_BUS_TYPE_SESSION, - service_name, - G_BUS_NAME_WATCHER_FLAGS_NONE, - (GBusNameAppearedCallback) web_extension_appeared_cb, - (GBusNameVanishedCallback) web_extension_vanished_cb, - shell, NULL); + if (!shell->priv->bus) + return; + + service_name = g_strdup_printf ("%s-%s", EPHY_WEB_EXTENSION_SERVICE_NAME, web_extension_id); + web_extension = ephy_web_extension_proxy_new (shell->priv->bus, service_name); + shell->priv->web_extensions = g_list_prepend (shell->priv->web_extensions, web_extension); + g_object_weak_ref (G_OBJECT (web_extension), (GWeakNotify)web_extension_destroyed, shell); g_free (service_name); } +static void +ephy_embed_shell_unwatch_web_extension (EphyWebExtensionProxy *web_extension, + EphyEmbedShell *shell) +{ + g_object_weak_unref (G_OBJECT (web_extension), (GWeakNotify)web_extension_destroyed, shell); +} + /** * ephy_embed_shell_get_global_history_service: * @shell: the #EphyEmbedShell @@ -304,16 +309,73 @@ initialize_web_extensions (WebKitWebContext* web_context, { GVariant *user_data; gboolean private_profile; + char *web_extension_id; + static guint web_extension_count = 0; webkit_web_context_set_web_extensions_directory (web_context, EPHY_WEB_EXTENSIONS_DIR); - ephy_embed_shell_watch_web_extension (shell); + + web_extension_id = g_strdup_printf ("%u-%u", getpid (), ++web_extension_count); + ephy_embed_shell_watch_web_extension (shell, web_extension_id); private_profile = EPHY_EMBED_SHELL_MODE_HAS_PRIVATE_PROFILE (shell->priv->mode); - user_data = g_variant_new ("(usb)", getpid (), ephy_dot_dir (), private_profile); + user_data = g_variant_new ("(ssb)", web_extension_id, ephy_dot_dir (), private_profile); webkit_web_context_set_web_extensions_initialization_user_data (web_context, user_data); } static void +ephy_embed_shell_setup_web_extensions_connection (EphyEmbedShell *shell) +{ + GError *error = NULL; + + shell->priv->bus = g_application_get_dbus_connection (G_APPLICATION (shell)); + if (!shell->priv->bus) { + g_warning ("Application not connected to session bus: %s\n", error->message); + g_error_free (error); + return; + } + + shell->priv->web_extensions_page_created_signal_id = + g_dbus_connection_signal_subscribe (shell->priv->bus, + NULL, + EPHY_WEB_EXTENSION_INTERFACE, + "PageCreated", + EPHY_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback)web_extension_page_created, + shell, + NULL); + shell->priv->web_extensions_form_auth_save_signal_id = + g_dbus_connection_signal_subscribe (shell->priv->bus, + NULL, + EPHY_WEB_EXTENSION_INTERFACE, + "FormAuthDataSaveConfirmationRequired", + EPHY_WEB_EXTENSION_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + (GDBusSignalCallback)web_extension_form_auth_save_requested, + shell, + NULL); +} + +static void +ephy_embed_shell_setup_process_model (EphyEmbedShell *shell, + WebKitWebContext *web_context) +{ + EphyPrefsProcessModel process_model; + + process_model = g_settings_get_enum (EPHY_SETTINGS_MAIN, EPHY_PREFS_PROCESS_MODEL); + switch (process_model) { + case EPHY_PREFS_PROCESS_MODEL_SHARED_SECONDARY_PROCESS: + webkit_web_context_set_process_model (web_context, WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS); + break; + case EPHY_PREFS_PROCESS_MODEL_ONE_SECONDARY_PROCESS_PER_WEB_VIEW: + webkit_web_context_set_process_model (web_context, WEBKIT_PROCESS_MODEL_ONE_SECONDARY_PROCESS_PER_WEB_VIEW); + break; + } +} + +static void ephy_embed_shell_startup (GApplication* application) { EphyEmbedShell *shell = EPHY_EMBED_SHELL (application); @@ -328,7 +390,11 @@ ephy_embed_shell_startup (GApplication* application) /* We're not remoting, setup the Web Context. */ mode = shell->priv->mode; + + ephy_embed_shell_setup_web_extensions_connection (shell); + web_context = webkit_web_context_get_default (); + ephy_embed_shell_setup_process_model (shell, web_context); g_signal_connect (web_context, "initialize-web-extensions", G_CALLBACK (initialize_web_extensions), shell); @@ -369,18 +435,17 @@ ephy_embed_shell_shutdown (GApplication* application) G_APPLICATION_CLASS (ephy_embed_shell_parent_class)->shutdown (application); - if (priv->web_extension_watch_name_id > 0) { - g_bus_unwatch_name (priv->web_extension_watch_name_id); - priv->web_extension_watch_name_id = 0; + if (priv->web_extensions_page_created_signal_id > 0) { + g_dbus_connection_signal_unsubscribe (priv->bus, priv->web_extensions_page_created_signal_id); + priv->web_extensions_page_created_signal_id = 0; } - if (priv->web_extension_form_auth_save_signal_id > 0) { - g_dbus_connection_signal_unsubscribe (g_dbus_proxy_get_connection (priv->web_extension), - priv->web_extension_form_auth_save_signal_id); - priv->web_extension_form_auth_save_signal_id = 0; + if (priv->web_extensions_form_auth_save_signal_id > 0) { + g_dbus_connection_signal_unsubscribe (priv->bus, priv->web_extensions_form_auth_save_signal_id); + priv->web_extensions_form_auth_save_signal_id = 0; } - g_clear_object (&priv->web_extension); + g_list_foreach (priv->web_extensions, (GFunc)ephy_embed_shell_unwatch_web_extension, application); ephy_embed_prefs_shutdown (); } @@ -507,29 +572,23 @@ ephy_embed_shell_class_init (EphyEmbedShellClass *klass) G_TYPE_NONE, 1, EPHY_TYPE_WEB_VIEW); - /* - * EphyEmbedShell::form-auth-data-save-requested: + /** + * EphyEmbedShell::page-created: * @shell: the #EphyEmbedShell - * @request_id: the identifier of the request - * @page_id: the identifier of the web page - * @hostname: the hostname - * @username: the username + * @page_id: the identifier of the web page created + * @web_extension: the #EphyWebExtensionProxy * - * Emitted when a web page requests confirmation to save - * the form authentication data for the given @hostname and - * @username - **/ - signals[FORM_AUTH_DATA_SAVE_REQUESTED] = - g_signal_new ("form-auth-data-save-requested", + * Emitted when a web page is created in the web process. + */ + signals[PAGE_CREATED] = + g_signal_new ("page-created", EPHY_TYPE_EMBED_SHELL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 4, - G_TYPE_UINT, + G_TYPE_NONE, 2, G_TYPE_UINT64, - G_TYPE_STRING, - G_TYPE_STRING); + EPHY_TYPE_WEB_EXTENSION_PROXY); g_type_class_add_private (object_class, sizeof (EphyEmbedShellPrivate)); } @@ -728,14 +787,6 @@ ephy_embed_shell_launch_handler (EphyEmbedShell *shell, return ret; } -GDBusProxy * -ephy_embed_shell_get_web_extension_proxy (EphyEmbedShell *shell) -{ - g_return_val_if_fail (EPHY_IS_EMBED_SHELL (shell), NULL); - - return shell->priv->web_extension; -} - /** * ephy_embed_shell_clear_cache: * @shell: an #EphyEmbedShell diff --git a/embed/ephy-embed-shell.h b/embed/ephy-embed-shell.h index 02016c819..f48864039 100644 --- a/embed/ephy-embed-shell.h +++ b/embed/ephy-embed-shell.h @@ -89,7 +89,6 @@ gboolean ephy_embed_shell_launch_handler (EphyEmbedShell const char *mime_type, guint32 user_time); void ephy_embed_shell_clear_cache (EphyEmbedShell *shell); -GDBusProxy *ephy_embed_shell_get_web_extension_proxy (EphyEmbedShell *shell); G_END_DECLS diff --git a/embed/ephy-web-extension-proxy.c b/embed/ephy-web-extension-proxy.c new file mode 100644 index 000000000..dc67723dc --- /dev/null +++ b/embed/ephy-web-extension-proxy.c @@ -0,0 +1,400 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * Copyright © 2014 Igalia S.L. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <config.h> +#include "ephy-web-extension-proxy.h" + +#include "ephy-web-extension.h" + +struct _EphyWebExtensionProxyPrivate +{ + GDBusProxy *proxy; + gchar *name_owner; + guint watch_name_id; + guint form_auth_save_signal_id; +}; + +enum +{ + FORM_AUTH_DATA_SAVE_REQUESTED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (EphyWebExtensionProxy, ephy_web_extension_proxy, G_TYPE_OBJECT) + +static void +ephy_web_extension_proxy_finalize (GObject *object) +{ + EphyWebExtensionProxyPrivate *priv = EPHY_WEB_EXTENSION_PROXY (object)->priv; + + g_clear_object (&priv->proxy); + + G_OBJECT_CLASS (ephy_web_extension_proxy_parent_class)->finalize (object); +} + +static void +ephy_web_extension_proxy_dispose (GObject *object) +{ + EphyWebExtensionProxyPrivate *priv = EPHY_WEB_EXTENSION_PROXY (object)->priv; + + if (priv->watch_name_id > 0) { + g_bus_unwatch_name (priv->watch_name_id); + priv->watch_name_id = 0; + } + + g_clear_pointer (&priv->name_owner, g_free); + + G_OBJECT_CLASS (ephy_web_extension_proxy_parent_class)->dispose (object); +} + +static void +ephy_web_extension_proxy_init (EphyWebExtensionProxy *web_extension) +{ + web_extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (web_extension, EPHY_TYPE_WEB_EXTENSION_PROXY, EphyWebExtensionProxyPrivate); +} + +static void +ephy_web_extension_proxy_class_init (EphyWebExtensionProxyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = ephy_web_extension_proxy_finalize; + object_class->dispose = ephy_web_extension_proxy_dispose; + + /** + * EphyWebExtensionProxy::form-auth-data-save-requested: + * @web_extension: the #EphyWebExtensionProxy + * @request_id: the identifier of the request + * @page_id: the identifier of the web page + * @hostname: the hostname + * @username: the username + * + * Emitted when a web page requests confirmation to save + * the form authentication data for the given @hostname and + * @username + **/ + signals[FORM_AUTH_DATA_SAVE_REQUESTED] = + g_signal_new ("form-auth-data-save-requested", + EPHY_TYPE_WEB_EXTENSION_PROXY, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, 4, + G_TYPE_UINT, + G_TYPE_UINT64, + G_TYPE_STRING, + G_TYPE_STRING); + + g_type_class_add_private (object_class, sizeof (EphyWebExtensionProxyPrivate)); +} + +static void +web_extension_proxy_created_cb (GDBusProxy *proxy, + GAsyncResult *result, + EphyWebExtensionProxy *web_extension) +{ + GError *error = NULL; + + web_extension->priv->proxy = g_dbus_proxy_new_finish (result, &error); + if (!web_extension->priv->proxy) { + g_warning ("Error creating web extension proxy: %s\n", error->message); + g_error_free (error); + } +} + +static void +web_extension_appeared_cb (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + EphyWebExtensionProxy *web_extension) +{ + web_extension->priv->name_owner = g_strdup (name_owner); + g_dbus_proxy_new (connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + name, + EPHY_WEB_EXTENSION_OBJECT_PATH, + EPHY_WEB_EXTENSION_INTERFACE, + NULL, + (GAsyncReadyCallback)web_extension_proxy_created_cb, + web_extension); +} + +static void +ephy_web_extension_proxy_watch_name (EphyWebExtensionProxy *web_extension, + GDBusConnection* bus, + const char *service_name) +{ + EphyWebExtensionProxyPrivate *priv = web_extension->priv; + + priv->watch_name_id = + g_bus_watch_name_on_connection (bus, + service_name, + G_BUS_NAME_WATCHER_FLAGS_NONE, + (GBusNameAppearedCallback)web_extension_appeared_cb, + NULL, + web_extension, + (GDestroyNotify)g_object_unref); +} + +EphyWebExtensionProxy * +ephy_web_extension_proxy_new (GDBusConnection *bus, + const char *service_name) +{ + EphyWebExtensionProxy *web_extension; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (bus), NULL); + g_return_val_if_fail (service_name != NULL, NULL); + + web_extension = g_object_new (EPHY_TYPE_WEB_EXTENSION_PROXY, NULL); + ephy_web_extension_proxy_watch_name (web_extension, bus, service_name); + + return web_extension; +} + +const char * +ephy_web_extension_proxy_get_name_owner (EphyWebExtensionProxy *web_extension) +{ + g_return_val_if_fail (EPHY_IS_WEB_EXTENSION_PROXY (web_extension), NULL); + + return web_extension->priv->name_owner; +} + +void +ephy_web_extension_proxy_form_auth_save_requested (EphyWebExtensionProxy *web_extension, + guint request_id, + guint64 page_id, + const char *hostname, + const char *username) +{ + g_return_if_fail (EPHY_IS_WEB_EXTENSION_PROXY (web_extension)); + + g_signal_emit (web_extension, signals[FORM_AUTH_DATA_SAVE_REQUESTED], 0, + request_id, page_id, hostname, username); +} + +void +ephy_web_extension_proxy_form_auth_data_save_confirmation_response (EphyWebExtensionProxy *web_extension, + guint request_id, + gboolean response) +{ + g_return_if_fail (EPHY_IS_WEB_EXTENSION_PROXY (web_extension)); + + if (!web_extension->priv->proxy) + return; + + g_dbus_proxy_call (web_extension->priv->proxy, + "FormAuthDataSaveConfirmationResponse", + g_variant_new ("(ub)", request_id, response), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, 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_return_if_fail (EPHY_IS_WEB_EXTENSION_PROXY (web_extension)); + + task = g_task_new (web_extension, cancellable, callback, user_data); + + if (web_extension->priv->proxy) { + g_dbus_proxy_call (web_extension->priv->proxy, + "HasModifiedForms", + g_variant_new ("(t)", page_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + 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_return_val_if_fail (g_task_is_valid (result, web_extension), FALSE); + + 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_return_if_fail (EPHY_IS_WEB_EXTENSION_PROXY (web_extension)); + + task = g_task_new (web_extension, cancellable, callback, user_data); + + if (web_extension->priv->proxy) { + g_dbus_proxy_call (web_extension->priv->proxy, + "GetBestWebAppIcon", + g_variant_new("(ts)", page_id, base_uri), + G_DBUS_CALL_FLAGS_NONE, + -1, + 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, + gboolean *icon_result, + char **icon_uri, + char **icon_color, + GError **error) +{ + GVariant *variant; + GTask *task = G_TASK (result); + + g_return_val_if_fail (g_task_is_valid (result, web_extension), FALSE); + + variant = g_task_propagate_pointer (task, error); + if (!variant) + return FALSE; + + g_variant_get (variant, "(bss)", icon_result, 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_return_if_fail (EPHY_IS_WEB_EXTENSION_PROXY (web_extension)); + + task = g_task_new (web_extension, cancellable, callback, user_data); + + if (web_extension->priv->proxy) { + g_dbus_proxy_call (web_extension->priv->proxy, + "GetWebAppTitle", + g_variant_new("(t)", page_id), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (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_return_val_if_fail (g_task_is_valid (result, web_extension), FALSE); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/embed/ephy-web-extension-proxy.h b/embed/ephy-web-extension-proxy.h new file mode 100644 index 000000000..fef44d2a7 --- /dev/null +++ b/embed/ephy-web-extension-proxy.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * Copyright © 2014 Igalia S.L. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#if !defined (__EPHY_EPIPHANY_H_INSIDE__) && !defined (EPIPHANY_COMPILATION) +#error "Only <epiphany/epiphany.h> can be included directly." +#endif + +#ifndef EPHY_WEB_EXTENSION_PROXY_H +#define EPHY_WEB_EXTENSION_PROXY_H + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define EPHY_TYPE_WEB_EXTENSION_PROXY (ephy_web_extension_proxy_get_type ()) +#define EPHY_WEB_EXTENSION_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_WEB_EXTENSION_PROXY, EphyWebExtensionProxy)) +#define EPHY_IS_WEB_EXTENSION_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_WEB_EXTENSION_PROXY)) +#define EPHY_WEB_EXTENSION_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_WEB_EXTENSION_PROXY, EphyWebExtensionProxyClass)) +#define EPHY_IS_WEB_EXTENSION_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_WEB_EXTENSION_PROXY)) +#define EPHY_WEB_EXTENSION_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_WEB_EXTENSION_PROXY, EphyWebExtensionProxyClass)) + +typedef struct _EphyWebExtensionProxyClass EphyWebExtensionProxyClass; +typedef struct _EphyWebExtensionProxy EphyWebExtensionProxy; +typedef struct _EphyWebExtensionProxyPrivate EphyWebExtensionProxyPrivate; + +struct _EphyWebExtensionProxy +{ + GObject parent; + + /*< private >*/ + EphyWebExtensionProxyPrivate *priv; +}; + +struct _EphyWebExtensionProxyClass +{ + GObjectClass parent_class; +}; + +GType ephy_web_extension_proxy_get_type (void); + +EphyWebExtensionProxy *ephy_web_extension_proxy_new (GDBusConnection *bus, + const char *service_name); +const char * ephy_web_extension_proxy_get_name_owner (EphyWebExtensionProxy *web_extension); +void ephy_web_extension_proxy_form_auth_save_requested (EphyWebExtensionProxy *web_extension, + guint request_id, + guint64 page_id, + const char *hostname, + const char *username); +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, + gboolean *icon_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); + +G_END_DECLS + +#endif /* !EPHY_WEB_EXTENSION_PROXY_H */ diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c index f50d5c5ed..26ac21190 100644 --- a/embed/ephy-web-view.c +++ b/embed/ephy-web-view.c @@ -43,6 +43,7 @@ #include "ephy-string.h" #include "ephy-web-app-utils.h" #include "ephy-web-dom-utils.h" +#include "ephy-web-extension-proxy.h" #include "ephy-zoom.h" #include <gio/gio.h> @@ -111,6 +112,9 @@ struct _EphyWebViewPrivate { /* TLS information. */ GTlsCertificate *certificate; GTlsCertificateFlags tls_errors; + + /* Web Extension */ + EphyWebExtensionProxy *web_extension; }; typedef struct { @@ -607,50 +611,71 @@ icon_changed_cb (EphyWebView *view, _ephy_web_view_update_icon (view); } +typedef struct { + EphyWebView *web_view; + guint request_id; +} FormAuthRequestData; + static void form_auth_data_save_confirmation_response (GtkInfoBar *info_bar, gint response_id, - gpointer user_data) + FormAuthRequestData *data) { - GDBusProxy *web_extension; - guint request_id = GPOINTER_TO_INT (user_data); - gtk_widget_destroy (GTK_WIDGET (info_bar)); - web_extension = ephy_embed_shell_get_web_extension_proxy (ephy_embed_shell_get_default ()); - if (!web_extension) - return; + if (data->web_view->priv->web_extension) { + ephy_web_extension_proxy_form_auth_data_save_confirmation_response (data->web_view->priv->web_extension, + data->request_id, + response_id == GTK_RESPONSE_YES); + } - g_dbus_proxy_call (web_extension, - "FormAuthDataSaveConfirmationResponse", - g_variant_new ("(ub)", request_id, response_id == GTK_RESPONSE_YES), - G_DBUS_CALL_FLAGS_NONE, - -1, NULL, NULL, NULL); + g_slice_free (FormAuthRequestData, data); } static void -form_auth_data_save_requested (EphyEmbedShell *shell, +form_auth_data_save_requested (EphyWebExtensionProxy *web_extension, guint request_id, guint64 page_id, const char *hostname, const char *username, - WebKitWebView *web_view) + EphyWebView *web_view) { GtkWidget *info_bar; + FormAuthRequestData *data; - if (webkit_web_view_get_page_id (web_view) != page_id) - return; + g_assert (webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)) == page_id); - info_bar = ephy_web_view_create_form_auth_save_confirmation_info_bar (EPHY_WEB_VIEW (web_view), - hostname, username); + info_bar = ephy_web_view_create_form_auth_save_confirmation_info_bar (web_view, hostname, username); + data = g_slice_new (FormAuthRequestData); + data->web_view = web_view; + data->request_id = request_id; g_signal_connect (info_bar, "response", G_CALLBACK (form_auth_data_save_confirmation_response), - GINT_TO_POINTER (request_id)); + data); gtk_widget_show (info_bar); } static void +page_created_cb (EphyEmbedShell *shell, + guint64 page_id, + EphyWebExtensionProxy *web_extension, + EphyWebView *web_view) +{ + EphyWebViewPrivate *priv = web_view->priv; + + if (webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)) != page_id) + return; + + priv->web_extension = web_extension; + g_object_add_weak_pointer (G_OBJECT (priv->web_extension), (gpointer *)&priv->web_extension); + + g_signal_connect (priv->web_extension, "form-auth-data-save-requested", + G_CALLBACK (form_auth_data_save_requested), + web_view); +} + +static void ephy_web_view_dispose (GObject *object) { EphyWebViewPrivate *priv = EPHY_WEB_VIEW (object)->priv; @@ -2001,8 +2026,8 @@ ephy_web_view_init (EphyWebView *web_view) G_CALLBACK (ge_popup_blocked_cb), NULL); - g_signal_connect (ephy_embed_shell_get_default (), "form-auth-data-save-requested", - G_CALLBACK (form_auth_data_save_requested), + g_signal_connect (ephy_embed_shell_get_default (), "page-created", + G_CALLBACK (page_created_cb), web_view); } @@ -2575,19 +2600,13 @@ ephy_web_view_set_typed_address (EphyWebView *view, } static void -has_modified_forms_cb (GDBusProxy *web_extension, +has_modified_forms_cb (EphyWebExtensionProxy *web_extension, GAsyncResult *result, GTask *task) { - GVariant *return_value; - gboolean retval = FALSE; - - return_value = g_dbus_proxy_call_finish (web_extension, result, NULL); - if (return_value) { - g_variant_get (return_value, "(b)", &retval); - g_variant_unref (return_value); - } + gboolean retval; + 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); } @@ -2612,19 +2631,18 @@ ephy_web_view_has_modified_forms (EphyWebView *view, GAsyncReadyCallback callback, gpointer user_data) { - GTask *task = g_task_new (view, cancellable, callback, user_data); - GDBusProxy *web_extension; - - web_extension = ephy_embed_shell_get_web_extension_proxy (ephy_embed_shell_get_default ()); - if (web_extension) { - g_dbus_proxy_call (web_extension, - "HasModifiedForms", - g_variant_new ("(t)", webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view))), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - (GAsyncReadyCallback)has_modified_forms_cb, - g_object_ref (task)); + GTask *task; + + g_return_if_fail (EPHY_IS_WEB_VIEW (view)); + + task = g_task_new (view, cancellable, callback, user_data); + + if (view->priv->web_extension) { + ephy_web_extension_proxy_web_page_has_modified_forms (view->priv->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); } @@ -2642,6 +2660,151 @@ ephy_web_view_has_modified_forms_finish (EphyWebView *view, return g_task_propagate_boolean (G_TASK (result), error); } +typedef struct { + gboolean icon_result; + char *icon_uri; + char *icon_color; +} GetBestWebAppIconAsyncData; + +static void +get_best_web_app_icon_async_data_free (GetBestWebAppIconAsyncData *data) +{ + g_free (data->icon_uri); + g_free (data->icon_color); + + g_slice_free (GetBestWebAppIconAsyncData, data); +} + +static void +get_best_web_app_icon_cb (EphyWebExtensionProxy *web_extension, + GAsyncResult *result, + GTask *task) +{ + gboolean retval = FALSE; + char *uri = NULL; + char *color = NULL; + GError *error = NULL; + + if (!ephy_web_extension_proxy_get_best_web_app_icon_finish (web_extension, result, &retval, &uri, &color, &error)) { + g_task_return_error (task, error); + } else { + GetBestWebAppIconAsyncData *data = g_slice_new0 (GetBestWebAppIconAsyncData); + + data->icon_result = retval; + data->icon_uri = uri; + data->icon_color = color; + g_task_return_pointer (task, data, (GDestroyNotify)get_best_web_app_icon_async_data_free); + } + g_object_unref (task); +} + +void +ephy_web_view_get_best_web_app_icon (EphyWebView *view, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_return_if_fail (EPHY_IS_WEB_VIEW (view)); + + task = g_task_new (view, cancellable, callback, user_data); + + if (view->priv->web_extension) { + ephy_web_extension_proxy_get_best_web_app_icon (view->priv->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); +} + +gboolean +ephy_web_view_get_best_web_app_icon_finish (EphyWebView *view, + GAsyncResult *result, + gboolean *icon_result, + char **icon_uri, + GdkRGBA *icon_color, + GError **error) +{ + GetBestWebAppIconAsyncData *data; + GTask *task = G_TASK (result); + + g_return_val_if_fail (g_task_is_valid (result, view), FALSE); + + data = g_task_propagate_pointer (task, error); + if (!data) + return FALSE; + + if (data->icon_uri != NULL && data->icon_uri[0] != '\0') { + *icon_uri = data->icon_uri; + data->icon_uri = NULL; + } + + if (data->icon_color != NULL && data->icon_color[0] != '\0') + gdk_rgba_parse (icon_color, data->icon_color); + + get_best_web_app_icon_async_data_free (data); + + return TRUE; +} + +static void +get_web_app_title_cb (EphyWebExtensionProxy *web_extension, + GAsyncResult *result, + GTask *task) +{ + char *retval; + 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 + g_task_return_pointer (task, retval, (GDestroyNotify)g_free); + g_object_unref (task); +} + +void +ephy_web_view_get_web_app_title (EphyWebView *view, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_return_if_fail (EPHY_IS_WEB_VIEW (view)); + + task = g_task_new (view, cancellable, callback, user_data); + + if (view->priv->web_extension) { + ephy_web_extension_proxy_get_web_app_title (view->priv->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); +} + +char * +ephy_web_view_get_web_app_title_finish (EphyWebView *view, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, view), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + /** * ephy_web_view_get_security_level: * @view: an #EphyWebView diff --git a/embed/ephy-web-view.h b/embed/ephy-web-view.h index 500aaab0f..5c817eb49 100644 --- a/embed/ephy-web-view.h +++ b/embed/ephy-web-view.h @@ -160,6 +160,23 @@ void ephy_web_view_load_error_page (EphyWebView const char *uri, EphyWebViewErrorPage page, GError *error); +void ephy_web_view_get_best_web_app_icon (EphyWebView *view, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean ephy_web_view_get_best_web_app_icon_finish (EphyWebView *view, + GAsyncResult *result, + gboolean *icon_result, + char **icon_uri, + GdkRGBA *icon_color, + GError **error); +void ephy_web_view_get_web_app_title (EphyWebView *view, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +char *ephy_web_view_get_web_app_title_finish (EphyWebView *view, + GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c index fae08492c..d6c051fc0 100644 --- a/embed/web-extension/ephy-web-extension.c +++ b/embed/web-extension/ephy-web-extension.c @@ -40,10 +40,14 @@ static UriTester *uri_tester; static EphyFormAuthDataCache *form_auth_data_cache; static GDBusConnection *dbus_connection; +static GArray *page_created_signals_pending; static const char introspection_xml[] = "<node>" " <interface name='org.gnome.Epiphany.WebExtension'>" + " <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'/>" @@ -945,10 +949,63 @@ web_page_document_loaded (WebKitWebPage *web_page, } static void +emit_page_created (guint64 page_id) +{ + GError *error = NULL; + + g_dbus_connection_emit_signal (dbus_connection, + NULL, + EPHY_WEB_EXTENSION_OBJECT_PATH, + EPHY_WEB_EXTENSION_INTERFACE, + "PageCreated", + g_variant_new ("(t)", page_id), + &error); + if (error) { + g_warning ("Error emitting signal PageCreated: %s\n", error->message); + g_error_free (error); + } +} + +static void +emit_page_created_signals_pending (void) +{ + guint i; + + if (!page_created_signals_pending) + return; + + for (i = 0; i < page_created_signals_pending->len; i++) { + guint64 page_id; + + page_id = g_array_index (page_created_signals_pending, guint64, i); + emit_page_created (page_id); + } + + g_array_free (page_created_signals_pending, TRUE); + page_created_signals_pending = NULL; +} + +static void +queue_page_created_signal_emission (guint64 page_id) +{ + if (!page_created_signals_pending) + page_created_signals_pending = g_array_new (FALSE, FALSE, sizeof (guint64)); + page_created_signals_pending = g_array_append_val (page_created_signals_pending, page_id); +} + +static void web_page_created_callback (WebKitWebExtension *extension, WebKitWebPage *web_page, gpointer user_data) { + guint64 page_id; + + page_id = webkit_web_page_get_id (web_page); + if (dbus_connection) + emit_page_created (page_id); + else + queue_page_created_signal_emission (page_id); + g_signal_connect_object (web_page, "send-request", G_CALLBACK (web_page_send_request), NULL, 0); @@ -1090,6 +1147,7 @@ bus_acquired_cb (GDBusConnection *connection, } else { dbus_connection = connection; g_object_add_weak_pointer (G_OBJECT (connection), (gpointer *)&dbus_connection); + emit_page_created_signals_pending (); } } @@ -1098,11 +1156,11 @@ webkit_web_extension_initialize_with_user_data (WebKitWebExtension *extension, GVariant *user_data) { char *service_name; - guint extension_id; + const char *extension_id; const char *dot_dir; gboolean private_profile; - g_variant_get (user_data, "(u&sb)", &extension_id, &dot_dir, &private_profile); + g_variant_get (user_data, "(&s&sb)", &extension_id, &dot_dir, &private_profile); ephy_debug_init (); uri_tester = uri_tester_new (dot_dir); @@ -1113,7 +1171,7 @@ webkit_web_extension_initialize_with_user_data (WebKitWebExtension *extension, G_CALLBACK (web_page_created_callback), NULL); - service_name = g_strdup_printf ("%s-%u", EPHY_WEB_EXTENSION_SERVICE_NAME, extension_id); + service_name = g_strdup_printf ("%s-%s", EPHY_WEB_EXTENSION_SERVICE_NAME, extension_id); g_bus_own_name (G_BUS_TYPE_SESSION, service_name, G_BUS_NAME_OWNER_FLAGS_NONE, |