Make hints wotk in "new window" mode
authorVirgil Dupras <hsoft@hardcoded.net>
Tue, 2 May 2017 00:34:53 +0000 (20:34 -0400)
committerDaniel Carl <danielcarl@gmx.de>
Sat, 6 May 2017 23:31:56 +0000 (01:31 +0200)
The old "set target to _blank" trick doesn't work anymore since the
whole "noopener" vulnerability thing
(https://mathiasbynens.github.io/rel-noopener/)

I couldn't find a workaround. We're unfortunately now limited in the
kind of elements we can interact with in "new window" mode.

src/hints.c
src/hints.h
src/normal.c
src/scripts/hints.js

index 3cd9e5f..8a5f921 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "config.h"
 #include <string.h>
+#include <webkit2/webkit2.h>
 #include <JavaScriptCore/JavaScript.h>
 #include <gdk/gdkkeysyms.h>
 #include <gdk/gdkkeysyms-compat.h>
 #include "ext-proxy.h"
 
 static struct {
-    JSObjectRef    obj;       /* the js object */
     char           mode;      /* mode identifying char - that last char of the hint prompt */
     int            promptlen; /* length of the hint prompt chars 2 or 3 */
     gboolean       gmode;     /* indicate if the hints 'g' mode is used */
-    JSContextRef   ctx;
     /* holds the setting if JavaScript can open windows automatically that we
      * have to change to open windows via hinting */
     gboolean       allow_open_win;
@@ -45,7 +44,7 @@ static struct {
 
 extern struct Vimb vb;
 
-static void call_hints_function(Client *c, const char *func, char* args);
+static void call_hints_function(Client *c, const char *func, const char* args);
 static void fire_timeout(Client *c, gboolean on);
 static gboolean fire_cb(gpointer data);
 
@@ -89,17 +88,15 @@ void hints_clear(Client *c)
 
         call_hints_function(c, "clear", "");
 
-        /* g_signal_emit_by_name(c->webview, "hovering-over-link", NULL, NULL); */
-
         /* if open window was not allowed for JavaScript, restore this */
-        /* if (!hints.allow_open_win) {                                                                                  */
-        /*     WebKitWebSettings *setting = webkit_web_view_get_settings(c->webview);                                    */
-        /*     g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", hints.allow_open_win, NULL); */
-        /* }                                                                                                             */
+        if (!hints.allow_open_win) {
+            WebKitSettings *setting = webkit_web_view_get_settings(c->webview);
+            g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", hints.allow_open_win, NULL);
+        }
     }
 }
 
-void hints_create(Client *c, char *input)
+void hints_create(Client *c, const char *input)
 {
     char *jsargs;
 
@@ -115,16 +112,16 @@ void hints_create(Client *c, char *input)
     if (!(c->mode->flags & FLAG_HINTING)) {
         c->mode->flags |= FLAG_HINTING;
 
-        /* WebKitWebSettings *setting = webkit_web_view_get_settings(c->webview); */
+        WebKitSettings *setting = webkit_web_view_get_settings(c->webview);
 
         /* before we enable JavaScript to open new windows, we save the actual
          * value to be able restore it after hints where fired */
-        /* g_object_get(G_OBJECT(setting), "javascript-can-open-windows-automatically", &(hints.allow_open_win), NULL); */
+        g_object_get(G_OBJECT(setting), "javascript-can-open-windows-automatically", &(hints.allow_open_win), NULL);
 
         /* if window open is already allowed there's no need to allow it again */
-        /* if (!hints.allow_open_win) {                                                                  */
-        /*     g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", true, NULL); */
-        /* }                                                                                             */
+        if (!hints.allow_open_win) {
+            g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", true, NULL);
+        }
 
         hints.promptlen = hints.gmode ? 3 : 2;
 
@@ -133,7 +130,7 @@ void hints_create(Client *c, char *input)
             hints.gmode ? "true" : "false",
             MAXIMUM_HINTS,
             GET_CHAR(c, "hintkeys"),
-            GET_BOOL(c, "hint-follow-last") ? "true" : "false",      
+            GET_BOOL(c, "hint-follow-last") ? "true" : "false",
             GET_BOOL(c, "hint-number-same-length") ? "true" : "false"
         );
 
@@ -145,8 +142,10 @@ void hints_create(Client *c, char *input)
         return;
     }
 
-    jsargs = *(input + hints.promptlen) ? input + hints.promptlen : "";
-    call_hints_function(c, "filter", jsargs);
+    call_hints_function(
+        c, "filter",
+        *(input + hints.promptlen) ? input + hints.promptlen : ""
+    );
 }
 
 void hints_focus_next(Client *c, const gboolean back)
