From 23bfe6daa2bfb960397a1e0d6252382c0c1730eb Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sun, 1 Apr 2018 15:09:56 +1200 Subject: [PATCH] Greasemonkey: fix window proxy for new attributes Previously scripts were failing to find attributes that they assigned to window and then tried to use from the global scope. Eg window.newthing = function() {...}; newthing(...); // newthing is not defined error This wasn't the case for things that already existed in the global scope and were just being overwritten. This change just overrides the `Proxy.has()` function which seems to fix it. Probably the `while` implementation was failing to pick up new attributes because of the lack. I also tweaked some comments and variable names and const-ness to be a little more production ready. --- .../javascript/greasemonkey_wrapper.js | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/qutebrowser/javascript/greasemonkey_wrapper.js b/qutebrowser/javascript/greasemonkey_wrapper.js index e9cdc9f1a..88024ff02 100644 --- a/qutebrowser/javascript/greasemonkey_wrapper.js +++ b/qutebrowser/javascript/greasemonkey_wrapper.js @@ -161,13 +161,16 @@ * So let's try to use a Proxy on window and the possibly deprecated * `with` function to make that proxy shadow the global scope. * unsafeWindow should still be the actual global page window. + * + * There are other Proxy functions that we may need to override. + * set, get and has are definitely required. */ const unsafeWindow = window; - let myWindow = {}; - var windowProxyHandler = { + const qute_gm_window_shadow = {}; // stores local changes to window + const qute_gm_windowProxyHandler = { get: function(obj, prop) { - if (prop in myWindow) - return myWindow[prop]; + if (prop in qute_gm_window_shadow) + return qute_gm_window_shadow[prop]; if (prop in obj) { if (typeof obj[prop] === 'function' && typeof obj[prop].prototype == 'undefined') // Getting TypeError: Illegal Execution when callers try to execute @@ -178,20 +181,28 @@ } }, set: function(target, prop, val) { - return myWindow[prop] = val; + return qute_gm_window_shadow[prop] = val; + }, + has: function(target, key) { + return key in qute_gm_window_shadow || key in target; } }; - var myProxy = new Proxy(unsafeWindow, windowProxyHandler); + const qute_gm_window_proxy = new Proxy(unsafeWindow, qute_gm_windowProxyHandler); - // ====== The actual user script source ====== // - with (myProxy) { - // can't assign window directly in with() scope because proxy doesn't - // allow assinging to things that a readonly on the target. - function blarg() { // why can't this be anonymous? - var window = myProxy; + with (qute_gm_window_proxy) { + // We can't return `this` or `qute_gm_window_proxy` from + // `qute_gm_window_proxy.get('window')` because the Proxy implementation + // does typechecking on read-only things. So we have to shadow `window` + // more conventionally here. Except we can't do it directly within + // with `with` scope because then it would get assigned to the + // proxy and we would get the same problem, so we have to make yet + // another nested scope. + function qute_gm_window_scope() { // why can't this be anonymous? + let window = qute_gm_window_proxy; + // ====== The actual user script source ====== // {{ scriptSource }} + // ====== End User Script ====== // }; - blarg(); + qute_gm_window_scope(); }; - // ====== End User Script ====== // })();