Accessing Variables from Greasemonkey to Page & vice versa
Asked Answered
L

3

12

I have the following code in test.js which is run right before </body>:

alert('stovetop');
alert(greasy);

I have the following code in test.user.js:

(function () {

    'use strict';
    var greasy = 'greasy variable';
    document.title = 'greasy title';

}());

'stovetop' gets alerted so I know the page javascript works, and document.title gets changes so I know that the script javascript works. However, on the webpage I get the error:

Error: ReferenceError: greasy is not defined Source File: /test.js

How from the webpage do I access the variable set by Greasemonkey and how about vice versa?

Leucoma answered 21/11, 2012 at 1:41 Comment(1)
If you go this way, it'll only work in firefox as Chrome sandboxes userscripts.Konstance
S
32
  • Greasemonkey scripts operate in a separate scope and may also operate in a sandbox, depending on the @grant settings.

  • Additionally, the question code isolates greasy in a function scope (as gladoscc said).

  • Finally, by default, test.js will fire before the Greasemonkey script does, so it won't see any set variables, anyway. Use @run-at document-start to address that.


So, given this test.js, run right before </body>:

window.targetPages_GlobalVar = 'stovetop';

console.log ("On target page, local global: ", targetPages_GlobalVar);
console.log ("On target page, script global: ", gmScripts_GlobalVar);

Then the following will work:

No sandbox:

// ==UserScript==
// @name        _Greasemonkey and target page, variable interaction
// @include     http://YOUR_SERVER.COM/YOUR_PATH/*
// @include     http://output.jsbin.com/esikut/*
// @run-at      document-start
// @grant       none
// ==/UserScript==

//--- For @grant none, could also use window. instead of unsafeWindow.
unsafeWindow.gmScripts_GlobalVar = 'greasy';

console.log ("In GM script, local global: ", unsafeWindow.targetPages_GlobalVar);
console.log ("In GM script, script global: ", gmScripts_GlobalVar);

window.addEventListener ("DOMContentLoaded", function() {
    console.log ("In GM script, local global, after ready: ", unsafeWindow.targetPages_GlobalVar);
}, false);


With sandbox, no function scope, unsafeWindow:
==> Important update: Greasemonkey changed unsafeWindow handling with version 2.0, the next sample script will not work with GM 2.0 or later. The other two solutions still work.

// ==UserScript==
// @name        _Greasemonkey and target page, variable interaction
// @include     http://YOUR_SERVER.COM/YOUR_PATH/*
// @include     http://output.jsbin.com/esikut/*
// @run-at      document-start
// @grant    GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a design change
    introduced in GM 1.0.   It restores the sandbox.
*/

unsafeWindow.gmScripts_GlobalVar = 'greasy';

console.log ("In GM script, local global: ", unsafeWindow.targetPages_GlobalVar);
console.log ("In GM script, script global: ", unsafeWindow.gmScripts_GlobalVar);

window.addEventListener ("DOMContentLoaded", function() {
    console.log ("In GM script, local global, after ready: ", unsafeWindow.targetPages_GlobalVar);
}, false);


With sandbox, no function scope, Script Injection:

// ==UserScript==
// @name        _Greasemonkey and target page, variable interaction
// @include     http://YOUR_SERVER.COM/YOUR_PATH/*
// @include     http://output.jsbin.com/esikut/*
// @run-at      document-start
// @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 GM_main () {
    window.gmScripts_GlobalVar = 'greasy';

    console.log ("In GM script, local global: ", window.targetPages_GlobalVar);
    console.log ("In GM script, script global: ", window.gmScripts_GlobalVar);

    window.addEventListener ("DOMContentLoaded", function() {
        console.log ("In GM script, local global, after ready: ", window.targetPages_GlobalVar);
    }, false);
}

addJS_Node (null, null, GM_main);

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);
}

Notes:

  1. You can test these script against this page (output.jsbin.com/esikut/1).
  2. With no sandbox, unsafeWindow and window are the same.
  3. All of these scripts produce the same output on the console:

    In GM script, local global: undefined
    In GM script, script global: greasy
    On target page, local global: stovetop
    On target page, script global: greasy
    In GM script, local global, after ready: stovetop
    
  4. The Script Injection code will work in a variety of browsers besides Firefox. unsafeWindow currently only works in Firefox+Greasemonkey(or Scriptish) or Chrome+Tampermonkey.

Secrete answered 21/11, 2012 at 2:55 Comment(3)
btw wiki.greasespot.net/UnsafeWindow say's its unsafe to use unsafeWindow, what would a safer alternative be in this case?Leucoma
The "safer" alternative would be to either use the @grant none method but replace unsafeWindow with window (although they are identical when @grant none is in effect), or to use the Script Injection method. ... However, the risk of using unsafeWindow is greatly overblown -- with no real exploits reported. As long as you aren't on a site that's actively targeting GM scripts, there should be no risk.Secrete
Can you perchance update your answer? I have a question similar to the original but it seems that things have changed in 10 years. Or should I post it as a new question?Trouvaille
K
2

Your variable greasy is defined in the scope of the anonymous function. You cannot access greasy even in your userscript, unless it is part of your function. Example:

(function(){
    var foo = 5;
    alert(foo);
}();
alert(foo); //ERROR, because foo is undefined outside of the function.

Do it this way:

var foo = 5;
(function(){
     alert(foo);
}();
alert(foo);

Also, why are you putting all your code in an anonymous function and then executing it?

Konstance answered 21/11, 2012 at 1:46 Comment(2)
I just used a greasemonkey template script and it had the example code in the anonymous function. I assumed it was required for greasemonkey scripts. I moved the var declaration outside of the function and I still can't alert the variable value from the test.js script on page.Leucoma
Could it be my test.js script is being run before Greasemonkey? If so how can I delay it?Leucoma
H
2

You can also use localStorage:

localStorage.setItem("numberOfThings", "42");

localStorage.getItem("numberOfThings");
Huddersfield answered 9/10, 2018 at 21:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.