I am trying to mock a very simple browser environment that mimics how browsers react to user's change in location variable. As far as I know, users can alter
self.location
location
document.location
window.location
one of those to navigate the current window by either
assigning a string of url directly onto the variable (e.g. self.location = 'http://www.google.com')
or assigning a string onto href inside the location object (e.g. self.location.href = 'http://www.google.com')
or maybe explicitly instantiating a location object.
So my real question is how can I instantiate the browser environment to allow users to assign location variable is anyway they wish and let me retrieve the location variable later on? Sorry if this sounds stupid, (I've never coded in javascript before) but in real world circumstances, do browsers convert string data into location objects through macro or does Javascript have some sort of "implicit constructor" mechanism that can automatically invoke the constructor of a class just by assigning a value?
(I am aware that there are dom libraries available, but I find them quite an overkill as I am only interested in the navigating mechanism.)
Well, there are actually several different answers, here.
First, don't anticipate being on the same page, when retrieving the location.
If a user changed it's value, then your page will change (and your in-memory application with it), so you'd need to do state management using some form of storage (online/offline) there.
In terms of how the object actually works, that isn't exactly JS (in all cases).
Long story short, there's JavaScript the language, and then there's the DOM/BOM.
Document-Object Model and Browser-Object Model.
The DOM is a set of functions/objects that let you interface with the HTML/CSS(as it applies to an element) of the page.
The BOM contains things that relate, not directly to the HTML, but to other parts of web functionality.
console.log( ); is a good example.
JS has no native console object or Console constructor. That's a part of the BOM that's added to a browser's environment, by the browser vendor (or third-party plugin), and is implementation a specific, with no real standard.
Same thing is true for URLs.
JS has a global object (in the BOM, it's called window), but it doesn't have a location natively.
So the implementation -- the "how", is hard to answer.
Some browsers might do it in legitimate JS, while others do it in C++ or C, and old IE even had ActiveX components.
That said, new JS engines do have magic get / set methods which can perform actions, while you set data.
Cutting-edge JS even has Proxies, which are sort of like that, but on steroids (these won't be widely supported everywhere for a few years)...
But older JS engines didn't have those features in place in the native language, so they went off into some other language and wired things up to behave in ways that wouldn't have been supported in JS itself, but were needed to fill out the BOM/DOM.
these days, using a .set might be all you need to grab an instance of a constructor.
So in terms of setting up your own object with similar functionality, you could RegEx parse (or iterate pieces of) the value handed to you.
On your object, you could have magic get/set methods assigned to a property name, where you could then, on a set, either modify all aspects of your current instance (based on value), or save a new instance to the var currently occupied by the old one, passing the new value to the constructor.
...but "where's the Location" constructor is a question that's not going to be answered in any simple way, without going browser by browser, version by version.
In a browser, window is effectively an alias for the global object so self === window.self and:
self.location
location
window.location
are all equivalent.
The location object is documented at MDN: window.location and at MSDN: The Window’s Location Object.
Users can set a location by entering it in the browser's address bar. You might also provide say an input element that they can type into, then a button that assigns the value to window.location (or uses any of the methods in the references provided).
do browsers convert string data into location objects through macro or does Javascript have some sort of "implicit constructor" mechanism that can automatically invoke the constructor of a class just by assigning a value?
The window and location objects are host objects that are not required to have any constructor, they just "are". Even if they are implemented as instances with prototype inheritance, there's no specification requiring users to have access to the constructor.
A new window can be created using:
var newWindow = window.open(...);
which I suppose is like calling a constructor.
When a location object is assigned a new URL, it behaves per the documentation referenced. How that happens is an implementation detail that is not specified, so you can implement it any way you like as long as it behaves like it should. That is the general philosophy behind javascript.
What you're looking for here is a getter/setter pair.
var window = {
get location() {
return {
get href() { return "foo"; },
set href() { /* change stored location data */; },
get port() { return 80; },
set port() { /* change stored location data */; },
/* etc ... */
};
}
set location() {
// change stored location data
}
};
Related
I've run across a JavaScript library that implement a cross-browser WeakMap in ES5. (WeakMap is slated for ES6.)
How can this possibly work without support in the JavaScript language itself?
Edit: Just to be clear, I'm referring to a Weak Map, not a regular Map. I tested this project out using Chrome's profiler and the keys are not held by strong references. They get GC'ed without having to remove them from the WeakMap.
It took me a while to grok the code, but then it hit me: the key itself is used to store a reference to the value.
For example, several layers into set it does
defProp(obj, globalID, { value: store });
where defProp has been defined to be Object.defineProperty, obj is the key, globalID is a guid and store is a storage object that contains the value.
Then down in get it looks up the value with
obj[globalID];
This is very clever. The WeakMap doesn't actually contain a reference to anything (weak or otherwise)-- it just sets up a policy of where to secretly store the value. The use of Object.defineProperty means that you won't accidentally discover the value storage-- you have to know the magic guid to look it up.
Since the key directly refers to the value (and the WeakMap doesn't refer to it), when all references to the key are gone, it gets GCed like normal.
Web App Model
Suppose I have a sensitive JS object by which I can do critical stuff. My requirement is that I would like to wrap this object entirely such that no one can access it. Here is my pattern to wrap this object.
var proxy = (function (window){
// A private reference to my critical object (i.e. big apple)
var bigApple = window.bigApple;
// Delete this property so that no one else can access it
delete window.bigApple;
// Oooah, It's mine! I'm now eating it :)
// Public APIs exposed globally
return {
doStuffWithBigApple: function (){
// The Script element being executed now
var who = document.currentScript;
// Access control
if(isLegitimate(who)){
return bigApple.doStuff();
}
}
};
}) (window);
By this code I export a public literal object named proxy so that every one can access it.
What is that isLegitimate? It is an abstract function to be implemented which decides which script elements access to which methods of my big apple. The decision is made with regard to src attribute of the script element. (i.e. their domain)
Others use this public API like this:
proxy.doStuffWithBigApple();
Attack Model
In my web app there are placeholders for advertising such that external contents including JavaScript codes could be loaded and get executed. All of these external resources eagerly would want to access my big apple.
Note: Those are added after my scripts resulting in there is no access to the original window.bigApple.
My Question
Is there any circumventing way for my security model?
Critical edges:
Changing src attribute at parse-time. --- Not possible, because src can only be set once.
Adding script element at run-time --- No problem is raised
Your idea of creating a proxy is good imo, however, if you have access to ES6, why not looking into Proxy? I think it does what you want out-of-the-box.
The MDN provides good examples on how do traps for value validation in a setter, etc.
EDIT :
Possible trap I have imagined :
document.currentScript is not supported in IE. So if you care about it and decide to polyfill it/use a pre-exisiting polyfill, make sure it is secure. Or it could be used to modify on the fly the external script url returned by document.currentScript and skew the proxy. I don't know if this could happen in real life tho.
This way for protecting JavaScript objects has a very significant issue which should be addressed, otherwise this way will not work properly.
MDN noted on this API that:
It's important to note that this will not reference the <script> element if the code in the script is being called as a callback or event handler; it will only reference the element while it's initially being processed.
Thus, any call to proxy.doStuffWithBigApple(); inside callbacks and event handlers might lead to misbehaving of your framework.
I have a big object defined in the global scope called global. I would like to dynamically find all the referenced properties under my variable global. That is, all the properties that were accessed during the execution of the code.
I want to do static code analysis to extract all the referenced properties under my variable. I can search for these patterns: global.PROPERTY_NAME AND global[PROPERTY_NAM]. However, what about the complicated cases like these ones
var tmp="PROPERTY_NAME";
global[tmp]
OR
var tmp=global;
tmp.PROPERTY_NAME
and the other ones?
I don't want to get all the variable's properties. I only want a list of the referenced ONES!! the properties that were referenced in my source code only
After your edit:
What you're looking for is JavaScript Proxy objects. Here is a tutorial on how to do this using them.
Proxy objects let you wrap an object and execute a method whenever its properties are accessed. Unfortunately as it currently stands they are not widely supported.
This is currently only way in JavaScript to accomplish this without changing your original global object.
You can turn them on in Chrome by enabling experimental JavaScript in the about:flags tab.
Before your edit:
The feature you're looking for is called reflection, JavaScript supports it well and natively
Here is some code that iterates through an object and gets its properties
for(var prop in global){
if(global.hasOwnProperty(prop)){ //this is to only get its properties and not its prototype's
alert(prop+" => "+global[prop]);
}
}
This is fairly cross-browser. More modern browsers allow you to do this in simpler ways like Object.keys(global) which returns an array containing all its enumerable properties, or Object.getOwnPropertyNames(global) which returns both enumerable and not-enumerable properties.
Due to the dynamic nature of JavaScript you won't achieve that with static code analysis. Think about cases like this:
var prop = document.getElementById('prop').value;
global[prop];
Impossible. The alternative, dynamic analysis, would mean that you modify your global object to log access to its properties, then run the code. This is easily possible in JavaScript but it won't help you either because how would you assure that you have covered every possible access? Especially in a 5 MB JavaScript, there are most likely edge cases that you will oversee.
So, if you can't narrow down your requirement, it won't be possible.
Is it possible to augment a DOM element by adding a new property to it as augmenting a normal JavaScript object? For example, can we add a new property of a button or an input, say a value that indicates when was the last time user click on it?
Yes. You can sometimes find it described as bad form. The obvious drawback is that in later versions of Web standards, the DOM element may have new properties added to it which will clash with your custom property, so if you do use this technique, choose a name that is unlikely to clash, e.g. incorporate your company name into it.
Another potential problem is that DOM objects in some browsers are not properly garbage collected. Instead they are reference counted. This means that if you create a system of circular references through custom properties, you may cause a memory leak.
For example, this may cause a memory leak, because there is a circular reference between the DOM and javascript objects:
var myJsObj = {};
var myDomElt = document.getElementById('myId');
myJsObj.domElt = myDomElt;
myDomElt.jsObj = myJsObj;
It is, however, safe to add a property that just holds a primitive value:
myDomElt.fullTitle = "My DOM Element";
Although the warning that the DOM standard may change is still important; what if HTML7 defines a fullTitle property on DOM elements? Your site will have unpredictable behaviour.
An alternative is to use the $.data feature of jQuery:
$(myDomElt).data('fullTitle', 'My Dom Element');
var retrieved = $(myDomElt).data('fullTitle');
It's possible but is a really bad idea. Daniel Earwicker's answer mentions some reasons why it's a bad idea. I'd add these:
DOM nodes are host objects and host objects can do what they like. Specifically, there is no requirement in the ECMAScript spec for host objects to allow this kind of extension, so browsers are not obliged to allow it. In particular, new browsers may choose not to, and existing code relying on it will break.
Not all host objects in existing browsers allow it. For example, text nodes and all ActiveX objects (such as XMLHttpRequest in older IE and XMLDOM objects, used for parsing XML) in IE do not, and failure behaviour varies from throwing errors to silent failure.
In IE, the ability to add properties can be switched off for a whole document, including all nodes within the document, with the line document.expando = false;. Thus if any of the code in your page includes this line, all code relying on adding properties to DOM nodes will fail.
I am working on a migration platform to migrate web applications from a device to another. I am extending it to add the support for preserving JavaScript state.
My main task is to create a file representing the current state of the executing application, to transmit it to another device and to reload the state in the destination device.
The basic solution I adopted is to navigate the window object and to save all its descendant properties using JSON as base format for exportation and extending it to implement some features:
preserving object reference, even if cyclic (dojox.json.ref library)
support for timers
Date
non-numericproperties of arrays
reference to DOM elements
The most important task I need to solve now is exportation of closures. At this moment I didn't know how to implement this feature.
I read about the internal EcmaScript property [[scope]] containing the scope chain of a function, a list-like object composed by all the nested activation context of the function. Unfortunately it is not accessible by JavaScript.
Anyone know if there is a way to directly access the [[scope]] property? Or another way to preserve the state of a closure?
This sounds like an impossible feat as you would need access to the references stored in each variable.
The best solution would probably be to first refactor your code into storing state on an available object - that way you could easily use JSON.stringify/parse to save/restore it.
So go from
var myFuncWithScope = (function() {
var variable = 0;
return function() {
return variable++;
}
})();
var serializedState = .... // no can do
to
var state = {
myScope = {
variable: 0
}
};
var myFuncWithoutScope = function(){
return state.myScope.variable++;
}
var serializedState = JSON.stringify(state);
From where are you executing? If you are a native app or web browser extension you may have some hope, via internal access to whichever scripting engine it's using. But from a script in web content, there is no hope.
[[Scope]] is one ECMAScript internal property that you cannot access or preserve from inside the interpreter, but far from the only one; almost all of the [[...]] properties are not accessible. Function code references, prototypes, properties, enumerability, owner context, listeners, everything to do with host objects (such as DOM nodes)... there are infinitely many ways to fail.
You can't preserve or migrate web applications without requiring them to follow some strict rules to avoid all but the most basic JS features.