@@ -161,6 +160,11 @@ void hints_fire(Client *c)
 
 void hints_follow_link(Client *c, const gboolean back, int count)
 {
+    /* TODO implement outside of hints.c */
+    /* We would previously "piggyback" on hints.js for the "js" part of this feature
+     * but this would actually be more elegant in its own JS file. This has nothing
+     * to do with hints.
+     */
     /* char *json = g_strdup_printf(                            */
     /*     "[%s]",                                              */
     /*     back ? vb.config.prevpattern : vb.config.nextpattern */
@@ -249,8 +253,7 @@ static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Cli
     gboolean success = FALSE;
     char *value = NULL;
 
-    g_print("callback!\n");
-    GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL);   
+    GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL);
     if (!return_value) {
         return;
     }
@@ -259,19 +262,11 @@ static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Cli
     if (!success) {
         return;
     }
-    g_print("foo! %s\n", value);
 
     if (!strncmp(value, "ERROR:", 6)) {
         return;
     }
 
-    if (!strncmp(value, "OVER:", 5)) {
-        /* g_signal_emit_by_name(                                                                */
-        /*     c->webview, "hovering-over-link", NULL, *(value + 5) == '\0' ? NULL : (value + 5) */
-        /* );                                                                                    */
-        return;
-    }
-
     /* following return values mark fired hints */
     if (!strncmp(value, "DONE:", 5)) {
         fire_timeout(c, false);
@@ -346,7 +341,7 @@ static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Cli
     }
 }
 
-static void call_hints_function(Client *c, const char *func, char* args)
+static void call_hints_function(Client *c, const char *func, const char* args)
 {
     char *jscode;
 
@@ -367,7 +362,6 @@ static void fire_timeout(Client *c, gboolean on)
 
     if (on) {
         millis = GET_INT(c, "hint-timeout");
-        g_print("millis %d", millis);
         if (millis) {
             hints.timeout_id = g_timeout_add(millis, (GSourceFunc)fire_cb, c);
         }
index 1b23e86..6821554 100644 (file)
@@ -23,7 +23,7 @@
 #include "main.h"
 
 VbResult hints_keypress(Client *c, int key);
-void hints_create(Client *c, char *input);
+void hints_create(Client *c, const char *input);
 void hints_fire(Client *c);
 void hints_follow_link(Client *c, gboolean back, int count);
 void hints_increment_uri(Client *c, int count);
index 156ef52..73c9a3a 100644 (file)
@@ -621,7 +621,7 @@ static VbResult normal_pass(Client *c, const NormalCmdInfo *info)
 
 static VbResult normal_prevnext(Client *c, const NormalCmdInfo *info)
 {
-#if 0 /* TODO need hinting to be available */
+#if 0 /* TODO implement outside of hints.js */
     int count = info->count ? info->count : 1;
     if (info->key2 == ']') {
         hints_follow_link(FALSE, count);
index 6f3530f..c381e45 100644 (file)
@@ -397,18 +397,15 @@ var hints = Object.freeze((function(){
 
     /* internal used methods */
     function open(e, newWin) {
-        var oldTarget = e.target;
-        if (newWin) {
-            /* set target to open in new window */
-            e.target = "_blank";
-        } else if (e.target === "_blank") {
-            e.removeAttribute("target");
+        if (newWin && e.hasAttribute('href')) {
+            /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's
+             * target to _blank. Therefore, we can't simulate ctrl-click through _blank like we
+             * used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a
+             * simple <a> link. In other cases, we fire the even normally.
+             */
+             window.open(e.getAttribute('href'), '_blank');
         }
-        /* to open links in new window the mouse events are fired with ctrl */
-        /* key - otherwise some ugly pages will ignore this attribute in their */
-        /* mouse event observers like duckduckgo */
-        click(e, newWin);
-        e.target = oldTarget;
+        click(e);
     }
 
     /* set focus on hint with given index valid hints array */
@@ -424,24 +421,22 @@ var hints = Object.freeze((function(){
             activeHint.e.classList.add(fClass);
             activeHint.label.classList.add(fClass);
             mouseEvent(activeHint.e, "mouseover");
-
-            return "OVER:" + getSrc(activeHint.e);;
         }
     }
 
-    function click(e, ctrl) {
-        mouseEvent(e, "mouseover", ctrl);
-        mouseEvent(e, "mousedown", ctrl);
-        mouseEvent(e, "mouseup", ctrl);
-        mouseEvent(e, "click", ctrl);
+    function click(e) {
+        mouseEvent(e, "mouseover");
+        mouseEvent(e, "mousedown");
+        mouseEvent(e, "mouseup");
+        mouseEvent(e, "click");
     }
 
-    function mouseEvent(e, name, ctrl) {
+    function mouseEvent(e, name) {
         var evObj = e.ownerDocument.createEvent("MouseEvents");
         evObj.initMouseEvent(
             name, true, true, e.ownerDocument.defaultView,
             0, 0, 0, 0, 0,
-            (typeof ctrl != "undefined") ? ctrl : false, false, false, false, 0, null
+            false, false, false, false, 0, null
         );
         e.dispatchEvent(evObj);
     }