Access global scope in frame script

The global scope of a frame script is very specific, and not the same as the global scope that you might be used to in other contexts. This page describes the global scope of a frame script.

To answer your actual question, you don’t access the global scope you are thinking of. However, you can access many of the properties of that global scope directly or indirectly, possibly by asking another scope to send you the value.

Slightly off topic, but perhaps might help you to understand that what you’re asking doesn’t really make sense - which global scope do you want? The global scope of the chrome process? Clearly not accessible in most cases. The global scope of a content script? That wouldn’t be very useful. The bootstrap global scope? A worker global scope? All these things are different, intentionally and for good reason. You have your own global scope in a frame script and the correct question is how do I access X, where X is anything except “the global scope” :slight_smile:

2 Likes

Thanks Litho!
What I was trying to do, was in my framescript I defined a global function named function blah(){} now if I did this in a window I can test if it is present by doing if (window.blah) and I can trigger it by going window.blah(). This is what I’m trying to do in the framescript. To work around it right now I have created an object in my framescript and checking/calling into that. Do you know how I can do this in framescript?

From that aweosme page on MDN:

instead, top-level variables are stored in a special per-script object that delegates to the per-tab global

I need to access this top-level var area. Doing this doesn’t get that, do you know how to get that?

Remember, frame scripts are usually in the content process (and also a copy in the chrome process just for fun), so accessing anything in a frame script from outside the frame script is generally doomed to failure.

Normally, each framescript gets its own global “sandbox”. However, you can use an optional third parameter to loadFrameScript() which forces the frame script to share a single global environment with other frame scripts that are loaded the same way. Then frame scripts (within the same frame!) can all access eachother’s globals directly. I don’t think this will do what you need. Accessing the frame script global environment from anywhere other than a frame script in the same frame (same tab) is still going to fail. This is partly for legacy purposes, since until FF17, frame scripts just all got the standard global environment.

1 Like

Thanks much for those warnings.

Thankfully I’m just staying within, definitely not trying to pierce into framescript or cross-framescript stuff. I am using message system, when framescript receives a message from the sendAsyncMessage method it whould trigger the function in the framescript that has the same name as the string received.

I think I’ve misunderstood what you were trying to do. You just want to access the global scope of a particular framescript, from inside that framescript? Not from somewhere else? It never occurred to me that was what you were doing because of course you never need to specify the global javescript scope explicitly. If “core” is a global variable then you just access it by “core”, assuming that core isn’t a local variable.

So what are you trying to do? Iterate through all the global variables? Something like that? Test if a global variable (ie. a property of the global scope) exists? You can just test the variable being undefined. Or it should appear as a property of the ContentFrameMessageManager object. I’ve never checked that in a framescript, but that’s what globals are in javascript.

1 Like

Yep I’m just trying to check if it exists in the socpe and if it does then trigger it.

The issue is though, another process is sending me a string. That string is the name of the function. I’m not able to do typeof(varHoldingString) without using eval. So I was doing this[varHoldingString]. But in the framescript this is not the local-global scope.

The simplest way to check if a javascript entity exists is just to compare it with undefined.

Does this really not give you access to the global scope of a frame script? Where else would a global variable be except as a property of the global object? I might have to do some testing …

So, I can alert a global function from a frame script:
content.alert(notifyStorage);

I get the same result with:
content.alert(this["notifyStorage"]);

Not sure what you’re doing …

I agree but it’s worth mentioning that still throws if the var is actually undefined (and not just set to that value), because you’re referencing it directly. So you would need to wrap it in a try/catch that handles that outcome accordingly.

On alternative, and I know it’s not exactly what you asked, can’t you get around it by defining blah as a method of this like this.blah = function() {}; instead of making it a global function like function blah() {};? Provided the scope where blah is defined is the same as where it will be checked later, it should work.

1 Like

Thanks so much Litho for testing that out. content is actually the global
of the currently loaded website. If I define functions in content then
when the website changes all my functions are destroyed. this is the
ContentFrameMessageManager.

content has nothing to do with it, It is only used to access the alert function. Forget content, it is a red herring.

My point is that everything you’ve said doesn’t work, does work. I don’t know what you’re doing, but your problem is not that this doesn’t access the global environment in a frame script. I defined a global function called notifyStorage. Actually, it already exists in my addon so really nothing fancy or tricky about it, just a function declared in the frame script. I can output it by referencing just notifyStorage, or this[“notifyStorage”]. It works. Look elsewhere for your troubles. Are you spawning workers? Using a javascript code module?

