views:

26

answers:

1

Hello, I am having a script that used unsafeWindow in FireFox, since that did not work, I have searched for another option, and found it, I am only wondering: How can I use a variable from my userscript into the unsafeWindow workaround?

My code is:

// ==UserScript==
// @name   Test
// @description  Test
// @include   http://www.google*
// ==/UserScript==

var toAlert = "This is what I want to alert...";
alert("Before implementation...");
contentEval( function(){ alert(toAlert);});
alert("And after...");
function contentEval(source) {
  // Check for function input.
  if ('function' == typeof source) {
    // Execute this function with no arguments, by adding parentheses.
    // One set around the function, required for valid syntax, and a
    // second empty set calls the surrounded function.
    source = '(' + source + ')();'
  }

  // Create a script node holding this  source code.
  var script = document.createElement('script');
  script.setAttribute("type", "application/javascript");
  script.textContent = source;

  // Insert the script node into the page, so it will run, and immediately
  // remove it to clean up.
  document.body.appendChild(script);
  document.body.removeChild(script);
}

And it does not work... What am I doing wrong?

A: 

Your script would work if toAlert happened to be defined in the page's global scope.

In Chrome, extension/Greasemonkey JavaScript cannot share variables or closures with the page JavaScript.
That's why you can't inject that function directly, from the extension scope to the page scope, but have to recreate it from a source string.

This means that if you create a function in the page scope, any variables or functions that your function requires must either:

  1. Already be present, globally, in the source page.
    Or
  2. Be scripted into the source page also.

For example, modifying your code like so...

//-- Must recreate the variable that the function requires.
scriptStr  = 'var toAlert="' + toAlert +'";';

//-- Now the function.
scriptStr += '(' + source.toString() + ')();'

var script = document.createElement('script');
script.textContent = scriptStr;

... works, but this approach obviously gets messy.

The smart thing to do is to either:
(A) keep all of your JavaScript in the extension; don't interact with the page's JavaScript.

Or (B) if you must interact with the page's JS, or load libraries like jQuery, then put all of your code in one main() function and script it into the source page.

Like so:

function localMain ()
{
    /*--- Put EVERYTHING inside this wrapper, functions and variables.
        Call or use nothing else that's defined in the GM script here.
        Can use objects in the source page's scope, though.
    */
}

//--- Now create the function in the page's scope and run it.
var scriptNode          = document.createElement ("script");
scriptNode.textContent  = localMain.toString() + "\n localMain ();";
document.head.appendChild (scriptNode);

Note that if you are also loading a library into the page's scope, then you may need to delay running localMain() by use of a timer and a check for that library.

Brock Adams
Thank you, I have used the first option and it works great!
Man Ik Weet
You're welcome. Glad to help.
Brock Adams