Firefox add-on: function not available after juggling scopes - javascript

As of Firefox 36, Function.__exposedProps__ was made unavailable. Instead if one wanted to expose a chrome JS object to be used in content scripts, they have to use Components.utils.cloneInto with the target scope as browser.contentWindow.wrappedJSObject.
If one does not turn on the cloneFunctions flag, only those attributes are cloned that are not functions. Turning the flag does clone functions too, but not those functions that are defined via the Function.prototype path. For those functions one has to export them via Components.utils.exportTo with the target scope as your exposed object.
Coming to the issue I'm facing. (As I am unable to put it in words, I am adding a MWE).
Chrome end JS:
function Foo(){
this._nFunc = "something";
this._func = function(){/*do something*/};
}
Foo.prototype.Bar = function(){
this._func();
}
Foo.prototype.FooBar = function(){
this._nFunc = "somthing else";
}
var myFoo = new Foo();
var targetScope = browser.contentWindow.wrappedJSObject;
targetScope.myExposedObject = Components.utils.cloneInto(myFoo, targetScope, {cloneFunctions:true});
Components.utils.exportFunction(myFoo.Bar, targetScope.myExposedObject , {defineAs:"Bar"});
Components.utils.exportFunction(myFoo.FooBar, targetScope.myExposedObject , {defineAs:"FooBar"});
Content end JS:
window.myExposedObject.FooBar(); // works
window.myExposedObject._func(); // works
window.myExposedObject.Bar() // error this._func is undefined
Upon logging the this scope received by the function Bar(), we get _func:(void 0), while _nFunc is logged correctly.
Questions:
Is there something I'm missing, or is this a limitation in Firefox? If it is a limitation, please suggest possible ways to workaround the limitation.
Initially I thought that Bar() was somehow unable to access the scope of the calling object, and I tried to supply it the scope as parameters, i.e., Foo.prototype.Bar = function(scope){ scope._func();} and window.myExposedObject.Bar(window.myExposedObject);. Interestingly upon logging, the scope object also turned out to be (void 0). Why is that? I am sure that I am missing something here. What I expected was that the exposed object would map to the original object and upon sending the exposed object as parameters the chrome end JS would be able to get the original object.

While what you're trying to do might be possible with the right combination of cloneInto/exportFunction and waiving of xrays i would suggest you simply load the unprivileged part of your class hierarchy directly into the target context with the subscript loader and only hook the minimal amount of privileged functions into the prototype once it has been created.
This should reduce the attack surface and also avoid headaches with inheritance.
Additionally, these may prove useful:
https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
https://developer.mozilla.org/en-US/docs/Components.utils.makeObjectPropsNormal

Related

OnChange function not defined when I reference my script as a module? [duplicate]