P.S. If try/catch is a nuisance, you can test this[“property”]. It will give undefined without throwing. Of course, if something in your environment is preventing access to this, then you have a problem :slight_smile:

1 Like

The problem is that the method name is a string, that was sendAsyncMessage’ed to the script, here is what I’m doing exactly - https://github.com/Noitidart/Chrome-Store-Foxified/blob/5f04b7baef3f7ab5a7e985acd15db45a77abccf5/resources/scripts/MainFramescript.js#L333

Thanks so much for all this discussion. The work around I used was to define all my functions in a variable named gCommScope and I check and call functions in there.

I’ll try using this again, I might have had some funky sandboxing going on, I was experiementing with sandboxing a lot over the last few months. Thanks brother!

Looks very likely to me that “this” is not the frame global scope when you try to use it. Or when you try to bind it. Certainly it isn’t in the version of the code you link to. Hard to be sure exactly what it was like when you were trying to get the global scope, but two things that could be tripping you up: this inside an event handler is the object that the event was raised on; and this inside a wrapper object is the wrapper. In those situations, “this” isn’t going to resolve to the global scope. If you want to work inside a wrapper (you don’t have to in a frame script) then wrap everything and don’t try to access the global scope using this. When you use event handlers, make sure you switch out of the event scope before you try to use this. Unless you actually want to access the event object. One handy little wrinkle is that “this” inside a frame script message handler is the frame script global scope :slight_smile:

1 Like

I finally got this down. What was happening was that in the framescript I was loading a subscript call Comm.js. I think I didn’t ask the question clearly and should have mentioned that the quirk was seen when was using loadSubScript.

Now this subscript was able to access properties set on this.xxx however if I did var xxx it would not be able to access it.

So what I did was in the framescript I removed all code and put it to subscript:

Services.scriptloader.loadSubScript('chrome://comm/content/resources/scripts/Comm/Comm.js');
Services.scriptloader.loadSubScript('chrome://comm/content/resources/scripts/MainFramescriptSubscript.js');

Now Comm.js can access globals var xxx as if it were this.xxx

I think this is unique the loadSubScript, as in the framescript itself, if I set this.xxx = true I can access it with xxx or this.xxx.

Seen here:

loadSubScript() has an optional second parameter which is the global object for the script. Top-level variables and functions declared by the script will be created as properties on this object, and can then be accessed from the caller. The default value is the same global scope as the caller. However, undeclared variables in the script (ie. not resolvable) will always be created (or resolved) on the callers global object regardless of the target object passed as the second parameter of loadSubScript(). let and const declarations are weird, don’t even go there!

The value of “this” inside the subscript should be the global object, but only at the top level of the script. Inside a function, the value of “this” may be different. The actual value of “this” inside a function depends on whether you are in strict mode or not and on where the function is called from.

1 Like

Ah yeah I tried with passing this and without passing anything to the second argument. It was wonky stuff, because either way, it wasn’t able to access globals defined in the scope I imported it to unless the globals were delcared by prefixing it with this.. So things like var gWorkerComm; were inaccessible. I seriously appreciate all your discussion about this! No one else hung through this with me! Your discussion motivated me!

Holy hell doing Services.scriptloader.loadSubScript('...', this) is extremely bad. Because it is writing into the content frame message manager. Which is basically a shared scope among all addons. Wow very very bad. I wrote two addons using this technique, and they clashed hard with each other because in both addons I had a var named core so whichever one registered 2nd in that tab would override the first ones core.

Gosh this stinks so much how come we can’t get the global environment in the framescript.

The frame script environment is described here. In particular:

All the frame scripts running in a tab share this global. However, any top-level variables defined by a script are not stored on the global: instead, top-level variables are stored in a special per-script object that delegates to the per-tab global. This means you don’t have to worry about global variables you define conflicting with global variables defined by another frame script. You can still access the global directly via this.

So you can act like you have your own sandbox most of the time but if you deliberately access the frame manager itself then you’re in a shared environment. If you want to stay within your own sandbox you can load the subscript within a global object that you declare, and put everything “public” as a property of that namespace object.

I’m still not sure what your complaint is. What “global environment” do you want? Your latest message seems to be complaining that you got the global environment in the frame script and that was a big problem.

1 Like

8416 from IRC helped me phrase my question better. I am trying to not get the global, but “the top level variable scope”. In window and bootstrap the top level variable scope is same as global because the vars are bound to the global.

Thanks so much for sticking through this with me! Apparently there is a way to get the top level global scope with this but it is very bad for unloading framescripts, so I’m just going to not go that way and just work around it with setting a scope like this:

var myscope = {}

And then load everything into that.