Proper way of using new Firefox 30 sandbox API

635 views
Skip to first unread message

TheVindicar

unread,
Jul 12, 2014, 8:57:16 AM7/12/14
to greasemon...@googlegroups.com
Where can I read about writing scripts in accordance with new API? Since now I can't even iterate over page-defined jQuery collection using script-defined callback, every single one of my scripts is broken.

Pretty much all of them use the same scheme:
1. load settings into object via GM_getValue()
2. find and modify some DOM nodes according to the settings
3. (optionally) replace some functions in the target page with my own versions

Now I fail to understand how do I do that using cloneInto() and exportFunction(). The fact Firefox shows wrong line number in error message doesn't help either.

Dr Sr

unread,
Jul 14, 2014, 7:33:25 PM7/14/14
to greasemon...@googlegroups.com
Same here, half a dozen scripts broken. The easiest way is to use "@grant none", so your script runs in the page context and can carry on as before. Replace "unsafeWindow" with "window". You lose access to all the GM_ calls, but if you're only using GM_getValue()/setValue() anyway, see the compatibility shim in the above link for a drop-in replacement that uses localStorage.

Vindicar

unread,
Jul 15, 2014, 7:53:50 AM7/15/14
to greasemon...@googlegroups.com
Well, I solved it in a different manner. I run everything I can in script scope, load settings into an object, then export that object into unsafeWindow via cloneInto() and run the rest in page scope via script tag injection.
It works for simple scripts that don't need to call priviledged calls from pages scope. If you do need it, you might want to use addEventListener()/postMessage() combo.


--
You received this message because you are subscribed to a topic in the Google Groups "greasemonkey-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/greasemonkey-users/SXfpyvcQofQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to greasemonkey-us...@googlegroups.com.
To post to this group, send email to greasemon...@googlegroups.com.
Visit this group at http://groups.google.com/group/greasemonkey-users.
For more options, visit https://groups.google.com/d/optout.

Daniel Wynalda

unread,
Jul 16, 2014, 12:03:17 PM7/16/14
to greasemon...@googlegroups.com
If possible could you post an example of how you used cloneInto() in your greasemonkey script to inject something?  My scripts also broke with greasemonkey 2.0 deployment and they are all failing inside jQuery so I don't know what to do to try to fix them.   Simple things like:

$(this).click(); 

no longer work -- and Firefox is giving this error:
Permission denied to access property 'length'.

I know it's finding the item (I have logged the content).  It works fine in Greasemonkey 1.5.   I am guessing this has something to do with the scope changes - but I have no idea how to work around them.  I'm not trying to inject anything into the web page.  I'm just trying to click a DIV element....



On Tuesday, July 15, 2014 7:53:50 AM UTC-4, TheVindicar wrote:
Well, I solved it in a different manner. I run everything I can in script scope, load settings into an object, then export that object into unsafeWindow via cloneInto() and run the rest in page scope via script tag injection.
It works for simple scripts that don't need to call priviledged calls from pages scope. If you do need it, you might want to use addEventListener()/postMessage() combo.
2014-07-15 3:33 GMT+04:00 Dr Sr <drsrfro...@gmail.com>:
Same here, half a dozen scripts broken. The easiest way is to use "@grant none", so your script runs in the page context and can carry on as before. Replace "unsafeWindow" with "window". You lose access to all the GM_ calls, but if you're only using GM_getValue()/setValue() anyway, see the compatibility shim in the above link for a drop-in replacement that uses localStorage.

On Sunday, July 13, 2014 12:57:16 AM UTC+12, TheVindicar wrote:
Where can I read about writing scripts in accordance with new API? Since now I can't even iterate over page-defined jQuery collection using script-defined callback, every single one of my scripts is broken.

Pretty much all of them use the same scheme:
1. load settings into object via GM_getValue()
2. find and modify some DOM nodes according to the settings
3. (optionally) replace some functions in the target page with my own versions

Now I fail to understand how do I do that using cloneInto() and exportFunction(). The fact Firefox shows wrong line number in error message doesn't help either.

