I'm creating a UserScript extension for Firefox & Chrome and I'm trying to use some of the code in the website's JavaScript, e.g.:
function: myFunction(){ return Grooveshark.playNextSong(); }
The problem is when I test this code, Grooveshark
is a null reference.
I know there are other people who have done it:
see BetterGrooveshark
But I don't know why my simple extension can't call Grooveshark's JavaScript functions.
Do I need to 'append' my script to the document in order for this to work?: document.document.body.appendChild(script);
Doesn't Greasemonkey inject my extensions JavaScript already? Can someone clarify this for me please.
Thanks.
You can think of them as a cosmetic makeover of the web apps that you use. Userscripts are browser-specific and website-specific; a particular script, if installed and supported by your browser, will work only for a particular website (or a group of websites).
Functions and variables declared in your GreaseMonkey scripts (and Chrome's user scripts) are kept separate from the ones declared by the web page, for obvious reasons. For GM scripts in Firefox, you can access global variables via unsafeWindow
.
The best approach for safety and compatibility is to inject your functions into the page using a script element. I use the following snippet in my user scripts:
function addFunction(func, exec) { var script = document.createElement("script"); script.textContent = "-" + func + (exec ? "()" : ""); document.body.appendChild(script); }
The "-"
here makes certain the function is parsed as an expression so that exec
can be used to immediately execute upon adding it. You call the function like so:
function myFunction () { return Grooveshark.playNextSong(); } // Inject the function and execute it: addFunction(myFunction, true);
Doesn't Greasemonkey inject my extensions JavaScript already? Can someone clarify this for me please.
Greasemonkey executes your scripts in a sandbox, which is a restricted environment without direct access to the JavaScript in the page. Earlier versions of Greasemonkey injected scripts directly into the page, but this introduced serious security vulnerabilities. In the old model, scripts ran with the elevated rights of the browser chrome, which allowed remote pages to access Greasemonkey's built-in functions using some clever JavaScript. This was bad:
Greasemonkey scripts contained their own GM_xmlhttprequest object which, unlike a normal xmlttprequest object, could access any local files one one's computer or make arbitrary requests to arbitrary sites without regard for the same origin policy that typically applies to xmlhttprequest. (source)
When you access the window
object from a Greasemonkey script today, what you get is a wrapper object that indirectly references the actual window
's properties. This wrapper object can be modified safely, but has important limitations. Access to the actual window object is provided by unsafeWindow
(shorthand for window.wrappedJSObject
). Use of unsafeWindow
re-opens all of Greasemonkey's original security problems and isn't available in Chrome. It should be avoided wherever possible.
The good news: there are at least two ways to work with Greasemonkey's new security model in a safe way.
Now that Greasemonkey scripts can safely access the DOM, it's trivial to inject a <script>
tag into the <head>
of the target document. Create a function like this:
function exec(fn) { var script = document.createElement('script'); script.setAttribute("type", "application/javascript"); script.textContent = '(' + fn + ')();'; document.body.appendChild(script); // run the script document.body.removeChild(script); // clean up }
It's simple to use:
exec(function() { return Grooveshark.playNextSong(); });
Script injection may be overkill in some cases, especially when all you need is to modify the value of a variable in the page or execute a single function. The Location Hack leverages javascript:
URLs to access code in the document's content. It's a lot like running a bookmarklet from within a Greasemonkey script.
location.assign("javascript:Grooveshark.playNextSong();void(0)");
Here's a complete Greasemonkey script that demonstrates the examples above. You can run it on this page.
// ==UserScript== // @name Content Function Test // @namespace lwburk // @include http://stackoverflow.com/questions/5006460/userscripts-greasemonkey-calling-a-websites-javascript-functions // ==/UserScript== function exec(fn) { var script = document.createElement('script'); script.setAttribute("type", "application/javascript"); script.textContent = '(' + fn + ')();'; document.body.appendChild(script); // run the script document.body.removeChild(script); // clean up } window.addEventListener("load", function() { // script injection exec(function() { // alerts true if you're registered with Stack Overflow alert('registered? ' + isRegistered); }); // location hack location.assign("javascript:alert('registered? ' + isRegistered);void(0)"); }, false);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With