I have my HTML setup like this:
<script type="module" src="main.js"></script>
and all the ES6 modules work fine. The only problem is I now can't refer to anything from within DevTools (like using the Console and typing in a variable to see it's value or using a function manually).
How do I import modules whilst being able to use the DevTools? Thanks!
One way to make a variable accessable within DevTools is to create it on the window object:
// Variable in your module
var importantNumber = 1;
window.importantNumber = importantNumber;
This method works fine if you just have a couple of variables, but if you need to have access to a lot more variables within DevTools, I would recommend you go to the sources-tab in DevTools, search for your module and adding a breakpoint. When the execution pauses, you have access to all the variables within that module on the DevTools console.
If you want to be able to refer to variables created within the module from the console's global scope, you'll have to deliberately expose each such variable that you want to be visible from the console. Either assign each variable to window (probably not a good idea - the whole point of modules is to make things more modular, without global pollution), or perhaps assign a single object to window, to which you assign module variables. For example:
// in the console:
setTimeout(() => {
window.myModule.foo();
console.log(window.myModule.bar);
});
<script type="module">
function foo() {
console.log('doing foo');
}
const bar = 'a string bar';
const thisModule = { foo, bar };
window.myModule = thisModule;
// If you ever reassign variables, you'll have to reassign them on thisModule too
// or, only reference and reassign properties of thisModule, rather than create independent variables
</script>
For anyone else interested, if you're comfortable with it, use a bundler like Webpack. I don't believe (at least at this point) that the browser will by itself be able to use the DevTools on modules (the other solutions are quite janky, and aren't fantastic to work with).
Hopefully in the future, when all major browsers will be able to support ES6 modules without a bundler, we'll be able to use DevTools.
Using a Helper
I personally use a little helper function in development that allows me to expose a bunch a variables in a single expression. For example, it makes the following two blocks equivalent:
window.playerOne = playerOne;
window.someClass = someClass;
window.localData = localData;
globalize({playerOne, someClass, localData});
The helper looks like this:
const globalize = function(variables) {
Object.entries(variables).forEach(([name, value]) => window[name] = value);
};

What purpose does this serve? IIFE assignment to var in Snap.svg.js

My self-directed javascript study has finally led me to reading libraries where I found the following snippet (truncated for brevity). I'm using Firefox with firebug on a windows apache server (xampp).
I figured the snippet below would suffice but if anyone needs it, the entire library can be found here: snap.svg.js on github
var Snap = (function(root) {
Snap.version = "0.4.0";
function Snap(w, h) { // can be either width, height OR
if (w) {
if (w.nodeType) { // deterimines if parameter is a dom element
return wrap(w);
}
if (is(w, "array") && Snap.set) { // deterimines if parameter is an array
return Snap.set.apply(Snap, w);
}
if (w instanceof Element) { // deterimines if parameter is a Snap.Element
return w;
}
if (h == null) { // by elimination determines if parameter is a dom element id.
w = glob.doc.querySelector(String(w));
return wrap(w);
}
}
<numerous public and private properties and methods>
.
.
.
glob.win.Snap = Snap;
return Snap;
}(window || this));
Firebug shows the Snap object in the window context before instantiating any user objects. I was wondering exactly what mechanism was injecting the Snap object into the DOM. That's when I noticed the "var Snap". Initially, I thought that was it. But, since it didn't break the app when I changed the variable name or even deleted it, I became confused.
Further investigation resulted in the discovery at the bottom of the iife... specifically, "glob.win.Snap = Snap". Since "window" is being passed into the iife, it seems this is what's actually creating the Snap object in the window. Changing the name to glob.win.Snappy confirmed this.
I'm still learning so please correct me if I'm wrong. I'm trying to understand what's going on with this library. It seems that the function Snap() is being injected to the window context via the glob.win.Snap assignment. I don't see that the "var Snap" at the top or the "return Snap" are doing anything. In fact, I can rem them out and everything seems to function fine. So, my first question: Do those two lines serve some function I'm not seeing?
A secondary question is: What does the "this" fallback parameter refer to? My limited understanding of Snap is that it is always used within the window namespace so wouldn't "this" always be the window?
Just when I think I'm beginning to make the paradigm shift from classical to prototypical language, I run across code like this and it sets me back. I'd really appreciate some insight.
I had a look at the referenced source code, here a more condensed version:
var Snap = (function(root) {
Snap.version = "0.4.0";
function Snap(w, h) {}
var glob = {
win: root.window,
doc: root.window.document
};
...
glob.win.Snap = Snap;
return Snap;
}(window || this));
Snap.plugin(...);
It seems that the function Snap() is being injected to the window
context via the glob.win.Snap assignment. I don't see that the "var
Snap" at the top or the "return Snap" are doing anything.
You are correct, the declaration of var Snap = ...; and assignment via return Snap; is superfluous, since that variable lives in the global scope (i. e. the Window object) and is already declared by glob.win.Snap = Snap;
I assume they just keept that var declaration since it is pretty much standard when using the class pattern:
var MyClass = (function() {
function MyClass(n) {}
return MyClass;
})();
A secondary question is: What does the "this" fallback parameter refer
to? My limited understanding of Snap is that it is always used within
the window namespace so wouldn't "this" always be the window?
In some JavaScript environments, the root object is not called window (e.g. global in node.js). window || this will evaluate to the root object, no matter what it is called.
You will see such dependency injections often in JavaScript modules. See https://carldanley.com/js-module-pattern/ for more.
However, it seems that this library will not run if there is no window object available due to the var glob = { win: root.window, ... } assignment. They might just have kept the this in there because it is part of a standard module pattern.

avoid Javascript variable modification from browser console

I have a problem. I have defined some global variables and namespaced it into an object called "app".
Example:
window.app : {
foo : null,
bar : null,
}
Well, the idea is that I want to be able to modify those variables from any module by calling app.foo = "baz" or app.bar = "baz", but I don't want the user to be able to modify those variables from the browser console (element inspector).
Is it possible?
PD: Well, I have a Backbone.js collection which is sinchronized with the server. I don't want the user to be able to modify that collection with the console
No. The browser is the user's domain. They have the possibility to modify your scripts and inject their own functionality in various ways (through the console or browser plug-ins). That's one of the reasons why you should never blindly trust user input on the server side.
They could even manually forge a complete request, tricking your server into thinking that your JavaScript code made that request.
If you want these values to be secure, you need to keep them on the server. You can send them to the client, of course, as long as you keep a possibility to validate the values against those on the server.
The only way to make the variables not (easily) modifiable by a user is to remove them from global scope - something like
!function() {
foo = null;
bar = null;
}()
You'll need to redesign the way your modules interact with each other to accomplish this. An MVC Framework like Angular.js will help.
You should never rely on this as a security mechanism, though - the browser is fully in the user's control.
Still for them who are searching solution to this problem, use const modifier while assigning variable instead of var. Now try to change value of variable from browser console. It will throw error Uncaught TypeError: Assignment to constant variable that will prevent your data from being modified.
A possible way to avoid to (easily) modify javascript variables from the browser console is to either use the get operator (ECMAScript 5) or a getter-function.
To make it possible to define "private" variables, an anonymous function defines the variables in the local scope, so that it is not globally available. (as mentioned in joews' answer)
As mentioned before, this does not make it impossible to manipulate the variables.
Via get operator:
window.app = (function () {
var _foo = 123; // private variable
return {
get foo () { return _foo; }
};
}());
// --- accessing app from the console ---
// app.foo is readable from console, but not modifiable
console.log(app.foo);
app.foo = 234;
console.log(app.foo); // 123
// However, app.foo can still be modified via Object.defineProperty or
// removed with the delete operator
Via getter-function (older browsers, e.g IE < 9):
window.app = (function () {
var _foo = 123; // private variable
return {
foo: function() { return _foo; }
};
}());
// --- accessing app from the console ---
console.log(app.foo()); // 123
// However, the foo function can still be overwritten.
// But at least, the internal _foo variable is unaffected.
app.foo = function () { return 234; }

Protecting a Global Javascript "API" Object

I currently have a Web Application that runs off a global Javascript-based API, and it is initialized like this:
var Api = {
someVar: "test",
someFunction: function() {
return "foo";
}
}
This API is shared across many "Widgets" that live in the Web Application, and they should all run off this single Api instance so they can pass data to each other.
AJAX is currently used to load these Widgets, for example in widgets/mywidget.html, and it's placed in, say, <div id='widget_<random number>'>...</div>
Certain other parts of the code may choose to add more functionality to Api, and it's currently done like this:
Api.myExtension = {
myNewFunction: function() {
return "bar";
}
}
However, some issues arise from this kind of usage:
Problem One: What if one Widget (these may be provided by third-parties) decides to hide some code within, and does something similar to Api = {}, destroying the global Api var everything lives on, and breaking the whole Application? Is it possible to protect this Api variable from being overwritten from outside? Only "extending" is allowed (adding new things), but "removing/changing" is not allowed. i.e.:
Api.foo = { test: "bar" } // allowed
Api.someVar = "changing the existing someVar"; // not allowed
The following code is located "inside" Api, for example:
var Api = {
Debug: {
Messages = new Array,
Write: function() {
Api.Debug.Messages.push("test"); // allowed
}
}
}
Api.Debug.Messages.push("test 2"); // not allowed
Probable Solutions I've Thought Of:
Suppose we simply use frames to resolve this issue. The Apis provided are now separate from each other. However, there's additional overhead when loading Api again and again if I have many Widgets running, and they can no longer communicate with the "Host" of the widgets (the page where frames reside in), for example, I may want to tell the host to show a notification: Api.Notify.Show("Test"), but it cannot do so because this Api is completely independent from other instances, and it cannot communicate with the "Host"
Using something like a "getter" and "setter" function for the Api to be read and written. I'm unsure on how to implement this, so any help on directions on how to implement this is welcome!
A mixture of 1/2?
There's no good way to prevent having a "third party" widget overwrite the a global variable. Generally it is the responsibility of whoever is putting together the final application to ensure that whatever JavaScripts they are using aren't littering the global namespace and conflicting. The best thing you can do in that direction is give your "Api" a nice, unique name.
What I think can help you a lot is something like the "revealing pattern", which would be a way of doing the "getters and setters" you mentioned, plus more if you needed it.
A simple, useless example would be like the following:
var Api = (function () {
// private variable
var myArray = [];
return {
addItem: function (newItem) {
myArray.push(newItem);
},
printItems: function () {
console.log("lots if items");
}
};
})();
Api.addItem("Hello, world");
Api.extensionValue = 5;
I think you should make a clear delineation of what is shared, or "singleton" data, and keep those items private, as with myArray in my example.
Make it a constant:
const Api = "hi";
Api = 0;
alert(Api); //"hi"
Take a look at
Object.freeze
More info here
Here is a code example from Mozilla's page:
var obj = {
prop: function (){},
foo: "bar"
};
// New properties may be added, existing properties may be changed or removed
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
var o = Object.freeze(obj);
assert(Object.isFrozen(obj) === true);
// Now any changes will fail
obj.foo = "quux"; // silently does nothing
obj.quaxxor = "the friendly duck"; // silently doesn't add the property
// ...and in strict mode such attempts will throw TypeErrors
function fail(){
"use strict";
obj.foo = "sparky"; // throws a TypeError
delete obj.quaxxor; // throws a TypeError
obj.sparky = "arf"; // throws a TypeError
}
fail();
// Attempted changes through Object.defineProperty will also throw
Object.defineProperty(obj, "ohai", { value: 17 }); // throws a TypeError
Object.defineProperty(obj, "foo", { value: "eit" }); // throws a TypeError
However browser support is still partial
EDIT: see Kernel James's answer, it's more relevant to your question (freeze will protect the object, but not protect reassigning it. however const will) same issue with limited browser support though.
The only way (at least that I can think of) to protect your global variable is to prevent the Widgets from having a direct access to it. This can be achieved by using frames functions, as you suggested. You should create an object that contains all the functions that the Widgets should be able to use, and pass such to each Widget. For example:
var Api = {
widgetApi = {
someFunction: function(){
// ...
}
},
addWidget:function(){
var temp = this.widgetApi.constructor();
for(var key in this.widgetApi)
temp[key] = clone(this.widgetApi[key]);
return temp;
}
// Include other variables that Widgets can't use
}
This way, the Widgets could execute functions and communicate with the host or global variable Api. To set variables, the Widget would be editing its private object, rather than the global one. For every frame (that represents a Widget), you must initialize or create a copy of the widgetApi object, and probably store it inside an array, in such a way that an instance of a Widget is stored in the main Api object.
For example, given <iframe id="widget"></iframe>
You would do the following:
var widget = document.getElementById("widget");
widget.contentWindow.Api = Api.addWidget();
widget.contentWindow.parent = null;
widget.contentWindow.top = null;
Additionally, in every frame you would need to set the parent and top variables to null so that the Widgets wouldn't be able to access the data of the main frame. I haven't tested this method in a while, so there might be ways to get around setting those variables to null.

can I load a javascript file under a context other than 'window'?

I try to load some external .js files, and have some irresolvable namespace conflicts.
I had the idea of loading some of the files in their own context somehow, replacing the "this" from pointing at the window object to some custom namespace.
example:
first.js:
name = "first";
second.js:
name = "second";
It seems to me that this kind of trick can be very useful. Is it possible at all?
EDIT
seems that replacing "this" does not begin to solve the problem, as it is not the default context for identifier resolution in javascript. this is my test code:
var first = {};
var second = {};
(function(){name = "first";}).call(first);
(function(){name = "second";}).call(second);
document.write('name= '+name+' <br/>\n'); //prints "second"
document.write('first.name= '+first.name+' <br/>\n'); //prints "undefined"
document.write('second.name= '+second.name+' <br/>\n'); //prints "undefined
any ideas?
RESOLUTION
It is not possible. I ended up wiser than I was this morning, and I gave it up.
I recommend these enlightening reading materials for anyone with a similar problem that might want to take a crack at it:
http://jibbering.com/faq/notes/closures/
http://softwareas.com/cross-domain-communication-with-iframes
One idea I've had for doing it without needing modifications to your external JavaScript file is getting the contents of the JavaScript file in an AJAXy way (up to you how you do that) and then put it all in a function using the new Function(code) way, then initialise that with new:
surrogateWindow = new new Function(jsCode)();
Then surrogateWindow is the this of that code. I think that that idea should work.
I'm not clear on your reason for doing this; what are you using this for, exactly?
Wrapping the contents of your second.js in an anonymous function will prevent variables in that file from conflicting with global variables. If you really must have a this set to a particular object that isn't the global object, you could do something like
var differentThis = {};
(function() {
// Contents of second.js go here
}).call(differentThis);
UPDATE
You can't do what you want. You seem to want to access the Variable object, which is the object to which a property is added when you declare a variable in JavaScript. In global code, the Variable object is the global object, so you can access it; within a function this is a property of the execution context that there is no way to access directly.
Even though this is an old question, this answer may still be relevant for some:
When a js file is loaded it automatically gets the window's context. That is not possible to change.
However, if you are trying to avoid conflicts between libraries that you are loading, and you don't have control over those libs, and they don't have a built-in "no-conflict" mechanism, then there is a nice trick -
you can load those into a source-less iframe.
This will make their context to be the window of the iframe, and you will still be able to access the iframe since there is no cross-domain issue here.
You can see this library as an example for use of this technique.
You can load your file in an iframe, the file is not a .js but an HTML file, like:
<html>
<body>
<script>
var $ = parent.$, // you can share objects with the parent, eg: jQuery
localObject = { // your local object definition
name: 'first',
showName: function(){
$('div.name').html( this.name );
}
};
//assign the local object to the custom namespace
parent.customNamespace.object1 = localObject;
</script>
</body>
</html>
The trick is to use parent. to get the javascript objects available in the parent page.
For the code you've written, I think you're misunderstanding some of the way classes work in JavaScript. In Java you can drop the this., but in JavaScript you can't. You'll always need to have this. there. So then your code becomes:
var first = {};
var second = {};
(function(){this.name = "first";}).call(first);
(function(){this.name = "second";}).call(second);
document.write('name= '+name+' <br/>\n'); //prints "undefined"
document.write('first.name= '+first.name+' <br/>\n'); //prints "first"
document.write('second.name= '+second.name+' <br/>\n'); //prints "second"
It would also be good to do it in a more normal class way. I'm not sure exactly what your situation is as I can't see all your code so you might be already doing it this way.
function Something(name) {
this.name = name;
}
var first = new Something("first");
var second = new Something("second");
document.write('name= '+name+' <br/>\n'); //prints "undefined"
document.write('first.name= '+first.name+' <br/>\n'); //prints "first"
document.write('second.name= '+second.name+' <br/>\n'); //prints "second"
Well you could wrap the contents of the js files with something like this:
var externalInterfaceForYourObject = (function(){
//code that defines your object
//this should refer to the current anonymous function, and not the window object
return this;
})();

Categories