3

I'm maintaining a Greasemonkey script and got some trouble due to Mozilla's change to the unsafeWindow API in Firefox 30.

The page my script runs on, triggers an event "MyEvent" and my script is interested in that event.

The event is fired using jQuery 1.6.4

Before, I used this code to hook into this event:

var jQuery = unsafeWindow.jQuery;
jQuery(unsafeWindow.document)
    .bind("MyEvent", function() {
        console.log("MyEvent Triggered!");
    });

But due to Mozilla's change this won’t work anymore.

I tried to insert my own jQuery in conflict-free mode but I don't think this can access events that are triggered by the other jQuery instance?

Any ideas how I could hook into this event?

Brock Adams
  • 90,639
  • 22
  • 233
  • 295
Reini
  • 1,233
  • 3
  • 19
  • 34

2 Answers2

3

The quick and dirty way to do this, if you don't need any GM_ functions and you don't @require your own jQuery, is to use @grant none mode. This works:

// ==UserScript==
// @name     _unsafeWindow tests
// @include  http://jsbin.com/xaman/*
// @grant    none
// ==/UserScript==

var jQuery = window.jQuery;
jQuery(document).bind ("MyEvent", function () {
    console.log ("From GM script: MyEvent caught!");
} );

If you did need GM_ functions, you can sometimes use the new exportFunction().
Unfortunately, jQuery and jQuery event handling is a special case. Depending on what you try, you will get error messages like:

Permission denied to access property 'handler'
or
CloneNonReflectorsWrite error

I've simply found no way to do this using any of the new unsafeWindow functionality. Your only recourse is to inject the code. Like so:

// ==UserScript==
// @name     _unsafeWindow tests
// @include  http://jsbin.com/xaman/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @grant    GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a design change
    introduced in GM 1.0.   It restores the sandbox.
*/
function myEventHandler (zEvent) {
    console.log (
        'From GM script: "' + zEvent.type + '" triggered on ', zEvent.target
    );
}

function bindMyEvent () {
    //-- Gets "jQuery is not defined" if GM script does not also use jQuery.
    jQuery(document).bind ("MyEvent", myEventHandler);
    console.log ("The jQuery version being used is: ", jQuery.fn.jquery);
}

//-- Create A COPY OF myEventHandler in the target page scope:
addJS_Node (myEventHandler);
//-- Create A COPY OF bindMyEvent in the target page scope and immediately run it.
addJS_Node (null, null, bindMyEvent);

function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
    var D                                   = document;
    var scriptNode                          = D.createElement ('script');
    if (runOnLoad) {
        scriptNode.addEventListener ("load", runOnLoad, false);
    }
    scriptNode.type                         = "text/javascript";
    if (text)       scriptNode.textContent  = text;
    if (s_URL)      scriptNode.src          = s_URL;
    if (funcToRun)  scriptNode.textContent  = '(' + funcToRun.toString() + ')()';

    var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
    targ.appendChild (scriptNode);
}

You can test both of these scripts against this jsBin page.


If you need to run/call GM_ functions from within the injected event handler(s), use techniques shown in "How to call Greasemonkey's GM_ functions from code that must run in the target page scope? ".

Community
  • 1
  • 1
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
  • I'm not sure the OP is really talking about GreaseMonkey - the blog post linked is about content scripts in the Add-on SDK. – Wladimir Palant Jul 18 '14 at 08:07
  • @WladimirPalant, that's because Greasemonkey 2.0 copied the SDK changes and referenced that SDK blog post for the workarounds. See [the Greasemonkey 2.0 announcement](http://www.greasespot.net/2014/06/greasemonkey-20-release.html). Most GM users can temporarily fix these unsafeWindow problems by reverting to previous GM versions -- at least for FF 30. – Brock Adams Jul 18 '14 at 08:29
  • The OP is rally talking about GreaseMonkey! @BrockAdams AWESOME! The second snipped is kind of a monster but so is this script I have to maintain here. Works like a charm. Thank you very much! – Reini Jul 18 '14 at 13:04
1

Just don't bind event handlers on unsafeWindow, use the regular window object instead:

window.document.addEventListener("MyEvent", function() {
  console.log("MyEvent Triggered!");
}, false, true);

Note the fourth parameter (wantsUntrusted) to addEventListener - this one allows your event handler to receive untrusted events.

Wladimir Palant
  • 56,865
  • 12
  • 98
  • 126
  • This does not work on custom events triggered by jQuery. Test it against [jsbin.com/xaman/1](http://jsbin.com/xaman/1). Also, the `wantsUntrusted` parameter is not needed for/with Greasemonkey scripts. – Brock Adams Jul 18 '14 at 07:19
  • @BrockAdams: You are right, jQuery implements an "alternative" event dispatching mechanism - I forgot about that huge WTF. One needs to dispatch events properly of course. – Wladimir Palant Jul 18 '14 at 07:43
  • Yes, but the OP can't control how the target page, or jQuery, dispatches the events. – Brock Adams Jul 18 '14 at 07:46
  • A soloution short like this would have been awesome! But as Brock already said, I have no control over how the page dispachtes the events. – Reini Jul 18 '14 at 13:03