--
You received this message because you are subscribed to a topic in the Google Groups "greasemonkey-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/greasemonkey-users/SXfpyvcQofQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to greasemonkey-users+unsub...@googlegroups.com.

Vindicar

unread,
Jul 17, 2014, 5:46:48 AM7/17/14
to greasemon...@googlegroups.com
Let me guess, you're trying to use jQuery provided by target page? Following code will fail:

  var $ = unsafeWindow.jQuery;
  $.each([], function(){});

Since $.each() is a function from page scope, and array and function are both in more priviledged script scope, jQuery will fail to access their properties (like Array.length and Function.call).
There are two ways to work around it. Either you can export everything you need explicitly (though strings and numbers don't have to be exported):

  $.each(
      cloneInto([], unsafeWindow),
      exportFunction(function(){}, unsafeWindow)
  );

Or you can run the page-modifying code entirely inside the page scope via script tag injection:

  function RunInPage(func) {
      var s = document.createElement("script");
      s.textContent = "(" + func + ")();";
      document.body.appendChild(s);
      setTimeout(function(){document.body.removeChild(s)}, 0);
  }
  RunInPage(function(){
      //you can use page-scope code (like jQuery if the page uses it) here freely - entirety of this script is run in page scope.
      var $ = window.jQuery;    $.each([], function(){});
  });


The latter method is much easier if you don't need to make priviledged calls from the page scope (i.e. you don't have an event handler that calls GM_setValue(), don't use GM_xmlHttpRequest(), and so on). One drawback is that you can't use closures with it. Following code will give you 'undefined':

  var my_settings = {settings : GM_getValue( 'settings', 'default' )};
  RunInPage(function(){
      alert(my_settings); 
  });

It seems that a good way to pass values into page scope would be this:

  var my_settings = {settings : GM_getValue( 'settings', 'default' )}
  unsafeWindow.my_settings = cloneInto(my_settings, unsafeWindow);
  RunInPage(function(){
      alert(window.my_settings);
  });


Mind that settings object better not include any functions.


To unsubscribe from this group and all its topics, send an email to greasemonkey-us...@googlegroups.com.

Daniel Wynalda

unread,
Jul 17, 2014, 7:42:45 AM7/17/14
to greasemon...@googlegroups.com
This is why I find it strange I'm having this problem.  I have a version of jQuery right in my script as a subroutine.  I basically have a main function that is called and inside it jQuery and all of my other functions exist.   I *believe* it's calling the jQuery i have provided and trying to access the page and the each seems to find all of the elements but the $(this).click() is failing with the unable to access property length.  

There is zero use of unsafeWindow.jQuery in my script.  I do have an occasional call to a subroutine that already exists in the page (but I'm calling the function outright, and that seems to be working).    This specific issue seems to be that the .click() function cannot access the item that is in the page from within the greasemonkey script.    Perhaps I'm misinterpreting it - but if I debug log messages before and after it is definitely that line that is causing greasemonkey to error and stop.

To unsubscribe from this group and all its topics, send an email to greasemonkey-users+unsubscribe@googlegroups.com.
To post to this group, send email to greasemon...@googlegroups.com.

Vindicar

unread,
Jul 17, 2014, 8:10:23 AM7/17/14
to greasemon...@googlegroups.com
Not entirely sure what causes that, but it looks like a scope bug to me. That's why I moved everything I could into page scope.


To unsubscribe from this group and all its topics, send an email to greasemonkey-us...@googlegroups.com.

Daniel Wynalda

unread,
Jul 17, 2014, 8:19:10 AM7/17/14
to greasemon...@googlegroups.com
Ok.  Some additional debugging.   The .click() routine is definitely not working but I tried creating an 'xclick' function in my script that calls the actual mouse functions.   It appears that I *can* xclick the DIV using the xclick routine below:

function xclick(elm)
{
 var evt=document.createEvent('MouseEvents');
evt.initMouseEvent('click',true,true,window,0,1,1,1,1, false, false, false, false, 0, null);
}

but when I try to .click the same element using jQuery it's not working. 

Oddly if I put the routine in a setTimeout it won't do the xclick or the click - so it's like the element is no longer there after the timeout of 3 seconds.   What I find odd is that the code works perfectly in greasemonkey 1.5 - but in 2.0 it's like there's cleanup going on or something that is whacking the element that was grabbed by jQuery.     

Perhaps there's some new Greasemonkey garbage cleanup taking place that I was assuming didn't take place.. 

Either way .click() is broken and jQuery crashes with the .length being inaccessible and I just don't know how to move it into the script scope.   The code you gave assumed I was using unsafeWindow but I'm not at all in this section...  The only unsafeWindow references I have call functions in the unsafeWindow and those seem to still be working.

Daniel Wynalda

unread,
Jul 17, 2014, 8:23:21 AM7/17/14
to greasemon...@googlegroups.com
I think what I'm having trouble with is understanding the use of cloneInto.

You mentioned this code:
 $.each( 
      cloneInto([], unsafeWindow),
      exportFunction(function(){}, unsafeWindow)
  );

But I don't understand it.  Is this suppose to replace the .each() inside jQuery?  

The .each seems to be running fine and I get the content that I expect with jquery (the text is visible, as is the .onClick routine that is in the code).   What I can't do is actually click the element (perhaps because it has an onclick and that is in the page scope??)


On Thursday, July 17, 2014 5:46:48 AM UTC-4, TheVindicar wrote:
To unsubscribe from this group and all its topics, send an email to greasemonkey-users+unsubscribe@googlegroups.com.
To post to this group, send email to greasemon...@googlegroups.com.

Vindicar

unread,
Jul 17, 2014, 12:51:59 PM7/17/14
to greasemon...@googlegroups.com
I used this snippet to illustrate my undestanding of how scurity scopes work.
jQuery is taken from unsafeWindow, and thus runs with page level of privilege.
The code itself is run in script scope, so empty array and anonymous function are also created in script scope.
That's why page-defined jQuery fails to access them.

You're using script-scope jQuery, so it should work just fine in my scenario.
As for yours... say again:
1. Where and how is click handler installed?
2. Where and how is .click() called?


To unsubscribe from this group and all its topics, send an email to greasemonkey-us...@googlegroups.com.
Message has been deleted

Daniel Wynalda

unread,
Jul 17, 2014, 6:42:26 PM7/17/14
to greasemon...@googlegroups.com
Here's a copy of a sample script. 
http://hithuntheal.com/x.user.js 
(also inserted/pasted below) 


It acts on the following URL:  
http://hithuntheal.com/x.php
It simply clicks the button.  (I've reduced my 50000 line script down to just the sample code showing the issue).
But this script won't click the button.  It finds the button -- it can see the button's properties.  But it can't click the button as jQuery ends with the error.

Now... If you remove the @grant lines and replace them with @grant none then this script will work just fine.  
This is what's confusing me.  I'm not doing anything exotic in this script sample.  I'm clicking a button.  I can see the button, I can get properties from the button.  I simply can't click the button.
My script has to have GM_getValue and GM_setValue because it runs cross domain and the entire value of the script is maintaining data between websites to use on other websites... 

// ==UserScript==
// @name           TEST CLICK BUTTON
// @namespace      http://hithuntheal.com/x.user.js
// @description    Hitlist hunt heal and level combined
// @grant GM_setValue
// @grant GM_getValue
// @include *hithuntheal.com/*
// ==/UserScript==



jQuery(".formInputButton").each(function(index)
{
var thisvalue=$(this).attr('value');
if (thisvalue="Finish Job")
{
alert('found button !');
$(this).click();
}
});

Vindicar

unread,
Jul 18, 2014, 2:27:37 AM7/18/14
to greasemon...@googlegroups.com
 if (thisvalue="Finish Job")

I do believe you have a typo here, but it's unrelated to the fact click() causes an error. So far I dug into event.trigger() routine.


To unsubscribe from this group and all its topics, send an email to greasemonkey-us...@googlegroups.com.

Vindicar

unread,
Jul 18, 2014, 2:59:27 AM7/18/14
to greasemon...@googlegroups.com
Ok, I think I got to the bottom of this.
The problem is in jQuery.event.trigger(), in following lines:

	// Native handler
handle = ontype && cur[ ontype ];
if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
event.preventDefault();
}

handle() is a page-scope function, and jQuery tries to call it while passing script-scope data object as parameter.

handle.apply( cur, data )
Of course, it doesn't end well. I was unable to quickfix it by inserting a call to cloneInto(), as for some reason it returns an error when I attempt to export data.

You could file a bug to jQuery developers, but I'm unsure if they will try to fix it - after all, jQuery wasn't specifically intended to be used in userscripts.
It's not just click() - any time jQuery does something like this, you will run into problems.
So the only way to work around it I can think of is to inject jQuery into the target page and work there, using postMessage() or user-defined events to communicate with priviledged code...

Daniel Wynalda

unread,
Jul 18, 2014, 7:39:02 AM7/18/14
to greasemon...@googlegroups.com
Thank you for looking at this for me.  I do see that i missed an  = in the if.  thanks.  Quickie small script created to show the problem.
So if jQuery isn't allowed to touch things in the page could you show me an example of how one could click using jQuery in the page?
In this specific case the page itself already has jQuery too (so I've had to alias my jQuery) so I could call the jQuery in the page - if I knew how to do it.   

My basic problem remains the same - I don't understand how to call something in the page when I have @grant set.   If you could 
give me a really brief snippet I'm sure I could work from there to understand the rest of the items that need to modify things in the page.
Interestingly if I use jQuery to modify elements in the page I don't have any issue.   I change text, I rearrange things, etc.   But when
I do this specific click (and other clicks) it's failing.   I wouldn't mind using a homebrew (altered) version of jQuery in the script.  I already
have a number of patches to jQuery (plugins) included in the script.  Adding another replacement routine wouldn't be a big deal if I could
learn how to actually work in the page.  

Anthony Lieuallen

unread,
Jul 18, 2014, 9:24:38 AM7/18/14
to greasemon...@googlegroups.com
On Fri, Jul 18, 2014 at 7:39 AM, Daniel Wynalda <dwyn...@gmail.com> wrote:
> I don't understand how to call something in the page when I have @grant set.

http://wiki.greasespot.net/Location_hack ?

Vindicar

unread,
Jul 18, 2014, 10:56:38 AM7/18/14
to greasemon...@googlegroups.com
>So if jQuery isn't allowed to touch things in the page
Not quite... script scope jQuery CAN touch things in the page scope just fine, but if it tries to call functions from the page scope (callback, event handlers, whatever!) and feed them objects in the script scope (since it doesn't know about scopes), then error happens.


>Interestingly if I use jQuery to modify elements in the page I don't have any issue.   I change text, I rearrange things, etc.  
Regular DOM manipulations are fine, since all objects involved belong to page scope. So it's script scope function accessing page scope (which works since script scope is more privileged). Problems arise when it's the other way around - page scope function trying to access script scope.


>I could call the jQuery in the page - if I knew how to do it.
    var $ = unsafeWindow.jQuery;
That's all! You can work with that jQuery, but again, once you give it object, array, or function created in script scope, it will choke.

>My basic problem remains the same - I don't understand how to call something in the page when I have @grant set.
What do you mean by "call"?
You can call a function from the page just fine, as long as you give it only primitive values (strings and numbers) and objects from page scope (either exported or created there) to work with. So:

  unsafeWindow.PageFunction( {data:1} ); //error, object is in the script scope

  var myobj = new unsafeWindow.Object(); //creating object in page scope
  myobj['data'] = 1; //if value was an object/array, you'd have to export it too, or create in page scope as above
  unsafeWindow.PageFunction(myobj); //should work

  unsafeWindow.PageFunction( cloneInto({data:1}, unsafeWindow) ); //should work

If you mean "execute your code with privileges of page scope", though... On #greasemonkey at FreeNode I've been told that the most practical way to actually run code in page scope is to inject script tag into the page. Let me quote myself:

  function RunInPage(func) {
      var s = document.createElement("script");
      s.textContent = "(" + func + ")();";
      document.body.appendChild(s);
      setTimeout(function(){document.body.removeChild(s)}, 0);
  }
  RunInPage(function(){
      //you can use page-scope code (like jQuery if the page uses it) here freely - entirety of this script is run in page scope.
      var $ = window.jQuery;   
      $.each([], function(){});
  });

Exporting homebrew jQuery into the page would be hard, though... exportFunction() only lets you call function from page scope, but it doesn't seem to open access to any defined properties on that function. Of course, you could just inject another script tag to load your jQuery into page scope (don't forget to back up and restore original jQuery if page needs it), and then (if necessary) run some code to apply your patches to it, but I'm not sure how well it would work.




--
You received this message because you are subscribed to a topic in the Google Groups "greasemonkey-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/greasemonkey-users/SXfpyvcQofQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to greasemonkey-us...@googlegroups.com.

Daniel Wynalda

unread,
Jul 19, 2014, 7:05:24 AM7/19/14
to greasemon...@googlegroups.com
You obviously understand this way better than I do.  I usually think of myself as a fairly competent programmer but the talk about scope loses me I guess.  I am simply trying to click something in a page.  There is jQuery *in the page* and there is jQuery in my script (different instances).  I'm simply looking to find out how I can click the button.  

In my really basic example I have set up a webpage:

and I have set up a script to click on the button

I'm trying to get the script to click the button - and allow for GM_setValue/GM_getValue.

How can I click an element in the page?   I find it odd that I can get the onClick attribute just fine but I can't click the element. 

To unsubscribe from this group and all its topics, send an email to greasemonkey-users+unsub...@googlegroups.com.

Vindicar

unread,
Jul 19, 2014, 8:16:27 AM7/19/14
to greasemon...@googlegroups.com
If my understanding of how jQuery works is correct, you should try using jQuery from page scope.

        var $ = unsafeWindow.jQuery;   
        //export function is needed so page-scope jQuery can call script-scope callback we pass to .each()
        $(".formInputButton").each( exportFunction(function(index)
                {
                    var thisvalue=$(this).attr('value');
                    if (thisvalue=="CLICK ME")
                    {
                        alert('found button !: '+thisvalue);
                        $(this).click();
                    }
                   
                }, unsafeWindow) );

Could you please include jQuery into your test page so I could test it?


To unsubscribe from this group and all its topics, send an email to greasemonkey-us...@googlegroups.com.

Daniel Wynalda

unread,
Jul 19, 2014, 8:18:22 AM7/19/14
to greasemon...@googlegroups.com
I believe I have figured out how to make things work.  I don't think this is actually a problem with scope and script vs page permissions.  I think it's an issue with jQuery not doing something correctly.    I was able to replace my code that called $(object).click() with a function written by myself (in script scope obviously) that actually clicks the link.     That function runs properly, no error, and I was able to get the script functional again.  I didn't have to insert anything into the page and the script seems to be functioning well again. 

What I found odd was I was not asking to run anything inside the page that was trying to get back to the script level.  I was simply trying to click on a link in the page and jquery couldn't click it.   Perhaps jQuery tries to call itself recursively after the click or something - as a callback??  That might cause the click (in jQuery) to try to call back to the script scope? 

Anyway - I was able to solve the problem I was having with the script.  I do want to gain more understanding about how to use cloneInto.

I also found another change from GM 1.5 to GM 2.0 -- it appears that it's either leaner or cleans things up better because executing behavior on objects in the page in a setTimeout doesn't function the same way every time in both versions.  I had to explicitly trap all of my delayed clicks in enclosures to insure they stay defined.  The objects were still on the page but Greasemonkey appears to whack defined variables  before the setTimeout occurs now.  (the full scope of the script isn't available to the setTImeout routine). 

Vindicar

unread,
Jul 19, 2014, 8:44:40 AM7/19/14
to greasemon...@googlegroups.com
Well, I think that's because your click() function and jQuery.click() work differently.
You just emit click event and let browser do the rest, when jQuery for some reason (cross-browser contingency? I don't know) tries to call existing event handlers itself - and handlers installed in page scope can't access event object jQuery makes for them in script scope.

Anyway, good to hear you solved your problem. I only hope that my attempts at coding workaroudn will be useful if you run into something else later. Good luck!


To unsubscribe from this group and all its topics, send an email to greasemonkey-us...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages