Use script message handler to track elements focus changes.
authorDaniel Carl <danielcarl@gmx.de>
Tue, 11 Apr 2017 22:03:03 +0000 (00:03 +0200)
committerDaniel Carl <danielcarl@gmx.de>
Tue, 11 Apr 2017 22:05:30 +0000 (00:05 +0200)
Use script message handler instead of dbus to track for focused editable
elements to switch vimb into input mode or back to normal mode.

src/ext-proxy.c
src/main.c
src/scripts/vimb_util.js [new file with mode: 0644]
src/util.c
src/util.h
src/webextension/ext-main.c

index b17f081..daf04ce 100644 (file)
@@ -32,10 +32,6 @@ static void on_proxy_created (GDBusProxy *proxy, GAsyncResult *result,
         gpointer data);
 static void dbus_call(Client *c, const char *method, GVariant *param,
         GAsyncReadyCallback callback);
-static void on_editable_change_focus(GDBusConnection *connection,
-        const char *sender_name, const char *object_path,
-        const char *interface_name, const char *signal_name,
-        GVariant *parameters, gpointer data);
 static void on_web_extension_page_created(GDBusConnection *connection,
         const char *sender_name, const char *object_path,
         const char *interface_name, const char *signal_name,
@@ -188,42 +184,6 @@ static void dbus_call(Client *c, const char *method, GVariant *param,
     g_dbus_proxy_call(c->dbusproxy, method, param, G_DBUS_CALL_FLAGS_NONE, -1, NULL, callback, c);
 }
 
-/**
- * Callback called if a editable element changes it's focus state.
- */
-static void on_editable_change_focus(GDBusConnection *connection,
-        const char *sender_name, const char *object_path,
-        const char *interface_name, const char *signal_name,
-        GVariant *parameters, gpointer data)
-{
-    gboolean is_focused;
-    Client *c = (Client*)data;
-    g_variant_get(parameters, "(b)", &is_focused);
-
-    /* Don't change the mode if we are in pass through mode. */
-    if (c->mode->id == 'n' && is_focused) {
-        vb_enter(c, 'i');
-    } else if (c->mode->id == 'i' && !is_focused) {
-        vb_enter(c, 'n');
-    }
-    /* TODO allo strict-focus to ignore focus event for initial set focus */
-}
-
-/**
- * Listen to the VerticalScroll signal of the webextension and set the scroll
- * percent value on the client to update the statusbar.
- */
-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, gpointer data)
-{
-    Client *c = (Client*)data;
-    g_variant_get(parameters, "(tt)", &c->state.scroll_max, &c->state.scroll_percent);
-
-    vb_statusbar_update(c);
-}
-
 /**
  * Called when the web context created the page.
  *
@@ -248,13 +208,6 @@ static void on_web_extension_page_created(GDBusConnection *connection,
         /* Set the dbus proxy on the right client based on page id. */
         p->dbusproxy = (GDBusProxy*)data;
 
-        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);
-        g_dbus_connection_signal_subscribe(connection, NULL,
-                VB_WEBEXTENSION_INTERFACE, "EditableChangeFocus",
-                VB_WEBEXTENSION_OBJECT_PATH, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
-                (GDBusSignalCallback)on_editable_change_focus, p, NULL);
+        /* TODO Subscribe to debus signals here. */
     }
 }
index f1a12e8..8ae8f52 100644 (file)
@@ -91,6 +91,8 @@ static void vimb_cleanup(void);
 #endif
 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);
 
 struct Vimb vb;
 
@@ -1554,9 +1556,34 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview)
         webkit_user_script_unref(script);
     }
 
+    /* Inject the global utility script. */
+    script = webkit_user_script_new(VIMB_UTIL,
+            WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+            WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, NULL, NULL);
+    webkit_user_content_manager_add_script(ucm, script);
+    webkit_user_script_unref(script);
+
+    /* 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);
+
     return new;
 }
 
+static void on_script_message_focus(WebKitUserContentManager *manager,
+        WebKitJavascriptResult *message, Client *c)
+{
+    gboolean is_focused;
+
+    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');
+    } else if (c->mode->id == 'i' && !is_focused) {
+        vb_enter(c, 'n');
+    }
+}
+
 int main(int argc, char* argv[])
 {
     Client *c;
diff --git a/src/scripts/vimb_util.js b/src/scripts/vimb_util.js
new file mode 100644 (file)
index 0000000..df747fa
--- /dev/null
@@ -0,0 +1,38 @@
+/* Utility object injected into all frames. */
+var vimb = Object.freeze((function(){
+    'use strict';
+    /* Checks if given element is editable */
+    function isEditable(e) {
+        if (e) {
+            var name = e.tagName.toLowerCase();
+            return e.isContentEditable
+                || name == "textarea"
+                || (name == "input" && /^(?:color|date|datetime|datetime-local|email|month|number|password|search|tel|text|time|url|week)$/i.test(e.type));
+        }
+        return false;
+    }
+
+    return {
+        init: function() {
+            var b = document.body;
+            b.addEventListener(
+                "focus",
+                function(ev) {
+                    var e = ev.target, n;
+                    if (isEditable(e)) {
+                        window.webkit.messageHandlers.focus.postMessage(1);
+                    }
+                },
+                true
+            );
+            b.addEventListener(
+                "blur",
+                function() {
+                    window.webkit.messageHandlers.focus.postMessage(0);
+                },
+                true
+            );
+        }
+    };
+})());
+vimb.init();
index 29557f2..3430a58 100644 (file)
@@ -20,6 +20,7 @@
 #include <ctype.h>
 #include <fcntl.h>
 #include <glib.h>
