From: Daniel Carl Date: Thu, 11 May 2017 22:08:45 +0000 (+0200) Subject: Fixed wrong client to webpage relation #349. X-Git-Url: https://git.owens.tech/projects.html/projects.html/git?a=commitdiff_plain;h=70b172403a3d97b25f039db658a0b38f495233a2;p=vimb.git Fixed wrong client to webpage relation #349. In case a related webview is created. The relation between webextensions web page and the client in the ui process where mismatched. In fact the relation was correct, but when a dbus call was fired from ui to webextension, the webextension used the last created web page to run javascript in or to focus input fields. This patch adds the page_id of the web page to the dbus calls that target a specific web page. So that th webextension can get the right page by this id to use to answer the call. Also the communication from webextension to ui lagged this essential information. So that a click to a editable filed in a related webview all related instances into input mode switched. --- diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 5dce15e..4cdcaf7 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -28,12 +28,14 @@ static gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, GIOStream *stream, GCredentials *credentials, gpointer data); static gboolean on_new_connection(GDBusServer *server, GDBusConnection *connection, gpointer data); +static void on_connection_close(GDBusConnection *connection, gboolean + remote_peer_vanished, GError *error, gpointer data); static void on_proxy_created (GDBusProxy *proxy, GAsyncResult *result, gpointer data); static void on_vertical_scroll(GDBusConnection *connection, const char *sender_name, const char *object_path, const char *interface_name, const char *signal_name, - GVariant *parameters, Client *c); + GVariant *parameters, gpointer data); static void dbus_call(Client *c, const char *method, GVariant *param, GAsyncReadyCallback callback); static GVariant *dbus_call_sync(Client *c, const char *method, GVariant @@ -118,7 +120,7 @@ static gboolean on_new_connection(GDBusServer *server, /* Create dbus proxy. */ g_return_val_if_fail(G_IS_DBUS_CONNECTION(connection), FALSE); - /*g_signal_connect(connection, "closed", G_CALLBACK(connection_closed_cb), NULL);*/ + g_signal_connect(connection, "closed", G_CALLBACK(on_connection_close), NULL); g_dbus_proxy_new(connection, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES|G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, @@ -133,6 +135,14 @@ static gboolean on_new_connection(GDBusServer *server, return TRUE; } +static void on_connection_close(GDBusConnection *connection, gboolean + remote_peer_vanished, GError *error, gpointer data) +{ + if (error && !remote_peer_vanished) { + g_warning("Unexpected lost connection to web extension: %s", error->message); + } +} + static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, gpointer data) { @@ -166,9 +176,19 @@ static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, static void on_vertical_scroll(GDBusConnection *connection, const char *sender_name, const char *object_path, const char *interface_name, const char *signal_name, - GVariant *parameters, Client *c) + GVariant *parameters, gpointer data) { - g_variant_get(parameters, "(tt)", &c->state.scroll_max, &c->state.scroll_percent); + glong max; + guint percent; + guint64 pageid; + Client *c; + + g_variant_get(parameters, "(ttq)", &pageid, &max, &percent); + c = vb_get_client_for_page_id(pageid); + if (c) { + c->state.scroll_max = max; + c->state.scroll_percent = percent; + } vb_statusbar_update(c); } @@ -176,15 +196,15 @@ static void on_vertical_scroll(GDBusConnection *connection, void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback) { if (callback) { - dbus_call(c, "EvalJs", g_variant_new("(s)", js), callback); + dbus_call(c, "EvalJs", g_variant_new("(ts)", js, c->page_id), callback); } else { - dbus_call(c, "EvalJsNoResult", g_variant_new("(s)", js), NULL); + dbus_call(c, "EvalJsNoResult", g_variant_new("(ts)", c->page_id, js), NULL); } } GVariant *ext_proxy_eval_script_sync(Client *c, char *js) { - return dbus_call_sync(c, "EvalJs", g_variant_new("(s)", js)); + return dbus_call_sync(c, "EvalJs", g_variant_new("(ts)", c->page_id, js)); } /** @@ -193,7 +213,7 @@ GVariant *ext_proxy_eval_script_sync(Client *c, char *js) */ void ext_proxy_focus_input(Client *c) { - dbus_call(c, "FocusInput", NULL, NULL); + dbus_call(c, "FocusInput", g_variant_new("(t)", c->page_id), NULL); } /** @@ -252,23 +272,22 @@ static void on_web_extension_page_created(GDBusConnection *connection, const char *interface_name, const char *signal_name, GVariant *parameters, gpointer data) { - Client *p; - guint64 page_id; + Client *c; + guint64 pageid; - g_variant_get(parameters, "(t)", &page_id); + g_variant_get(parameters, "(t)", &pageid); /* Search for the client with the same page id as returned by the * webextension. */ - for (p = vb.clients; p && p->page_id != page_id; p = p->next); - - if (p) { + c = vb_get_client_for_page_id(pageid); + if (c) { /* Set the dbus proxy on the right client based on page id. */ - p->dbusproxy = (GDBusProxy*)data; + c->dbusproxy = (GDBusProxy*)data; /* Subscribe to dbus signals here. */ g_dbus_connection_signal_subscribe(connection, NULL, VB_WEBEXTENSION_INTERFACE, "VerticalScroll", VB_WEBEXTENSION_OBJECT_PATH, NULL, G_DBUS_SIGNAL_FLAGS_NONE, - (GDBusSignalCallback)on_vertical_scroll, p, NULL); + (GDBusSignalCallback)on_vertical_scroll, NULL, NULL); } } diff --git a/src/main.c b/src/main.c index b6952c4..d394f12 100644 --- a/src/main.c +++ b/src/main.c @@ -95,7 +95,7 @@ static void vimb_cleanup(void); static void vimb_setup(void); static WebKitWebView *webview_new(Client *c, WebKitWebView *webview); static void on_script_message_focus(WebKitUserContentManager *manager, - WebKitJavascriptResult *message, Client *c); + WebKitJavascriptResult *res, gpointer data); static gboolean profileOptionArgFunc(const gchar *option_name, const gchar *value, gpointer data, GError **error); @@ -278,6 +278,21 @@ void vb_enter_prompt(Client *c, char id, const char *prompt, gboolean print_prom } } +/** + * Returns the client for given page id. + */ +Client *vb_get_client_for_page_id(guint64 pageid) +{ + Client *c; + /* Search for the client with the same page id. */ + for (c = vb.clients; c && c->page_id != pageid; c = c->next); + + if (c) { + return c; + } + return NULL; +} + /** * Retrieves the content of the command line. * Returned string must be freed with g_free. @@ -1679,17 +1694,32 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) /* Setup script message handlers. */ webkit_user_content_manager_register_script_message_handler(ucm, "focus"); - g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), c); + g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), NULL); return new; } static void on_script_message_focus(WebKitUserContentManager *manager, - WebKitJavascriptResult *message, Client *c) + WebKitJavascriptResult *res, gpointer data) { + char *message; + GVariant *variant; + guint64 pageid; gboolean is_focused; + Client *c; + + message = util_js_result_as_string(res); + variant = g_variant_parse(G_VARIANT_TYPE("(tb)"), message, NULL, NULL, NULL); + g_free(message); + + g_variant_get(variant, "(tb)", &pageid, &is_focused); + g_variant_unref(variant); + + c = vb_get_client_for_page_id(pageid); + if (!c) { + return; + } - is_focused = (gboolean)util_js_result_as_number(message); /* Don't change the mode if we are in pass through mode. */ if (c->mode->id == 'n' && is_focused) { vb_enter(c, 'i'); diff --git a/src/main.h b/src/main.h index 38f8808..3b7a31e 100644 --- a/src/main.h +++ b/src/main.h @@ -271,6 +271,7 @@ void vb_echo(Client *c, MessageType type, gboolean hide, const char *error, ...) void vb_echo_force(Client *c, MessageType type, gboolean hide, const char *error, ...); void vb_enter(Client *c, char id); void vb_enter_prompt(Client *c, char id, const char *prompt, gboolean print_prompt); +Client *vb_get_client_for_page_id(guint64 pageid); char *vb_input_get_text(Client *c); void vb_input_set_text(Client *c, const char *text); void vb_input_update_style(Client *c); diff --git a/src/util.c b/src/util.c index 54bc416..38f5d98 100644 --- a/src/util.c +++ b/src/util.c @@ -561,6 +561,10 @@ gboolean util_filename_fill_completion(Client *c, GtkListStore *store, const cha return found; } +/** + * Returns the script result as string. + * Returned string must be freed by g_free. + */ char *util_js_result_as_string(WebKitJavascriptResult *result) { JSValueRef value; diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index c8dadd7..d7ff217 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -32,18 +32,20 @@ static gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, static void on_dbus_connection_created(GObject *source_object, GAsyncResult *result, gpointer data); static void add_onload_event_observers(WebKitDOMDocument *doc, - WebKitWebExtension *extension); + WebKitWebPage *page); static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *event, - gpointer data); + WebKitWebPage *page); static void emit_page_created(GDBusConnection *connection, guint64 pageid); static void emit_page_created_pending(GDBusConnection *connection); static void queue_page_created_signal(guint64 pageid); static void dbus_emit_signal(const char *name, GVariant *data); +static WebKitWebPage *get_web_page_or_return_dbus_error(GDBusMethodInvocation *invocation, + WebKitWebExtension *extension, guint64 pageid); static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, const char *object_path, const char *interface_name, const char *method, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer data); static void on_editable_change_focus(WebKitDOMEventTarget *target, - WebKitDOMEvent *event, WebKitWebExtension *extension); + WebKitDOMEvent *event, WebKitWebPage *page); static void on_page_created(WebKitWebExtension *ext, WebKitWebPage *webpage, gpointer data); static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extension); static gboolean on_web_page_send_request(WebKitWebPage *webpage, WebKitURIRequest *request, @@ -59,21 +61,25 @@ static const char introspection_xml[] = "" " " " " + " " " " " " " " " " " " + " " " " " " " " + " " " " " " " " " " " " + " " " " - " " + " " " " " " " " @@ -85,11 +91,8 @@ static const char introspection_xml[] = struct Ext { guint regid; GDBusConnection *connection; - WebKitWebPage *webpage; - WebKitDOMElement *active; GHashTable *headers; GHashTable *documents; - gboolean input_focus; GArray *page_created_signals; }; struct Ext ext = {0}; @@ -188,7 +191,7 @@ static void on_dbus_connection_created(GObject *source_object, * too. */ static void add_onload_event_observers(WebKitDOMDocument *doc, - WebKitWebExtension *extension) + WebKitWebPage *page) { WebKitDOMEventTarget *target; @@ -204,27 +207,27 @@ static void add_onload_event_observers(WebKitDOMDocument *doc, target = WEBKIT_DOM_EVENT_TARGET(webkit_dom_document_get_default_view(doc)); webkit_dom_event_target_add_event_listener(target, "focus", - G_CALLBACK(on_editable_change_focus), TRUE, extension); + G_CALLBACK(on_editable_change_focus), TRUE, page); webkit_dom_event_target_add_event_listener(target, "blur", - G_CALLBACK(on_editable_change_focus), TRUE, extension); + G_CALLBACK(on_editable_change_focus), TRUE, page); /* Check for focused editable elements also if they where focused before * the event observer where set up. */ /* TODO this is not needed for strict-focus=on */ - on_editable_change_focus(target, NULL, extension); + on_editable_change_focus(target, NULL, page); /* Observe scroll events to get current position in the document. */ webkit_dom_event_target_add_event_listener(target, "scroll", - G_CALLBACK(on_document_scroll), false, NULL); + G_CALLBACK(on_document_scroll), false, page); /* Call the callback explicitly to make sure we have the right position * shown in statusbar also in cases the user does not scroll. */ - on_document_scroll(target, NULL, NULL); + on_document_scroll(target, NULL, page); } /** * Callback called when the document is scrolled. */ static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *event, - gpointer data) + WebKitWebPage *page) { WebKitDOMDocument *doc; @@ -264,7 +267,8 @@ static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *eve if (scrollTop && max) { percent = (guint)(0.5 + (scrollTop * 100 / max)); } - dbus_emit_signal("VerticalScroll", g_variant_new("(tt)", max, percent)); + dbus_emit_signal("VerticalScroll", g_variant_new("(ttq)", + webkit_web_page_get_id(page), max, percent)); } } @@ -344,6 +348,19 @@ static void dbus_emit_signal(const char *name, GVariant *data) } } +static WebKitWebPage *get_web_page_or_return_dbus_error(GDBusMethodInvocation *invocation, + WebKitWebExtension *extension, guint64 pageid) +{ + WebKitWebPage *page = webkit_web_extension_get_page(extension, pageid); + if (!page) { + g_warning("invalid page id %lu", pageid); + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, "Invalid page ID: %"G_GUINT64_FORMAT, pageid); + } + + return page; +} + /** * Handle dbus method calls. */ @@ -352,22 +369,29 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer extension) { char *value; + guint64 pageid; + WebKitWebPage *page; if (g_str_has_prefix(method, "EvalJs")) { char *result = NULL; gboolean success; - gboolean no_result = !g_strcmp0(method, "EvalJsNoResult"); - JSValueRef ref = NULL; + gboolean no_result; + JSValueRef ref = NULL; JSGlobalContextRef jsContext; - g_variant_get(parameters, "(s)", &value); + g_variant_get(parameters, "(ts)", &pageid, &value); + page = get_web_page_or_return_dbus_error(invocation, WEBKIT_WEB_EXTENSION(extension), pageid); + if (!page) { + return; + } + no_result = !g_strcmp0(method, "EvalJsNoResult"); jsContext = webkit_frame_get_javascript_context_for_script_world( - webkit_web_page_get_main_frame(ext.webpage), - webkit_script_world_get_default() - ); + webkit_web_page_get_main_frame(page), + webkit_script_world_get_default() + ); - success = ext_util_js_eval(jsContext, value, &ref); + success = ext_util_js_eval(jsContext, value, &ref); if (no_result) { g_dbus_method_invocation_return_value(invocation, NULL); @@ -377,7 +401,12 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, g_free(result); } } else if (!g_strcmp0(method, "FocusInput")) { - ext_dom_focus_input(webkit_web_page_get_dom_document(ext.webpage)); + g_variant_get(parameters, "(t)", &pageid); + page = get_web_page_or_return_dbus_error(invocation, WEBKIT_WEB_EXTENSION(extension), pageid); + if (!page) { + return; + } + ext_dom_focus_input(webkit_web_page_get_dom_document(page)); g_dbus_method_invocation_return_value(invocation, NULL); } else if (!g_strcmp0(method, "SetHeaderSetting")) { g_variant_get(parameters, "(s)", &value); @@ -397,12 +426,13 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, * WebKitDOMDOMWindow. */ static void on_editable_change_focus(WebKitDOMEventTarget *target, - WebKitDOMEvent *event, WebKitWebExtension *extension) + WebKitDOMEvent *event, WebKitWebPage *page) { - gboolean input_focus; WebKitDOMDocument *doc; WebKitDOMDOMWindow *dom_window; WebKitDOMElement *active; + GVariant *variant; + char *message; if (WEBKIT_DOM_IS_DOM_WINDOW(target)) { g_object_get(target, "document", &doc, NULL); @@ -417,9 +447,8 @@ static void on_editable_change_focus(WebKitDOMEventTarget *target, } active = webkit_dom_document_get_active_element(doc); - /* Don't do anything if there is no active element or the active element - * is the same as before. */ - if (!active || active == ext.active) { + /* Don't do anything if there is no active element */ + if (!active) { return; } if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT(active)) { @@ -428,23 +457,19 @@ static void on_editable_change_focus(WebKitDOMEventTarget *target, iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT(active); subdoc = webkit_dom_html_iframe_element_get_content_document(iframe); - add_onload_event_observers(subdoc, extension); + add_onload_event_observers(subdoc, page); return; } - ext.active = active; - /* Check if the active element is an editable element. */ - input_focus = ext_dom_is_editable(active); - if (input_focus != ext.input_focus) { - ext.input_focus = input_focus; - - webkit_dom_document_get_default_view(doc); - if (!webkit_dom_dom_window_webkit_message_handlers_post_message(dom_window, "focus", input_focus ? "1" : "0")) { - g_warning("Error sending focus message"); - return; - } + variant = g_variant_new("(tb)", webkit_web_page_get_id(page), + ext_dom_is_editable(active)); + message = g_variant_print(variant, FALSE); + g_variant_unref(variant); + if (!webkit_dom_dom_window_webkit_message_handlers_post_message(dom_window, "focus", message)) { + g_warning("Error sending focus message"); } + g_free(message); g_object_unref(dom_window); } @@ -455,8 +480,6 @@ static void on_page_created(WebKitWebExtension *extension, WebKitWebPage *webpag { guint64 pageid = webkit_web_page_get_id(webpage); - /* Save the new created page in the extension data for later use. */ - ext.webpage = webpage; if (ext.connection) { emit_page_created(ext.connection, pageid); } else { @@ -481,8 +504,7 @@ static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extensi } ext.documents = g_hash_table_new(g_direct_hash, g_direct_equal); - add_onload_event_observers(webkit_web_page_get_dom_document(webpage), - WEBKIT_WEB_EXTENSION(extension)); + add_onload_event_observers(webkit_web_page_get_dom_document(webpage), webpage); } /**