Is there an easy way to create a Firefox extension for a simple content script WITHOUT having to use the Add-On SDK and PageMod? It really seems overkill to go through the hassle of installing Python and the SDK, learning how to use the SDK and API, and adding unnecessary bloat and abstraction layers, just to execute a simple content script.
I already tried using a XUL browser overlay and injecting the scripts there, but having everything injected in the browser.xul context instead of document.body is also adding a lot of complexity...
So what's the easiest, lightweight way to inject a few scripts and css files in the html document instead of the XUL document?
Your guestion is border-line too broad, so I won't be discussing everything in detail but give a general overview.
Easy might be an overstatement, but SDK content scripts (and actually modules too), Greasemonkey/Scriptish and everything else that resembles a content script uses Sandbox internally. Even bootstrap.js in restartless add-ons are executed in a sandbox.
The basic idea is the following:
Get a reference to the content window you want to attach too.
Choose a "principal" the script should run under. The principal is essentially the security context/policy that also defines the same origin. An unprivileged content script would usually use the content window itself (which is a principal too), while a privileged script (chrome access to Components) script would use the system principal.
Choose if you want XRay wrappers. The docs tell you more about it.
Choose the Sandbox prototype (the "global" or top-level this). Usually for content script stuff you'll choose the content window.
Create the Sandbox.
Add any stuff your content script may need to the Sandbox.
Execute a script by either evalInSandbox or the subscript loader.
Here is a limited example adding an unprivileged content script to a window:
// 1. get the content window, e.g. the currently selected tab window
var contentWindow = gBrowser.contentWindow;
// 2. Choose the principal, e.g. just use the content window again
var principal = contentWindow;
// 3. We want XRay wrappers, to keep our content script and the actual
// page scripts in their own corners.
var wantXrays = true;
// 4. Our prototype will be the window
var sbProto = contentWindow;
// 5. Putting it all together to create a sandbox
var sandbox = Cu.Sandbox(principal, {
sandboxPrototype: sbProto,
wantXrays: wantXrays
});
// 6. Adding a random helper function (e.g.)
sandbox.getRandomInt = function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
// 7. Execute some content script, aka. the stupid example.
try {
var execute = function() {
var demo1 = document.querySelector('title').textContent;
var demo2 = getRandomInt(1, 1000);
alert(demo1 + " " + demo2);
}
Cu.evalInSandbox(
"(" + execute.toSource() + ")()",
sandbox
);
} catch(ex) {
console.error(ex);
}
PS: This example will run verbatim in a Scratchpad with Environment/Browser.
Regarding styles:
Do what the SDK does, I guess, which is simplified:
var wu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
var uri = Services.io.newURI(
"chrome://myaddon/style/content-style.css",
null,
null);
wu.loadSheet(uri, wu.USER_SHEET);
Related
Hi I was wondering if there is the ability in node js and zombie js to inject javascript files in to the headless browser, similar to what you can do with phantomjs.
For example in phantom js you would do:
page.injectJs("amino/TVI.js")
I have used phantomjs and it does do what I want it to do but however I am testing other options due to the high memory required by using phantom js.
you can append script tag into document object since it support DOM API in zombie.
The following example shows how to insert jquery into zombie homepage:
var Browser = require("zombie");
var assert = require("assert");
// Load the page from localhost
browser = new Browser()
browser.visit("http://zombie.labnotes.org/", function () {
assert.ok(browser.success);
// append script tag
var injectedScript = browser.document.createElement("script");
injectedScript.setAttribute("type","text/javascript");
injectedScript.setAttribute("src", "http://code.jquery.com/jquery-1.11.0.min.js");
browser.body.appendChild(injectedScript);
browser.wait(function(window) {
// make sure the new script tag is inserted
return window.document.querySelectorAll("script").length == 4;
}, function() {
// jquery is ready
assert.equal(browser.evaluate("$.fn.jquery"), "1.11.0");
console.log(browser.evaluate("$('title').text()"));
});
});
Try to think the other way around. You have already everything at your hand in zombie to inject everything you want.
For example: that.browser.window points to the jsdom window that every part of your site javascript is using as a base. So you can access the dom and all other window objects in the page already loaded.
I don't know what you want to archieve with injecting - you should not use it for testing anway, but it looks this is not your actual goal
I'm trying to develop extension that works only on specified pages - If page owner adds global variable into their code (for eg. ACCEPT_STATS = true;) I want to execute specified code.
I've already bind my function to the onload event, i've also found solution how to do that in Firefox:
var win = window.top.getBrowser().selectedBrowser.contentWindow;
if (typeof win.wrappedJSObject.ACCEPT_STATS !== 'undefined') {
// code to run if global variable present
}
but I couldn't make this work under Chrome. Is there any possibility to access document's global variable throw Chrome Extension code?
My extension's code is injected as a content-script.
Yes, including script into the page does run in an isolated context from the pages runtime script.
However, it is possible to work around the isolated worlds issue by pushing inline script into the runtime context via a script tag appended to the document's html. That inline script can then throw a custom event.
The included script in the isolated context can listen for that event and respond to it accordingly.
So code in your included script would look something like this:
// inject code into "the other side" to talk back to this side;
var scr = document.createElement('script');
//appending text to a function to convert it's src to string only works in Chrome
scr.textContent = '(' + function () {
var check = [do your custom code here];
var event = document.createEvent("CustomEvent");
event.initCustomEvent("MyCustomEvent", true, true, {"passback":check});
window.dispatchEvent(event); } + ')();'
//cram that sucker in
(document.head || document.documentElement).appendChild(scr);
//and then hide the evidence as much as possible.
scr.parentNode.removeChild(scr);
//now listen for the message
window.addEventListener("MyCustomEvent", function (e) {
var check = e.detail.passback;
// [do what you need to here].
});
The javascript running on the page is running in a different "isolated world" than the javascript that you inject using content scripts. Google Chrome keeps these two worlds separate for security reasons and therefore you can't just read window.XYZ on any window. More info on how isolated worlds work : http://www.youtube.com/watch?v=laLudeUmXHM
The correct way of implementing this is by communicating with the page is via window.postMessage API. Here're how I would go about it :
Inject a content script into each tab
Send a message to the tab via window.postMessage
If the page understands this message, it responds correctly (again via window.postMessage)
Content script executes the code that it needed to execute.
HTH
Background Information
I am working on a web application that utilizes GWT (v2.4). For the application, I am creating an iframe that will display some information from another website. I need to access some information from that iframe that is normally restricted via the Same Origin Policy (SOP). However, both sites (the parent and iframe) are hosted on the same super-domain, just under different sub-domains. So, something like this:
Parent: dev.app.mySite.com
frame: someOtherContent.mySite.com
I know the usual solution for this problem is to set the property: document.domain = 'mySite.com' on both parent and iframe site to allow passage of SOP. This works for all browsers (that I'm concerned with) except Internet Explorer 8 (and probably other versions).
The Problem
In IE, when I attempt to load my web application, I get a completely blank page with the following JS exception, "Access is denied." The source of this problem is in GWT's myGwtAppName.nochache.js where GWT generates some code during the compilation process that it needs (see below).
From the research I've done on this problem in general, the root cause of this issue seems to be that in IE, unlike all other browsers, iframes don't inherit their parent's document.domain settings. From what I understand, the code generated by GWT runs in an iframe (based on this comment: https://stackoverflow.com/a/5161888). So, what I think is happening based on my limited knowledge of JS:
I set document.domain = 'mySite.com' in the parent index page via JS and it is processed.
myGwtAppName.nochache.js is processed.
In nochache.js, code is ran to setup the GWT iframe sand-box environment
In that code, a call is being made to a SOP restricted property of the sand-box iframe
An exception is thrown because the site's parent document domain has been set to 'mySite.com' and the iframe's document.domain doesn't inherit that setting, so it's still 'dev.app.mySite.com'. This won't pass SOP because the domain has to be exactly the same.
The generated code that causes the exception
The below code, looks like it's setting up the GWT sandbox iframe environment.
var $intern_4 = 'myGwtAppName',
$intern_7 = 'body',
$intern_8 = 'iframe',
$intern_9 = 'javascript:""',
$intern_10 = 'position:absolute; width:0; height:0; border:none; left: -1000px; top: -1000px; !important',
$intern_11 = '<html><head><\/head><body><\/body><\/html>',
$intern_12 = 'script',
$intern_13 = 'javascript',
$intern_14 = 'var $wnd = window.parent;''
....
....
function setupInstallLocation(){
if (frameDoc) {
return;
}
var scriptFrame = $doc.createElement($intern_8);
scriptFrame.src = $intern_9;
scriptFrame.id = $intern_4;
scriptFrame.style.cssText = $intern_10;
scriptFrame.tabIndex = -1;
$doc.body.appendChild(scriptFrame);
frameDoc = scriptFrame.contentDocument;
if (!frameDoc) {
frameDoc = scriptFrame.contentWindow.document; //THIS CAUSES THE EXCEPTION
}
frameDoc.open();
frameDoc.write($intern_11);
frameDoc.close();
var frameDocbody = frameDoc.getElementsByTagName($intern_7)[0];
var script = frameDoc.createElement($intern_12);
script.language = $intern_13;
var temp = $intern_14;
script.text = temp;
frameDocbody.appendChild(script);
}
....
....
My Questions
Is my analysis of the situation completely off-base?
Has anyone seen a solution for this problem that will work in a GWT environment in IE?
Information Sources
IE doesn't inherit document.domain settings: https://stackoverflow.com/a/1888711 (and many other threads).
GWT runs in an iframe sand-box environment: https://stackoverflow.com/a/5161888
You may use html5 web messaging to communicate between iframe and parent.
Be aware that Internet Explorer has following bugs. You can send only string as messages. You can't send object like other browser support.
Some people advice to encode object into JSON if you wish to send more then just a string but sometimes it is cheaper to send URL encoded string just like query string in URL.
Here are examples 2 top results from my google
http://tutorials.jenkov.com/html5/messaging.html
https://thenewcircle.com/bookshelf/html5_tutorial/messaging.html
Take a look that they use different code to listen for messages
window.attachEvent("onmessage", handleMessage);
window.addEventListener("message", handleMessage, true);
First works with IE and old Opera and last works with a rest of world.
I've run into the same issue and have found no elegant solution, but...
Right now I have an ant task that manually alters 3 specific points in the GWT nocache.js file after compilation to workaround the issue. You have to use regular expressions to make sure the injected code can reference a couple specific variables in the obfuscated code. It's all terribly ugly...
If you've found a more elegant solution please do post, since my solution is a hack. Details below...
Note - This assumes you have compiled GWT in "PRETTY" mode, since that's the only way I could reference variable/method names.
The problem we really need to solve here is that IE does not inherit altered document.domain values. So IFrames will have invalid document.domains. You can, however, force an IFrame to set its own document.domain, so that it is in sync with the outer page. However - this method requires letting the IFrame load and execute first. Which means that further operations must be executed in a callback after the iframe has loaded.
1) You need to add the follow two methods to the gwt .js file:
function waitForSetupInstallLocation(callback){
if (frameDoc) {
return;
}
var scriptFrame = $doc_0.createElement('iframe');
scriptFrame.src="javascript:document.open();document.domain='<domainvalue>';document.close();document.body.contentEditable=true;";
scriptFrame.id = '<MyWidgetSetName>';
scriptFrame.style.cssText = 'position:absolute; width:0; height:0; border:none; left: -1000px;' + ' top: -1000px;';
scriptFrame.tabIndex = -1;
$doc_0.body.appendChild(scriptFrame);
var addedContent = false;
try {
setFrameDoc(scriptFrame);
callback();
}
catch(e){
scriptFrame.onload = function(){
if(!addedContent){
addedContent = true;
setFrameDoc(scriptFrame);
callback();
}
};
}
}
function setFrameDoc(scriptFrame){
frameDoc = scriptFrame.contentDocument;
if (!frameDoc) {
frameDoc = scriptFrame.contentWindow.document;
}
frameDoc.open();
var doctype = document.compatMode == 'CSS1Compat'?'<!doctype html>':'';
frameDoc.write(doctype + '<html><head><\/head><body><\/body><\/html>');
frameDoc.close();
}
These two methods allow GWT to inject code into the page while also waiting for IE to load an IFRAME which then changes its own document.domain. You can see that the first accepts a callback. The callback is executed only after the IFrame is loaded.
The next issue is that these are asynchronous methods, and only accept callbacks. GWT currently does all setup synchronously. So the next modification is that the two methods that need to use it must be altered. All of the inner content of the following methods:
function installCode(code_0){...}
<WidgetSetName>.__installRunAsyncCode = function(code_0){...}
Needs to be wrapped in a function, and passed to the waitForSetupInstallLocation method as a callback. So that essentially you have turned those methods into asynchronous methods.
An example of what this looks like is:
function installCode(code_0){
waitForSetupInstallLocation(function(){
<real method content>
});
}
Once you have done all this - it should work in IE, and should remain functional in other browsers, since youve only added the use of a callback.
I am developing chrome extension. I want to connect some API to current tab after click on button in popup.html. I use this code in popup.js:
$('button').click(function() {
chrome.tabs.executeScript({
file: 'js/ymaps.js'
}, function() {});
});
In ymaps.js I use following code to connect API to current tab:
var script = document.createElement('script');
script.src = "http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU";
document.getElementsByTagName('head')[0].appendChild(script);
This API is needed to use Yandex Maps. So, after that code I create <div> where map should be placed:
$('body').append('<div id="ymapsbox"></div>');
And this simple code only loads map to created <div>:
ymaps.ready(init);//Waits DOM loaded and run function
var myMap;
function init() {
myMap = new ymaps.Map("ymapsbox", {
center: [55.76, 37.64],
zoom: 7
});
}
I think, everything is clear, and if you are still reading, I'll explain what is the problem.
When I click on button in my popup.html I get in Chrome's console Uncaught ReferenceError: ymaps is not defined. Seems like api library isn't connected. BUT! When I manually type in console ymaps - I get list of available methods, so library is connected. So why when I call ymaps-object from executed .js-file I get such an error?
UPD: I also tried to wrap ymaps.ready(init) in $(document).ready() function:
$(document).ready(function() {
ymaps.ready(init);
})
But error is still appearing.
Man below said that api library maybe isn't loaded yet. But this code produces error too.
setTimeout(function() {
ymaps.ready(init);
}, 1500);
I even tried to do such a way...
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
if (changeInfo.status == "complete") {
chrome.tabs.executeScript({
file: 'js/gmm/yandexmaps.js'
});
}
});
ymaps is not defined because you're trying to use it in the content script, while the library is loaded in the context of the page (via the <script> tag).
Usually, you can solve the problem by loading the library as a content script, e.g.
chrome.tabs.executeScript({
file: 'library.js'
}, function() {
chrome.tabs.executeScript({
file: 'yourscript.js'
});
});
However, this will not solve your problem, because your library loads more external scripts in <script> tags. Consequently, part of the library is only visible to scripts within the web page (and not to the content script, because of the separate script execution environments).
Solution 1: Intercept <script> tags and run them as a content script.
Get scriptTagContext.js from https://github.com/Rob--W/chrome-api/tree/master/scriptTagContext, and load it before your other content scripts. This module solves your problem by changing the execution environment of <script> (created within the content script) to the content script.
chrome.tabs.executeScript({
file: 'scriptTagContext.js'
}, function() {
chrome.tabs.executeScript({
file: 'js/ymaps.js'
});
});
See Rob--W/chrome-api/scriptTagContext/README.md for documentation.
See the first revision of this answer for the explanation of the concept behind the solution.
Solution 2: Run in the page's context
If you -somehow- do not want to use the previous solution, then there's another option to get the code to run. I strongly recommend against this method, because it might (and will) cause conflicts in other pages. Nevertheless, for completeness:
Run all code in the context of the page, by inserting the content scripts via <script> tags in the page (or at least, the parts of the extension that use the external library). This will only work if you do not use any of the Chrome extension APIs, because your scripts will effectively run with the limited privileges of the web page.
For example, the code from your question would be restructed as follows:
var script = document.createElement('script');
script.src = "http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU";
script.onload = function() {
var script = document.createElement('script');
script.textContent = '(' + function() {
// Runs in the context of your page
ymaps.ready(init);//Waits DOM loaded and run function
var myMap;
function init() {
myMap = new ymaps.Map("ymapsbox", {
center: [55.76, 37.64],
zoom: 7
});
}
} + ')()';
document.head.appendChild(script);
};
document.head.appendChild(script);
This is just one of the many ways to switch the execution context of your script to the page. Read Building a Chrome Extension - Inject code in a page using a Content script to learn more about the other possible options.
This is not a timing issue, rather an "execution environment"-related issue.
You inject the script into the web-page's JS context (inserting the script tag into head), but try to call ymaps from the content script's JS context. Yet, content-scripts "live" in an isolated world and have no access to the JS context of the web-page (take a look at the docs).
EDIT (thx to Rob's comment)
Usually, you are able to bundle a copy of the library and inject it as a content script as well. In your perticular case, this won't help either, since the library itself inserts script tags into to load dependencies.
Possible solutions:
Depending on your exact requirements, you could:
Instead of inserting the map into the web-page, you could display (and let the user interact with) it in a popup window or new tab. You will provide an HTML file to be loaded in this new window/tab containing the library (either referencing a bundled copy of the file or using a CDN after relaxing the default Content Security Policy - the former is the recommended way).
Modify the external library (i.e. to eliminate insertion of script tags). I would advise against it, since this method introduces additional maintainance "costs" (e.g. you need to repeat the process every time the library is updated).
Inject all code into the web-page's context.
Possible pitfall: Mess up the web-pages JS, e.g. overwriting already defined variables/functions.
Also, this method will become increasingly complex if you need to interact with chrome.* APIs (which will not be available to the web-page's JS context, so you'll need to device a proprietary message passing mechanism, e.g. using custom events).
Yet, if you only need to execute some simple initialization code, this is a viable alternative:
E.g.:
ymaps.js:
function initMap() {
ymaps.ready(init);//Waits DOM loaded and run function
var myMap;
function init() {
myMap = new ymaps.Map("ymapsbox", {
center: [55.76, 37.64],
zoom: 7
});
}
}
$('body').append('<div id="ymapsbox"></div>');
var script1 = document.createElement('script');
script1.src = 'http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU';
script1.addEventListener('load', function() {
var script2 = document.createElement('script');
var script2.textContent = '(' + initMap + ')()';
document.head.appendChild(script2);
});
document.head.appendChild(script1);
Rob already pointed to this great resource on the subject:
Building a Chrome Extension - Inject code in a page using a Content script
There is a much easier solutioin from Yandex itself.
// div-container of the map
<div id="YMapsID" style="width: 450px; height: 350px;"></div>
<script type="text/javascript">
var myMap;
function init (ymaps) {
myMap = new ymaps.Map("YMapsID", {
center: [55.87, 37.66],
zoom: 10
});
...
}
</script>
// Just after API is loaded the function init will be invoked
// On that moment the container will be ready for usage
<script src="https://...?load=package.full&lang=ru_RU&onload=init">
Update
To work this properly you must be sure that init has been ready to the moment of Yandex-scirpt is loaded. This is possible in the following ways.
You place init on the html page.
You initiate loading Yandex-script from the same script where init is placed.
You create a dispatcher on the html page which catches the ready events from both components.
And you also need to check that your container is created to the moment of Yandex-script is loaded.
Update 2
Sometimes it happens that init script is loaded later than Yandex-lib. In this case it is worth checking:
if(typeof ymaps !== 'undefined' && typeof ymaps.Map !== 'undefined') {
initMap();
}
Also I came across a problem with positioning of the map canvas, when it is shifted in respect to the container. This may happen, for example, when the container is in a fading modal window. In this case the best is to invoke a window resize event:
$('#modal').on('shown.bs.modal', function (e) {
window.dispatchEvent(new Event('resize'));
});
My app is loading an external javascript file with jQuery.getScript(). When I use the bookmarklet or an extension to start the app everything works fine. When the app is installed through KBX though inside Chrome with the KBX extension the included functions inside the javascript file are not accessible in the callback anymore and I get : Uncaught ReferenceError: myfunc is not defined .
Is there any trick to get access to the included functions?
Bookmarklet : javascript:(function(){var d=document;var s=d.createElement('script');s.text="KOBJ_config={'rids':['a1135x30']};";d.body.appendChild(s);var l=d.createElement('script');l.src='http://init.kobj.net/js/shared/kobj-static.js';d.body.appendChild(l);})()
Chrome extension : crx
url for installation via KBX : app on KBX
Here is the ruleset:
ruleset a1135x30 {
meta {
name "test_external_js_loading"
description <<
debugging external loading in kbx
>>
author "loic devaux"
logging on
}
dispatch {
domain ".*"
}
global {
}
rule first_rule {
select when pageview ".*" setting ()
// pre { }
// notify("Hello World", "This is a sample rule.");
{
emit <|
$K.getScript('http\:\/\/lolo.asia/kynetx_debug/js/myfunc.js',function() {
myfunc();
/*
* myfunc.js content:
myfunc = function(){
console.log('running myfunc');
};
*/
}
);
|>
}
}
}
I'm not completely sure that your issue has to do with the sandboxed environment that the KBX runs your code in but I think it might. Here is a post I wrote about dealing with the sandboxed environment of the KBX http://geek.michaelgrace.org/2011/03/kynetxs-new-sandboxed-browser-extensions/
From blog post
I recently released my “Old School Retweet” Kynetx app in the Kynetx app store for the newly released browser extensions. I super love the new extensions and all that they do for users and developers alike. Something that I forgot when I released the app in the app store is that the new extension are sandboxed.
Because the extensions are sandboxed, all of the scripts from the extensions run a bit differently than they used to in the previous Kynetx extensions. Without getting into the technical details too much, the previous extensions just injected JavaScript into the page and the new extensions run JavaScript in a sandbox which has access to the DOM but can’t access anything else on the page. Because of this change my retweet app broke since I was using the jQuery loaded by Twitter.com to bring up the new tweet box (I do this because Twitter.com used that library to bind a click event and to trigger that event it has to be from the same library that bound it). Thankfully, with the help of a friend, I was able to get a work around for both Firefox and Chrome’s sandbox environment.
How I did it…
If the app is run not inside a sandbox I can just access the jQuery that Twitter.com loads to open a new tweet box
$("#new-tweet").trigger("click");
From within the Firefox sandbox I can access the page outside of the sandbox
window['$']("#new-tweet").trigger("click");
If I am in the Chrome sandbox I can create a script element that has the JavaScript that I want to execute. Crude, but it works. : )
var trigger_click_script = document.createElement("script");
var fallback = "window['$']('#new-tweet').trigger('click');";
trigger_click_script.innerHTML = fallback;
document.getElementsByTagName("head")[0].appendChild(trigger_click_script);
Here is the JavaScript code that I ended up with that gets executed when a user clicks on the retweet button.
// get stuff to retweet
var tweet = $K(this).parents(".tweet-content").find(".tweet-text").text();
var name = $K(this).parents(".tweet-content").find(".tweet-screen-name").text();
// build tweet
var retweet = "RT #"+name+" "+tweet;
// open new tweet box
$("#new-tweet").trigger("click");
// hack for FF sandbox
if ($("#tweet-dialog:visible").length === 0) {
window['$']("#new-tweet").trigger("click");
}
// put tweet in new tweet box
$K(".draggable textarea.twitter-anywhere-tweet-box-editor").val(retweet).focus();
$K("#tweet_dialog a.tweet-button.button.disabled").removeClass("disabled");
// hack for chrome sandbox
if ($("#tweet-dialog:visible").length === 0) {
var fallback = "window['$']('#new-tweet').trigger('click'); ";
fallback += "window['$']('.draggable textarea.twitter-anywhere-tweet-box-editor').val('"+retweet+"').focus(); ";
fallback += "window['$']('#tweet_dialog a.tweet-button.button.disabled').removeClass('disabled'); ";
var trigger_click_script = document.createElement("script");
trigger_click_script.innerHTML = fallback;
document.getElementsByTagName("head")[0].appendChild(trigger_click_script);
}
Another thing that you can do to make your stuff accessible outside of the sandbox is the declare your stuff at the window level (defeats the purpose of the sandbox, and not recommended). For example: if you want to perform a console.log, whilst inside the sandbox, the console.log won't log to the window console. But, if you say window.console.log, it will. So, you could (but shouldn't) declare a var the following way:
window.myvar = "MyValue";
That would make the var a window level var. Even though I am preaching against this, I have done it a time or two, for testing.
So... I just did something that worked for both FF and Chrome. It isn't pretty, but none of this really is. It was nice to have one workaround for both instead of having to work differently for FF than Chrome. I needed to get the value from a global object... but the sandbox was blocking that. With this hack I was able to do that one way for both browsers.
First, from within the sandbox... add an invisible div to the bottom of the document.body
$K('body').append('<div id="randomdiv" style="display:none;"></div>');
Then create a script in the document.head that will set the text of the randomdiv to the value that I needed.
var temp = '$("#randomdiv").text(twttr.currentUserScreenName);';
var somescript = document.createElement("script");
somescript.innerHTML = temp;
document.getElementsByTagName("head")[0].appendChild(somescript);
Then... at this point, from within the sandbox, you can select the value from the DOM, rather than from some global js object. This is how you would do it.
var myvar = $K('#randomdiv').text();
Let me know your thoughts. This is what was the easiest for me.