+#include <JavaScriptCore/JavaScript.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <string.h>
@@ -414,6 +415,35 @@ gboolean util_filename_fill_completion(Client *c, GtkListStore *store, const cha
     return found;
 }
 
+char *util_js_result_as_string(WebKitJavascriptResult *result)
+{
+    JSValueRef value;
+    JSStringRef string;
+    size_t len;
+    char *retval = NULL;
+
+    value  = webkit_javascript_result_get_value(result);
+    string = JSValueToStringCopy(webkit_javascript_result_get_global_context(result),
+                value, NULL);
+
+    len = JSStringGetMaximumUTF8CStringSize(string);
+    if (len) {
+        retval = g_malloc(len);
+        JSStringGetUTF8CString(string, retval, len);
+    }
+    JSStringRelease(string);
+
+    return retval;
+}
+
+double util_js_result_as_number(WebKitJavascriptResult *result)
+{
+    JSValueRef value = webkit_javascript_result_get_value(result);
+
+    return JSValueToNumber(webkit_javascript_result_get_global_context(result), value,
+        NULL);
+}
+
 /**
  * Reads given input and try to parse ~/, ~user, $VAR or ${VAR} expansion
  * from the start of the input and moves the input pointer to the first
index ca9ba68..a9fd042 100644 (file)
@@ -43,6 +43,8 @@ GList *util_file_to_unique_list(const char *filename, Util_Content_Func func,
         guint max_items);
 gboolean util_fill_completion(GtkListStore *store, const char *input, GList *src);
 gboolean util_filename_fill_completion(Client *c, GtkListStore *store, const char *input);
+char *util_js_result_as_string(WebKitJavascriptResult *result);
+double util_js_result_as_number(WebKitJavascriptResult *result);
 gboolean util_parse_expansion(Client *c, const char **input, GString *str,
         int flags, const char *quoteable);
 char *util_str_replace(const char* search, const char* replace, const char* string);
index ae1fe72..97e4261 100644 (file)
@@ -36,13 +36,13 @@ static void add_onload_event_observers(WebKitDOMDocument *doc,
 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);
+#if 0
 static void dbus_emit_signal(const char *name, WebKitWebExtension* extension,
         GVariant *data);
+#endif
 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);
 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,
@@ -57,9 +57,6 @@ static const GDBusInterfaceVTable interface_vtable = {
 static const char introspection_xml[] =
     "<node>"
     " <interface name='" VB_WEBEXTENSION_INTERFACE "'>"
-    "  <signal name='EditableChangeFocus'>"
-    "   <arg type='b' name='focused' direction='out'/>"
-    "  </signal>"
     "  <method name='FocusInput'>"
     "  </method>"
     "  <signal name='PageCreated'>"
@@ -180,6 +177,7 @@ static void on_dbus_connection_created(GObject *source_object,
 static void add_onload_event_observers(WebKitDOMDocument *doc,
         WebKitWebExtension *extension)
 {
+#if 0 /* might soon be use for some events */
     WebKitDOMEventTarget *target;
 
     /* Add the document to the table of known documents or if already exists
@@ -192,15 +190,7 @@ static void add_onload_event_observers(WebKitDOMDocument *doc,
      * function is called with content document of an iframe. Else the event
      * observing does not work. */
     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);
-    webkit_dom_event_target_add_event_listener(target, "blur",
-            G_CALLBACK(on_editable_change_focus), TRUE, extension);
-    /* 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);
+#endif
 }
 
 /**
@@ -255,6 +245,7 @@ static void queue_page_created_signal(guint64 pageid)
     ext.page_created_signals = g_array_append_val(ext.page_created_signals, pageid);
 }
 
+#if 0
 /**
  * Emits a signal over dbus.
  *
@@ -279,6 +270,7 @@ static void dbus_emit_signal(const char *name, WebKitWebExtension* extension,
         g_error_free(error);
     }
 }
+#endif
 
 /**
  * Handle dbus method calls.
@@ -304,52 +296,6 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender,
     }
 }
 
-/**
- * Callback called if a editable element changes it focus state.
- * Event target may be a WebKitDOMDocument (in case of iframe) or a
- * WebKitDOMDOMWindow.
- */
-static void on_editable_change_focus(WebKitDOMEventTarget *target,
-        WebKitDOMEvent *event, WebKitWebExtension *extension)
-{
-    gboolean input_focus;
-    WebKitDOMDocument *doc;
-    WebKitDOMElement *active;
-
-    if (WEBKIT_DOM_IS_DOM_WINDOW(target)) {
-        g_object_get(target, "document", &doc, NULL);
-    } else {
-        /* target is a doc document */
-        doc = WEBKIT_DOM_DOCUMENT(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) {
-        return;
-    }
-    if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT(active)) {
-        WebKitDOMHTMLIFrameElement *iframe;
-        WebKitDOMDocument *subdoc;
-
-        iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT(active);
-        subdoc = webkit_dom_html_iframe_element_get_content_document(iframe);
-        add_onload_event_observers(subdoc, extension);
-        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;
-
-        dbus_emit_signal("EditableChangeFocus", extension,
-                g_variant_new("(b)", input_focus));
-    }
-}
-
 /**
  * Callback for web extensions page-created signal.
  */