"var" declarations shadowing properties from Window.prototype
On Fri, Aug 10, 2012 at 4:11 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
Neither ES5 or ES5.1 does a "own" check for either global var or function declarations. See 10.5 step 5.c/8.b each of which calls HasBinding 10.2.1.2.1 which does a [[HasProperty]] which, if necessary, looks up the [[Prototype]] chain.
The change made in ES5.1 (renumbered existing ES5 10.5 step 5.e as 5.f and inserted a new step 5.e + substeps related to dealing with function declarations that over-wrote accessor properties (both own or inherited) of the global object as well function declarations that tried to redefined non-configurable global object properties. This change was motivated by bugzilla.mozilla.org/show_bug.cgi?id=577325 and the es-discuss thread referenced in that bug.
Note that the ES5.1 change only related to function declarations. ES5.1 did not make any changes to the specified semantics of global var declarations.
I just reviewed test262 and there aren't any tests that are specifically dedicated, however there is a ticket filed here: ecmascript#315
First, apologies for my posts based on partial knowledge -- not that I'll ever have total knowledge, but clearly a lot of people were confused about what ES5 and 5.1 said (me included!) vs. the post-ES5.1 erratum that engines implemented. It did not help that the WebKit IDL binding machinery introduced a mitigating effect, IDL attributes as "own" global properties, which benefited only some browsers.
Vendor prefixes still suck, btw. :-|
Onward to what we might do:
Allen Wirfs-Brock wrote:
These post ES5.1 errata was based upon these desired semantics:
- "variable" accesses that bind to inherited properties of the global object should return the current value of the inherited property. (note such "variable" accesses may be to properties created by function declarations)
You mean unqualified Identifier expressions here, right? Of course, this cannot change, it goes back to the dawn of JS and would break the web if we lost it.
- "variable" assignments to inherited properties of the global object should be equivalent to a [[Put]] to the global object. Whether or not a own property is created depends upon the [[Writable]] attribute of the inherited property and the extensible internal property of the global object.
Yes, same as with any object.
- global function and var declarations always create own properties of the global object.
So far so good.
If an inherited property of the same name already exists it is over-ridden with an own property.
Isn't this sentence really 4, below?
- The declaration instantiation rules relating to pre-existing bindings only consider own properties of the global object. Inherited properties of the global object have no effect upon the processing of function and var declarations.
This is the incompatible change from ES1-5.1 and reality that I question whether we can get away with.
You didn't give motivation for it. Obviously the motivation involves not wanting var declarations to be trumped by non-writable/non-configurable properties of the global object's prototype or grand-proto, etc. But do we have such properties?
One solution is to say that global proto-properties cannot be non-writable. I think that's an effective compatibility constraint already.
ES5 made the "own" property from ES3, undefined AKA this.undefined in global code, non-writable and non-configurable, but we have separate logic to allow 'var undefined;' (which is all over the web). Please correct me if I'm mistaken here. This is a different case, because "own" and not involving the prototype chain.
Supporting requirements 3&4 are where the "own" property checks were introduced.
I don't see 3, first sentence, as novel or at issue. If (and only if) a new property is bound by var, it will be "own". And function always blows away any prior configurable, etc. (10.5 step 5(e)), binding.
However, I don't thing we can just drop them without some careful thought.
Always think carefullly. But also think about this: we have shipped 4 and it is hurting.
If we did that, we would reintroduce problems related to global declarations firing inherited setters and also interactions between inherited property attributes and global declarations.
This is two-edged. The object detection code that people copied for indexedDB, combined with WebIDL and the IndexedDB spec based on WebIDL, wants inherited properties to prevent var from shadowing:
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB || ...
This isn't the last such case of coincident naming derived from vendor-prefixed names.
It feel like we are still playing semantics ping-pong with compat. bug paddles. I'm not sure that there is an ideal solution.
On the Web, compat matters more. We had problems with var being sensitive to the prototype chain, particularly with proxies prototyped in SpiderMonkey and mutable proto. These were arguably implementation bugs, though.
Compat vs. implementation, compat wins. Rock smashes scissors.
Setters predate ES5 and so do host-object readonly properties. I do not believe these were a problem for the 15+ years before engines tried switching to get ahead of ES5.1 by fixing ecmascript#78 and shadowing proto-properties with var.
Engines really all did, until very recently (and some popular ones may still do), refuse to shadow when var restates a proto-property by name.
Perhaps we need to consider var and function declarations separately. Perhaps we need be have different redeclaration rules for own and for inherited global object properties. In any chase I think we need to do a more care analysis then just fixing this bug and it needs be be coordinated between the ES spec. and WebIDL.
Agreed, but we need to avoid delays and compounding hacks. Cameron's solution (3), having the global [[DefineOwnProperty]] check the global's proto chain, is interesting. It could be layered on top of the compat break wanted by ecmascript#78. But is this playing Jenga? I smell it.
I think we might be better off looking again at the global proto-chain proxy bugs, which were more implementation than spec but also partly "spec", that we suffered, and seeing if they shouldn't be constrained or ruled out.
On Aug 10, 2012, at 3:25 PM, Brendan Eich wrote:
First, apologies for my posts based on partial knowledge -- not that I'll ever have total knowledge, but clearly a lot of people were confused about what ES5 and 5.1 said (me included!) vs. the post-ES5.1 erratum that engines implemented. It did not help that the WebKit IDL binding machinery introduced a mitigating effect, IDL attributes as "own" global properties, which benefited only some browsers.
Vendor prefixes still suck, btw. :-|
Onward to what we might do:
Allen Wirfs-Brock wrote:
These post ES5.1 errata was based upon these desired semantics:
- "variable" accesses that bind to inherited properties of the global object should return the current value of the inherited property. (note such "variable" accesses may be to properties created by function declarations)
You mean unqualified Identifier expressions here, right? Of course, this cannot change, it goes back to the dawn of JS and would break the web if we lost it.
- "variable" assignments to inherited properties of the global object should be equivalent to a [[Put]] to the global object. Whether or not a own property is created depends upon the [[Writable]] attribute of the inherited property and the extensible internal property of the global object.
Yes, same as with any object.
- global function and var declarations always create own properties of the global object.
So far so good.
If an inherited property of the same name already exists it is over-ridden with an own property.
Isn't this sentence really 4, below?
true
- The declaration instantiation rules relating to pre-existing bindings only consider own properties of the global object. Inherited properties of the global object have no effect upon the processing of function and var declarations.
This is the incompatible change from ES1-5.1 and reality that I question whether we can get away with.
True, for var declarations. For function declarations it changed in 5.1 as a result of bugzilla.mozilla.org/show_bug.cgi?id=577325 which initially concerned what happens with a global function declaration when there is an inherited access with the same name. Is the inherited setter called? We all concluded that it shouldn't. Rule 4 above is essentially an expression of that idea.
Note this is a real world situation as Jonas notes in bugzilla.mozilla.org/show_bug.cgi?id=781739#c9 :
Since we are on the subject, a similar thing which have been breaking in Firefox but working in Chrome is code which does:
function onmessage(event) { ... }
in global scope in workers.
In Firefox the global scope object has on its prototype chain a setter for the 'onmessage' property. However this setter isn't run and instead a shadowing variable is declared.
In Chrome the global scope object has the setter on the object itself, causing the setter to run.
This caused the page to work in Chrome since the setter is run and thus an event handler was registered, while in Firefox a "expando" variable is created and nothing else happens.
In this case, firing the setter is perhaps what the programmer wanted, even if it is a terrible way to accomplish that end. However, the opposite could easily be true. The programmer has a working program with a function declaration for Foo. Sometime latter the browser adds an unrelated accessor property coincidentally named Foo to window's prototype. The program stops working when the declaration doesn't over-ride the inherited property but instead calls some setter with the wrong shaped function..
You didn't give motivation for it. Obviously the motivation involves not wanting var declarations to be trumped by non-writable/non-configurable properties of the global object's prototype or grand-proto, etc. But do we have such properties?
It started with functions declarations as per bug 577325 between ES5 and ES5.1. Post ES5 the var issue came up as ecmascript#78 The attribute sniffing logic in the ES5.1 change was about trying to identify properties that have the characteristics of those created by function declarations (which should be overwriteable by subsequent declarations) from those that don't.
One solution is to say that global proto-properties cannot be non-writable. I think that's an effective compatibility constraint already.
By restricting [[DefineOwnProperty]] on proto-prototypes??
ES5 made the "own" property from ES3, undefined AKA this.undefined in global code, non-writable and non-configurable, but we have separate logic to allow 'var undefined;' (which is all over the web). Please correct me if I'm mistaken here. This is a different case, because "own" and not involving the prototype chain.
I don't think there is anything special about the global named undefined. As long as it is a own property of the global object (which is is spec'ed to be) 'var undefined' is fine because redundant var declarations don't do anything anything. If undefined was inherited from window.prototype it would be a different story.
Supporting requirements 3&4 are where the "own" property checks were introduced.
I don't see 3, first sentence, as novel or at issue. If (and only if) a new property is bound by var, it will be "own". And function always blows away any prior configurable, etc. (10.5 step 5(e)), binding.
What you just said about functions is how we justified over-riding inherited function valued properties.
But what about var. Rule 3 also means that 'var foo' guarantees you a own property of the global object. Otherwise we have the var analog of the the above function Foo issue.
How do we know whether 'var onClick = null' is intended to call an inherited setter or to create and initialize a global object property. Since there is an explicit 'var' that latter seems like a reasonable guess.
However, I don't thing we can just drop them without some careful thought.
Always think carefullly. But also think about this: we have shipped 4 and it is hurting.
I know, somebody gets hurt however this goes. Some are already being hurt by interop issues.
The least painful solution to the immediate FF issue may be to make indexedDB an own property of the window object rather than an inherited property. Perhaps do that for all variable-like properties (those that down need real get/set logic) of the window object.
If we did that, we would reintroduce problems related to global declarations firing inherited setters and also interactions between inherited property attributes and global declarations.
This is two-edged. The object detection code that people copied for indexedDB, combined with WebIDL and the IndexedDB spec based on WebIDL, wants inherited properties to prevent var from shadowing:
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB || ... if only that had recommended: window.indexedDB = window.indexedDB || ...
This isn't the last such case of coincident naming derived from vendor-prefixed names.
My sense is that the mistake may be making these properties be inherited. Perhaps WebIDL instead of thinking about the window object as just another object with various properties that are inherited from a prototype should be thinking about what global vars and functions it is implicitly declaring. ie, what is the standard prologue of var and function declarations that are processed as an ECMAScipt program production prior to processing any user programs.
It feel like we are still playing semantics ping-pong with compat. bug paddles. I'm not sure that there is an ideal solution.
On the Web, compat matters more. We had problems with var being sensitive to the prototype chain, particularly with proxies prototyped in SpiderMonkey and mutable proto. These were arguably implementation bugs, though.
Compat vs. implementation, compat wins. Rock smashes scissors.
Yes, but sometimes its just a matter which compatibility problem is noticed first. The pingponging comes opposing compatibility requirements. When those exist somebody has to loose.
Setters predate ES5 and so do host-object readonly properties. I do not believe these were a problem for the 15+ years before engines tried switching to get ahead of ES5.1 by fixing ecmascript#78 and shadowing proto-properties with var.
but isn't widespread use of inherited setters to implement DOM properties fairly recent?
Engines really all did, until very recently (and some popular ones may still do), refuse to shadow when var restates a proto-property by name.
Perhaps we need to consider var and function declarations separately. Perhaps we need be have different redeclaration rules for own and for inherited global object properties. In any chase I think we need to do a more care analysis then just fixing this bug and it needs be be coordinated between the ES spec. and WebIDL.
Agreed, but we need to avoid delays and compounding hacks. Cameron's solution (3), having the global [[DefineOwnProperty]] check the global's proto chain, is interesting. It could be layered on top of the compat break wanted by ecmascript#78. But is this playing Jenga? I smell it.
I don't think [[DefineOwnProperty]] is the place to do this. There are already too many complex interactions the spec. between declaration instantiation, environment record methods, internal methods like [[DefineOwnProperty]], etc. Plus WebIDL probably shouldn't be placed in the position of have to change how ES global declarations work. Instead, I think we need to mutually decide the semantic rules for globals including those introduce by Web APIs. The 4 rules discussed above were a start at that. Also, as I mention above, I don't see why WebIDL needs to restrict itself to think at the level of properties when it really is defining additional built-in declaration. It pretty much can do what it wants in its own object but perhaps for globals it should be following the same rules as regular ES built-ins.
I think we might be better off looking again at the global proto-chain proxy bugs, which were more implementation than spec but also partly "spec", that we suffered, and seeing if they shouldn't be constrained or ruled out.
that too
On Fri, Aug 10, 2012 at 5:17 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
On Aug 10, 2012, at 3:25 PM, Brendan Eich wrote:
First, apologies for my posts based on partial knowledge -- not that I'll ever have total knowledge, but clearly a lot of people were confused about what ES5 and 5.1 said (me included!) vs. the post-ES5.1 erratum that engines implemented. It did not help that the WebKit IDL binding machinery introduced a mitigating effect, IDL attributes as "own" global properties, which benefited only some browsers.
Vendor prefixes still suck, btw. :-|
Onward to what we might do:
Allen Wirfs-Brock wrote:
These post ES5.1 errata was based upon these desired semantics:
- "variable" accesses that bind to inherited properties of the global object
should return the current value of the inherited property. (note such "variable" accesses
may be to properties created by function declarations)
You mean unqualified Identifier expressions here, right? Of course, this cannot change, it goes back to the dawn of JS and would break the web if we lost it.
- "variable" assignments to inherited properties of the global object should
be equivalent to a [[Put]] to the global object. Whether or not a own property
is created depends upon the [[Writable]] attribute of the inherited property
and the extensible internal property of the global object.
Yes, same as with any object.
- global function and var declarations always create own properties of the
global object.
So far so good.
If an inherited property of the same name already exists it is
over-ridden with an own property.
Isn't this sentence really 4, below?
true
- The declaration instantiation rules relating to pre-existing bindings
only consider own properties of the global object. Inherited properties of the
global object have no effect upon the processing of function and var
declarations.
This is the incompatible change from ES1-5.1 and reality that I question whether we can get away with.
True, for var declarations. For function declarations it changed in 5.1 as a result of bugzilla.mozilla.org/show_bug.cgi?id=577325 which initially concerned what happens with a global function declaration when there is an inherited access with the same name. Is the inherited setter called? We all concluded that it shouldn't. Rule 4 above is essentially an expression of that idea.
Note this is a real world situation as Jonas notes in bugzilla.mozilla.org/show_bug.cgi?id=781739#c9 :
Since we are on the subject, a similar thing which have been breaking in Firefox but working in Chrome is code which does:
function onmessage(event) { ... }
in global scope in workers.
In Firefox the global scope object has on its prototype chain a setter for the 'onmessage' property. However this setter isn't run and instead a shadowing variable is declared.
In Chrome the global scope object has the setter on the object itself, causing the setter to run.
This caused the page to work in Chrome since the setter is run and thus an event handler was registered, while in Firefox a "expando" variable is created and nothing else happens.
In this case, firing the setter is perhaps what the programmer wanted, even if it is a terrible way to accomplish that end. However, the opposite could easily be true. The programmer has a working program with a function declaration for Foo. Sometime latter the browser adds an unrelated accessor property coincidentally named Foo to window's prototype. The program stops working when the declaration doesn't over-ride the inherited property but instead calls some setter with the wrong shaped function..
You didn't give motivation for it. Obviously the motivation involves not wanting var declarations to be trumped by non-writable/non-configurable properties of the global object's prototype or grand-proto, etc. But do we have such properties?
It started with functions declarations as per bug 577325 between ES5 and ES5.1. Post ES5 the var issue came up as ecmascript#78 The attribute sniffing logic in the ES5.1 change was about trying to identify properties that have the characteristics of those created by function declarations (which should be overwriteable by subsequent declarations) from those that don't.
One solution is to say that global proto-properties cannot be non-writable. I think that's an effective compatibility constraint already.
By restricting [[DefineOwnProperty]] on proto-prototypes??
ES5 made the "own" property from ES3, undefined AKA this.undefined in global code, non-writable and non-configurable, but we have separate logic to allow 'var undefined;' (which is all over the web). Please correct me if I'm mistaken here. This is a different case, because "own" and not involving the prototype chain.
I don't think there is anything special about the global named undefined. As long as it is a own property of the global object (which is is spec'ed to be) 'var undefined' is fine because redundant var declarations don't do anything anything. If undefined was inherited from window.prototype it would be a different story.
Supporting requirements 3&4 are where the "own" property checks were introduced.
I don't see 3, first sentence, as novel or at issue. If (and only if) a new property is bound by var, it will be "own". And function always blows away any prior configurable, etc. (10.5 step 5(e)), binding.
What you just said about functions is how we justified over-riding inherited function valued properties.
But what about var. Rule 3 also means that 'var foo' guarantees you a own property of the global object. Otherwise we have the var analog of the the above function Foo issue.
How do we know whether 'var onClick = null' is intended to call an inherited setter or to create and initialize a global object property. Since there is an explicit 'var' that latter seems like a reasonable guess.
However, I don't thing we can just drop them without some careful thought.
Always think carefullly. But also think about this: we have shipped 4 and it is hurting.
I know, somebody gets hurt however this goes. Some are already being hurt by interop issues.
The least painful solution to the immediate FF issue may be to make indexedDB an own property of the window object rather than an inherited property. Perhaps do that for all variable-like properties (those that down need real get/set logic) of the window object.
If we did that, we would reintroduce problems related to global declarations firing inherited setters and also interactions between inherited property attributes and global declarations.
This is two-edged. The object detection code that people copied for indexedDB, combined with WebIDL and the IndexedDB spec based on WebIDL, wants inherited properties to prevent var from shadowing:
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB || ...
if only that had recommended: window.indexedDB = window.indexedDB ||
...
This isn't the last such case of coincident naming derived from vendor-prefixed names.
My sense is that the mistake may be making these properties be inherited. Perhaps WebIDL instead of thinking about the window object as just another object with various properties that are inherited from a prototype should be thinking about what global vars and functions it is implicitly declaring. ie, what is the standard prologue of var and function declarations that are processed as an ECMAScipt program production prior to processing any user programs.
It feel like we are still playing semantics ping-pong with compat. bug paddles. I'm not sure that there is an ideal solution.
On the Web, compat matters more. We had problems with var being sensitive to the prototype chain, particularly with proxies prototyped in SpiderMonkey and mutable proto. These were arguably implementation bugs, though.
Compat vs. implementation, compat wins. Rock smashes scissors.
Yes, but sometimes its just a matter which compatibility problem is noticed first. The pingponging comes opposing compatibility requirements. When those exist somebody has to loose.
Setters predate ES5 and so do host-object readonly properties. I do not believe these were a problem for the 15+ years before engines tried switching to get ahead of ES5.1 by fixing ecmascript#78 and shadowing proto-properties with var.
but isn't widespread use of inherited setters to implement DOM properties fairly recent?
Engines really all did, until very recently (and some popular ones may still do), refuse to shadow when var restates a proto-property by name.
Perhaps we need to consider var and function declarations separately. Perhaps we need be have different redeclaration rules for own and for inherited global object properties. In any chase I think we need to do a more care analysis then just fixing this bug and it needs be be coordinated between the ES spec. and WebIDL.
Agreed, but we need to avoid delays and compounding hacks. Cameron's solution (3), having the global [[DefineOwnProperty]] check the global's proto chain, is interesting. It could be layered on top of the compat break wanted by ecmascript#78. But is this playing Jenga? I smell it.
I don't think [[DefineOwnProperty]] is the place to do this. There are already too many complex interactions the spec. between declaration instantiation, environment record methods, internal methods like [[DefineOwnProperty]], etc. Plus WebIDL probably shouldn't be placed in the position of have to change how ES global declarations work. Instead, I think we need to mutually decide the semantic rules for globals including those introduce by Web APIs. The 4 rules discussed above were a start at that. Also, as I mention above, I don't see why WebIDL needs to restrict itself to think at the level of properties when it really is defining additional built-in declaration. It pretty much can do what it wants in its own object but perhaps for globals it should be following the same rules as regular ES built-ins.
I think we might be better off looking again at the global proto-chain proxy bugs, which were more implementation than spec but also partly "spec", that we suffered, and seeing if they shouldn't be constrained or ruled out.
that too
One potential solution that I think we should keep in mind is to declare that WebIDL properties on global objects doesn't go on the prototype chain, but rather on the global objects themselves. That seems like it'll reduce a lot of the foot guns since they will behave much more like "normal" javascript properties. I.e. we'd get a model that is much simpler for developers.
I feel like whatever we'll do, the interactions with 'var X' and 'function X()' will end up getting complex when there are collisions with properties on the prototype chain.
We'd likely also have to allow some "readonly" properties to be overwritten using arbitrary values. I.e. evaluating |indexedDB = 42;| or |var indexedDB = 42;| in the global scope would actually change window.indexedDB to the value 42. We end up having to do that right now anyway for a lot of properties since web pages otherwise break due to name collisions with random global variables.
This also has the advantage that it's a change "only" on the WebIDL side. So we'd be free to leave ES with fewer webcompat restrictions when defining how to deal with var/function and properties on the prototype chain.
It also has the advantage that Chrome already does this so we have some basis for thinking that this could be web compatible. (Though Chrome does this for not just global objects, which is a different topic).
/ Jonas
Allen Wirfs-Brock wrote:
On Aug 10, 2012, at 3:25 PM, Brendan Eich wrote:
- The declaration instantiation rules relating to pre-existing bindings only consider own properties of the global object. Inherited properties of the global object have no effect upon the processing of function and var declarations.
This is the incompatible change from ES1-5.1 and reality that I question whether we can get away with.
True, for var declarations. For function declarations it changed in 5.1 as a result of bugzilla.mozilla.org/show_bug.cgi?id=577325 which initially concerned what happens with a global function declaration when there is an inherited access with the same name. Is the inherited setter called? We all concluded that it shouldn't. Rule 4 above is essentially an expression of that idea.
Yes, I remember. 'function' always differed from 'var' in JS: it would create the binding (or throw trying, ES5+).
Note this is a real world situation as Jonas notes in bugzilla.mozilla.org/show_bug.cgi?id=781739#c9 :
Since we are on the subject, a similar thing which have been breaking in Firefox but working in Chrome is code which does:
function onmessage(event) { ... }
in global scope in workers.
In Firefox the global scope object has on its prototype chain a setter for the 'onmessage' property. However this setter isn't run and instead a shadowing variable is declared.
In Chrome the global scope object has the setter on the object itself, causing the setter to run.
This caused the page to work in Chrome since the setter is run and thus an event handler was registered, while in Firefox a "expando" variable is created and nothing else happens.
Yeah, once more Chrome scores.
In primordial JS, writing function onload(){...} defined an onload handler. WYSIWYG and Occam's razor (perhaps too close a shave).
In this case, firing the setter is perhaps what the programmer wanted, even if it is a terrible way to accomplish that end.
It's not that bad if you start from the DOM level 0, especially window.onload being the same binding as function onload() {}.
However, the opposite could easily be true. The programmer has a working program with a function declaration for Foo. Sometime latter the browser adds an unrelated accessor property coincidentally named Foo to window's prototype. The program stops working when the declaration doesn't over-ride the inherited property but instead calls some setter with the wrong shaped function..
Yup. The "on" convention I borrowed from HyperCard, with all lowercase, helps avoid collisions for event handlers. Convention helps, but it's not enough.
OTOH the real challenge is not functions (Chrome got it right, I think). The issue is 'var' not creating a shadowing property, or else working because WebIDL does something different from its current spec.
You didn't give motivation for it. Obviously the motivation involves not wanting var declarations to be trumped by non-writable/non-configurable properties of the global object's prototype or grand-proto, etc. But do we have such properties?
It started with functions declarations as per bug 577325 between ES5 and ES5.1. Post ES5 the var issue came up as ecmascript#78 The attribute sniffing logic in the ES5.1 change was about trying to identify properties that have the characteristics of those created by function declarations (which should be overwriteable by subsequent declarations) from those that don't.
One solution is to say that global proto-properties cannot be non-writable. I think that's an effective compatibility constraint already. By restricting [[DefineOwnProperty]] on proto-prototypes??
No, just by convention. Jonas points out that
var indexedDB = window.mozIndexedDB || ... || window.indexedDB;
still will fail if the engine does not create a shadowing var, because indexedDB is a get-only accessor so the assignment in the initialiser will fail for want of set.
ES5 made the "own" property from ES3, undefined AKA this.undefined in global code, non-writable and non-configurable, but we have separate logic to allow 'var undefined;' (which is all over the web). Please correct me if I'm mistaken here. This is a different case, because "own" and not involving the prototype chain.
I don't think there is anything special about the global named undefined. As long as it is a own property of the global object (which is is spec'ed to be) 'var undefined' is fine because redundant var declarations don't do anything anything. If undefined was inherited from window.prototype it would be a different story.
Agreed, just noting that const x = 42; var x; is not tolerated in ES6 as proposed (right?).
Supporting requirements 3&4 are where the "own" property checks were introduced.
I don't see 3, first sentence, as novel or at issue. If (and only if) a new property is bound by var, it will be "own". And function always blows away any prior configurable, etc. (10.5 step 5(e)), binding.
What you just said about functions is how we justified over-riding inherited function valued properties.
Yes. 'function' and 'var' differ. I think we agree -- I hope you're not trying to make 'var' more like 'function'.
But what about var. Rule 3 also means that 'var foo' guarantees you a own property of the global object. Otherwise we have the var analog of the the above function Foo issue.
That is not a problem so much in practice, until recently :-|.
How do we know whether 'var onClick = null' is intended to call an inherited setter or to create and initialize a global object property. Since there is an explicit 'var' that latter seems like a reasonable guess.
LOL, the capital C means you are not touching a window.onclick handler. Plus, no such event handler on window. But you're right, we can't have it both ways. This strongly argues for global WebIDL-inherited properties being "own".
I see Jonas posted a followup making the same observation.
Jonas Sicking wrote:
One potential solution that I think we should keep in mind is to declare that WebIDL propertieson global objects doesn't go on the prototype chain, but rather on the global objects themselves. That seems like it'll reduce a lot of the foot guns since they will behave much more like "normal" javascript properties. I.e. we'd get a model that is much simpler for developers.
+1 or more.
I feel like whatever we'll do, the interactions with 'var X' and 'function X()' will end up getting complex when there are collisions with properties on the prototype chain.
Yes.
All my fault for making objects be scopes. Sorry. But we should go with it, since object detection is a virtue (and var is needed for strict mode or just good hygiene).
var detect = window.mozDetect || window.webkitDetect || ... || window.detect;
with detect a get-only accessor must work. The simplest way to ensure this is to make all the window.* props "own".
We'd likely also have to allow some "readonly" properties to be overwritten using arbitrary values. I.e. evaluating |indexedDB = 42;| or |var indexedDB = 42;| in the global scope would actually change window.indexedDB to the value 42. We end up having to do that right now anyway for a lot of properties since web pages otherwise break due to name collisions with random global variables.
Yes, so set as well as get accessor functions.
This also has the advantage that it's a change "only" on the WebIDL side. So we'd be free to leave ES with fewer webcompat restrictions when defining how to deal with var/function and properties on the prototype chain.
Cameron may have to take the hit. I had mistakenly thought we could stick to ES5.1 and fix SpiderMonkey, but it's just not so.
It also has the advantage that Chrome already does this so we have some basis for thinking that this could be web compatible. (Though Chrome does this for not just global objects, which is a different topic).
Could you say more? I can see the benefit in having an odd-man-out exception for globals only. Does the "own" promotion hurt elsewhere in WebKit's DOM bindings?
On Fri, Aug 10, 2012 at 11:17 PM, Brendan Eich <brendan at mozilla.org> wrote:
Jonas Sicking wrote:
One potential solution that I think we should keep in mind is to declare that WebIDL propertieson global objects doesn't go on the
prototype chain, but rather on the global objects themselves. That seems like it'll reduce a lot of the foot guns since they will behave much more like "normal" javascript properties. I.e. we'd get a model that is much simpler for developers.
+1 or more.
I feel like whatever we'll do, the interactions with 'var X' and 'function X()' will end up getting complex when there are collisions with properties on the prototype chain.
Yes.
All my fault for making objects be scopes. Sorry. But we should go with it, since object detection is a virtue (and var is needed for strict mode or just good hygiene).
var detect = window.mozDetect || window.webkitDetect || ... || window.detect;
with detect a get-only accessor must work. The simplest way to ensure this is to make all the window.* props "own".
If we make attributes on the global live on the global itself rather than on the prototype chain, then "own" vs. "in" doesn't matter. I.e. either would work.
We'd likely also have to allow some "readonly" properties to be overwritten using arbitrary values. I.e. evaluating |indexedDB = 42;| or |var indexedDB = 42;| in the global scope would actually change window.indexedDB to the value 42. We end up having to do that right now anyway for a lot of properties since web pages otherwise break due to name collisions with random global variables.
Yes, so set as well as get accessor functions.
Yup.
This also has the advantage that it's a change "only" on the WebIDL side. So we'd be free to leave ES with fewer webcompat restrictions when defining how to deal with var/function and properties on the prototype chain.
Cameron may have to take the hit. I had mistakenly thought we could stick to ES5.1 and fix SpiderMonkey, but it's just not so.
It also has the advantage that Chrome already does this so we have some basis for thinking that this could be web compatible. (Though Chrome does this for not just global objects, which is a different topic).
Could you say more? I can see the benefit in having an odd-man-out exception for globals only. Does the "own" promotion hurt elsewhere in WebKit's DOM bindings?
Not sure I understand the question, so let me shoot from the hip and see if the answer fits :-)
My point was that an odd-man-out exception for globals seems like it would be beneficial since it reduces complexity. And that such an exception hopefully is web compatible enough since it matches how webkit treats globals. And in fact we know that it has some compatibility benefits as has been mentioned above.
Webkit also puts attributes on objects for non-globals, but I'm not promoting that behavior. Nor do I know of any benefits regarding web compatibility that comes with that behavior.
/ Jonas
Jonas Sicking wrote:
On Fri, Aug 10, 2012 at 11:17 PM, Brendan Eich<brendan at mozilla.org> wrote:
Jonas Sicking wrote:
One potential solution that I think we should keep in mind is to declare that WebIDL propertieson global objects doesn't go on the
prototype chain, but rather on the global objects themselves. That seems like it'll reduce a lot of the foot guns since they will behave much more like "normal" javascript properties. I.e. we'd get a model that is much simpler for developers. +1 or more.
I feel like whatever we'll do, the interactions with 'var X' and 'function X()' will end up getting complex when there are collisions with properties on the prototype chain. Yes.
All my fault for making objects be scopes. Sorry. But we should go with it, since object detection is a virtue (and var is needed for strict mode or just good hygiene).
var detect = window.mozDetect || window.webkitDetect || ... || window.detect;
with detect a get-only accessor must work. The simplest way to ensure this is to make all the window.* props "own".
If we make attributes on the global live on the global itself rather than on the prototype chain, then "own" vs. "in" doesn't matter. I.e. either would work.
That's right -- then we can fix the ES5.1 erratum as SpiderMonkey and other engines already ahve, and stick with testing for only an "own" property before var decides to create a fresh property, which will shadow as desired (to avoid problems in implementations and probably in user-code with proxies on the global's prototype chain).
IOW, WebIDL changes, as you wrote. ES5.1 + the ecmascript#78 erratum stay fixed (well, ES6 gets that erratum fixed; many implementations already ship it fixed).
It also has the advantage that Chrome already does this so we have some basis for thinking that this could be web compatible. (Though Chrome does this for not just global objects, which is a different topic). Could you say more? I can see the benefit in having an odd-man-out exception for globals only. Does the "own" promotion hurt elsewhere in WebKit's DOM bindings?
Not sure I understand the question, so let me shoot from the hip and see if the answer fits :-)
My point was that an odd-man-out exception for globals seems like it would be beneficial since it reduces complexity. And that such an exception hopefully is web compatible enough since it matches how webkit treats globals. And in fact we know that it has some compatibility benefits as has been mentioned above.
With you on all this. My question was about your "(Though Chrome does this for not just global objects, which is a different topic)"aside.
Webkit also puts attributes on objects for non-globals, but I'm not promoting that behavior. Nor do I know of any benefits regarding web compatibility that comes with that behavior.
Ok, that's what I was getting at. It may be that this is just historical consistency for all objects, no global exception, but sounds like we are agreeing that the global object needs an exception ("own" promotion of attributes and methods) in WebIDL.
On Sat, Aug 11, 2012 at 8:57 AM, Brendan Eich <brendan at mozilla.org> wrote:
Webkit also puts attributes on objects for non-globals, but I'm not promoting that behavior. Nor do I know of any benefits regarding web compatibility that comes with that behavior.
Ok, that's what I was getting at. It may be that this is just historical consistency for all objects, no global exception, but sounds like we are agreeing that the global object needs an exception ("own" promotion of attributes and methods) in WebIDL.
Yes. Sounds like we're fully in agreement.
/ Jonas
On Aug 10, 2012, at 11:14 PM, Brendan Eich wrote:
Allen Wirfs-Brock wrote:
I don't think there is anything special about the global named undefined. As long as it is a own property of the global object (which is is spec'ed to be) 'var undefined' is fine because redundant var declarations don't do anything anything. If undefined was inherited from window.prototype it would be a different story.
Agreed, just noting that const x = 42; var x; is not tolerated in ES6 as proposed (right?).
correct.
On Aug 10, 2012, at 11:14 PM, Brendan Eich wrote:
Allen Wirfs-Brock wrote:
On Aug 10, 2012, at 3:25 PM, Brendan Eich wrote:
- The declaration instantiation rules relating to pre-existing bindings only consider own properties of the global object. Inherited properties of the global object have no effect upon the processing of function and var declarations.
This is the incompatible change from ES1-5.1 and reality that I question whether we can get away with.
True, for var declarations. For function declarations it changed in 5.1 as a result of bugzilla.mozilla.org/show_bug.cgi?id=577325 which initially concerned what happens with a global function declaration when there is an inherited access with the same name. Is the inherited setter called? We all concluded that it shouldn't. Rule 4 above is essentially an expression of that idea.
Yes, I remember. 'function' always differed from 'var' in JS: it would create the binding (or throw trying, ES5+).
...
In primordial JS, writing function onload(){...} defined an onload handler. WYSIWYG and Occam's razor (perhaps too close a shave).
In this case, firing the setter is perhaps what the programmer wanted, even if it is a terrible way to accomplish that end.
It's not that bad if you start from the DOM level 0, especially window.onload being the same binding as function onload() {}.
There seems to be a contradiction between what you describe above for primordial and what ES1-3 said:
For each FunctionDeclaration in the code, in source text order, instantiate a declared function from the FunctionDeclaration and create a property of the variable object whose name is the Identifier in the FunctionDeclaration, whose value is the declared function and whose attributes are determined by the type of code. If the variable object already has a property with this name, replace its value and attributes.
One way to rationalize the two would seems to be by assuming that the global objects has special create property/replace value and attribute behavior (ie, [[DefineOwnProperty]]) that triggers special side-effects for certain properties such as 'onload'.
I still think the cleanest approach is for WebIDL to treat window object properties just like ES chapter 15 global object properties (probably extended with a standard handling for global accessor properties). Then ES can have a consistent semantics for all "built-in" globals. If special behavior is needed for defining certain globals, such as "onload" that can be handled by WebIDL specify custom [[DefineOwnProperty]] behavior for its window object.
Allen Wirfs-Brock wrote:
On Aug 10, 2012, at 11:14 PM, Brendan Eich wrote:
In this case, firing the setter is perhaps what the programmer wanted, even if it is a terrible way to accomplish that end.
It's not that bad if you start from the DOM level 0, especially window.onload being the same binding as function onload() {}.
There seems to be a contradiction between what you describe above for primordial and what ES1-3 said:
There was no contradiction in the old days. Writing
function onload() {}
did not run a proto-setter to add an event listener. Rather, a load event fired by trying any function named by window.onload.
At some point this stopped working. Not sure when, but it's how JS + DOM level 0 worked in Netscape 2 when they debuted, and I believe for a long while after.
Are we talking just about attributes here, and not operations? So Window.prototype.open would still exist and there'd be no own "open" property on window?
Jonas Sicking:
Webkit also puts attributes on objects for non-globals, but I'm not promoting that behavior. Nor do I know of any benefits regarding web compatibility that comes with that behavior.
Brendan Eich:
Ok, that's what I was getting at. It may be that this is just historical consistency for all objects, no global exception, but sounds like we are agreeing that the global object needs an exception ("own" promotion of attributes and methods) in WebIDL.
Before ES5 was out, Web IDL put properties for IDL attributes on instances, exposed via special [[Get]] and [[Put]] behaviour. That was pretty much what implementations were doing. Once ES5 standardised accessor properties, we had the opportunity to make IDL attributes less magical -- which was argued for pretty strongly by TC39 folks, before Proxies came along. Now perhaps I didn't consider closely enough the possibility of this being a breaking change (especially on the global object), but it seemed everyone was happy enough with moving in that direction. And I think it's a cleaner model for shared properties with getter/setter behaviour, anyway, and gives you the opportunity to monkeypatch them that the special [[Get]] and [[Put]] behaviour couldn't.
Which is to say that I think we should still push forward with attributes-as-accessor-properties-on-prototypes at least for objects other than the global object.
As bz and others point out, the object detection w/ var pattern can arise for operations, e.g. for requestAnimationFrame, using the same
var requestAnimationFrame = window.mozRequestAnimationFrame || ... || window.requestAnimationFrame;
pattern. So WebIDL operations (JS methods) on the global would be promoted to "own" too. They'd be configurable, if I recall correctly, and either writable or replaceable. Do I have that right?
On Aug 11, 2012, at 6:12 PM, Cameron McCormack wrote:
Jonas Sicking:
Webkit also puts attributes on objects for non-globals, but I'm not promoting that behavior. Nor do I know of any benefits regarding web compatibility that comes with that behavior.
Brendan Eich:
Ok, that's what I was getting at. It may be that this is just historical consistency for all objects, no global exception, but sounds like we are agreeing that the global object needs an exception ("own" promotion of attributes and methods) in WebIDL.
Before ES5 was out, Web IDL put properties for IDL attributes on instances, exposed via special [[Get]] and [[Put]] behaviour. That was pretty much what implementations were doing. Once ES5 standardised accessor properties, we had the opportunity to make IDL attributes less magical -- which was argued for pretty strongly by TC39 folks, before Proxies came along. Now perhaps I didn't consider closely enough the possibility of this being a breaking change (especially on the global object), but it seemed everyone was happy enough with moving in that direction. And I think it's a cleaner model for shared properties with getter/setter behaviour, anyway, and gives you the opportunity to monkeypatch them that the special [[Get]] and [[Put]] behaviour couldn't.
Which is to say that I think we should still push forward with attributes-as-accessor-properties-on-prototypes at least for objects other than the global object.
Exactly, attributes-as-accessors-on-prototypes seems to work great for everything other than the global object. But for the global object, attributes and operators are essentially equivalent to ES declared or built-in global variables/functions. The global object has a special role. It's fine to special case its treatment of WebIDL attributes and operators.
Brendan Eich:
As bz and others point out, the object detection w/ var pattern can arise for operations, e.g. for requestAnimationFrame, using the same
var requestAnimationFrame = window.mozRequestAnimationFrame || ... || window.requestAnimationFrame;
pattern. So WebIDL operations (JS methods) on the global would be promoted to "own" too. They'd be configurable, if I recall correctly, and either writable or replaceable. Do I have that right?
OK. So one thing that I think has been pointed out is that moving properties for operations on to window makes it harder to monkeypatch addEventListener and friends. We could, if we thought it was important, have the properties on window forward on to the ones from the prototype.
Some other questions on specifics:
-
If we don't do that auto-forwarding, does Window.prototype still need to exist? What should window.proto be?
-
If Window.prototype still does exist, should it be empty?
-
The only writable IDL attributes on Window are:
attribute DOMString name; attribute DOMString status; attribute WindowProxy? opener;
and all of the event handler attributes like "onclick". How do these need to behave if script blithely tries to use variables of the same name? With this:
<script> alert([window.status, typeof window.status]); window.status = "hello"; alert([window.status, typeof window.status]); </script> <script> var status; alert([window.status, typeof window.status]); status = 1; alert([window.status, typeof window.status]); </script>
Gecko and Opera alert:
,string hello,string ,undefined 1,number
while Chrome and Safari alert:
,string hello,string hello,string 1,string
which seems to indicate that they're setting the IDL attribute. I guess this is related to whether we want "function onclick(){}" to invoke the setter.
With this:
<body onerror="alert('exception')"> <script> alert([String(window.onclick), typeof window.onclick)]); </script> <script> var onclick = 1; alert([String(window.onclick), typeof window.onclick]); </script>
Gecko, Opera and Chrome alert:
null,object 1,number
which could mean shadowing or treating "onclick" as IDL type "any" and treating non-Function values as null, while Safari alerts:
null,object null,object
which looks like it's invoking the setter but ignoring the assignment of a non-Function value.
On Aug 11, 2012, at 12:21 PM, Brendan Eich wrote:
Allen Wirfs-Brock wrote:
On Aug 10, 2012, at 11:14 PM, Brendan Eich wrote:
In this case, firing the setter is perhaps what the programmer wanted, even if it is a terrible way to accomplish that end.
It's not that bad if you start from the DOM level 0, especially window.onload being the same binding as function onload() {}.
There seems to be a contradiction between what you describe above for primordial and what ES1-3 said:
There was no contradiction in the old days. Writing
function onload() {}
did not run a proto-setter to add an event listener. Rather, a load event fired by trying any function named by window.onload.
At some point this stopped working. Not sure when, but it's how JS + DOM level 0 worked in Netscape 2 when they debuted, and I believe for a long while after.
I see, this makes perfect sense when onload is viewed simply as a passive property. It is turning into an active side-effecting registration mechanism that conceptually clashed with function declarations. I wonder why that was necessary. Could it be as something as simple as WebIDL (and its predecessors) lacking the concept of interfaces with dynamically extensible properties?
Kyle Huey:
Can we leave EventTarget's methods on the proto chain and only move the ones on the Window interface itself? Unlike Window, EventTarget isn't changing very much.
We could. You're right it's not changing much, but I wouldn't want to box ourselves in to not being able to extend it later. But maybe it's less likely that people will try to use the
var newEventTargetFunction = window.newEventTargetFunction || window.mozNewEventTargetFunction || ...
pattern there.
On Aug 11, 2012, at 6:46 PM, Cameron McCormack wrote:
Brendan Eich:
As bz and others point out, the object detection w/ var pattern can arise for operations, e.g. for requestAnimationFrame, using the same
var requestAnimationFrame = window.mozRequestAnimationFrame || ... || window.requestAnimationFrame;
pattern. So WebIDL operations (JS methods) on the global would be promoted to "own" too. They'd be configurable, if I recall correctly, and either writable or replaceable. Do I have that right?
OK. So one thing that I think has been pointed out is that moving properties for operations on to window makes it harder to monkeypatch addEventListener and friends. We could, if we thought it was important, have the properties on window forward on to the ones from the prototype.
Some other questions on specifics:
- If we don't do that auto-forwarding, does Window.prototype still need to exist? What should window.proto be?
Requirements on the global object are specified here: ecma-international.org/ecma-262/5.1/#sec-15.1 It says that the value [[Prototype]] is implementation defined, but in practice in needs to inherit from Object.prototype. So, Object.prototype seems like a strong candidate for being the actual [[Prototype]] value.
- If Window.prototype still does exist, should it be empty?
I don't seen, right off, why it needs to to exist as anything other than Object.prototype.
The only writable IDL attributes on Window are:
attribute DOMString name; attribute DOMString status; attribute WindowProxy? opener;
and all of the event handler attributes like "onclick". How do these need to behave if script blithely tries to use variables of the same name? With this:
<script> alert([window.status, typeof window.status]); window.status = "hello"; alert([window.status, typeof window.status]); </script> <script> var status; alert([window.status, typeof window.status]); status = 1; alert([window.status, typeof window.status]); </script>
Gecko and Opera alert:
,string hello,string ,undefined 1,number
while Chrome and Safari alert:
,string hello,string hello,string 1,string
which seems to indicate that they're setting the IDL attribute. I guess this is related to whether we want "function onclick(){}" to invoke the setter.
global var declarations don't over-write or redefine own properties of the global object. What a JS programmer would expect for any built-in would be:
,string hello,string hello,string 1,number
I don't really see what would be wrong with not doing the automatic string conversion on assignment, but if you really want to do it we can var declarations handle pre-existing own accessor properties in the same way that own data properties are handled.
With this:
<body onerror="alert('exception')"> <script> alert([String(window.onclick), typeof window.onclick)]); </script> <script> var onclick = 1; alert([String(window.onclick), typeof window.onclick]); </script>
Gecko, Opera and Chrome alert:
null,object 1,number
which could mean shadowing or treating "onclick" as IDL type "any" and treating non-Function values as null, while Safari alerts:
null,object null,object
which looks like it's invoking the setter but ignoring the assignment of a non-Function value.
see, the subthread that Brendan and I here having about: function onload () {} declarations.
Do onload and friends need to even exist as global object properties if they haven't been explicitly set? Why can't the triggering mechanism for them simply look to see if they have been created? Why can't they be simple data properties of the global object? Basically, what would a JS programmer do if they were implemented the same mechanism?
Cameron McCormack wrote:
Jonas Sicking:
Webkit also puts attributes on objects for non-globals, but I'm not promoting that behavior. Nor do I know of any benefits regarding web compatibility that comes with that behavior.
Brendan Eich:
Ok, that's what I was getting at. It may be that this is just historical consistency for all objects, no global exception, but sounds like we are agreeing that the global object needs an exception ("own" promotion of attributes and methods) in WebIDL.
Before ES5 was out, Web IDL put properties for IDL attributes on instances, exposed via special [[Get]] and [[Put]] behaviour. That was pretty much what implementations were doing.
Not all. Gecko had both custom [[Get]] and [[Put]] (ES3 internal method names) and accessors masquerading as data properties, on prototypes.
Once ES5 standardised accessor properties, we had the opportunity to make IDL attributes less magical -- which was argued for pretty strongly by TC39 folks, before Proxies came along.
I'm on TC39 and I argued more pragmatically, since at least Gecko had (masquerading) accessors on prototypes. I am leery of a-priori "cleaner = better = right" thinking, especially given web compatibility.
Now perhaps I didn't consider closely enough the possibility of this being a breaking change (especially on the global object), but it seemed everyone was happy enough with moving in that direction. And I think it's a cleaner model for shared properties with getter/setter behaviour, anyway, and gives you the opportunity to monkeypatch them that the special [[Get]] and [[Put]] behaviour couldn't.
Yes, that's a good thing and it goes back to how I did the DOM level 0 in Netscape 2 -- with (masquerading) accessors and plain old method data properties on prototypes.
But the global object -- the window object then -- was quite a bit simpler in its prototype chain. We didn't have window implements EventTarget!
Which is to say that I think we should still push forward with attributes-as-accessor-properties-on-prototypes at least for objects other than the global object.
Agreed. Jonas and I are talking about an exception for the global object. It's the one on the scope chain. Well, other DOM objects are on scope chains of attribute-expressed event handlers. But such event handlers do not have any problem with var aliasing or shadowing a scope-object's properties (own or inherited).
The global object is the exception.
Just to clarify:
Brendan Eich wrote:
Agreed. Jonas and I are talking about an exception for the global object. It's the one on the scope chain. Well, other DOM objects are on scope chains of attribute-expressed event handlers. But such event handlers do not have any problem with var aliasing or shadowing a scope-object's properties (own or inherited).
... because event-handler attributes express function bodies, and function activations have their own (non-object) scope in which var binds, whatever outer object-based scope chain such DOM attribute event handlers have. No pigeon hole problem as with the global scope/object.
Cameron McCormack wrote:
Brendan Eich:
As bz and others point out, the object detection w/ var pattern can arise for operations, e.g. for requestAnimationFrame, using the same
var requestAnimationFrame = window.mozRequestAnimationFrame || ... || window.requestAnimationFrame;
pattern. So WebIDL operations (JS methods) on the global would be promoted to "own" too. They'd be configurable, if I recall correctly, and either writable or replaceable. Do I have that right?
OK. So one thing that I think has been pointed out is that moving properties for operations on to window makes it harder to monkeypatch addEventListener and friends.
This is a recent feature, right? I can't even find EventTarget in Opera 12, e.g.
It's plausible the global object exception breaks symmetry here. Could we live with the inability to monkey-patch? As Jonas points out, playing deep prototype chain games with the global object when var (and function) shadow as in ES6 or ES5.1+erratum-fix is just bug-making and confusing.
We could, if we thought it was important, have the properties on window forward on to the ones from the prototype.
Less magic if we can avoid it.
Some other questions on specifics:
- If we don't do that auto-forwarding, does Window.prototype still need to exist? What should window.proto be?
There's no need to over-invent for uniformity, if the global object is exceptional. The simpler the exception, the better.
However, implementations (at least Gecko) give window a proto chain that is "interesting":
-- [19:29:57.405] Object.prototype.toString.call(window.proto).slice(8,-1) [19:29:57.408] "XPC_WN_ModsAllowed_NoCall_Proto_JSClass"
[19:30:06.143] Object.prototype.toString.call(window.proto.proto).slice(8,-1) [19:30:06.145] "Global Scope Polluter"
[19:30:11.629] Object.prototype.toString.call(window.proto.proto.proto).slice(8,-1) [19:30:11.631] "Object"
[19:30:18.373] Object.prototype.toString.call(window.proto.proto.proto.proto).slice(8,-1) [19:30:18.375] "Null"
This is from a stable-release Firefox Web Console. I believe it matches a real DOM content window. I don't see EventTarget, because its methods get flattened into the direct proto, Window.prototype:
[19:33:30.076] Window.prototype === this.proto [19:33:30.077] true
[19:33:50.323] Object.getOwnPropertyNames(this.proto) [19:33:50.325] ["addEventListener", "removeEventListener", "dispatchEvent", "dump", "name", "parent", "top", "self", "sessionStorage", "localStorage", "onmouseenter", "onmouseleave", "getSelection", "scrollByLines", "getComputedStyle", "lookupGetter", "lookupSetter", "defineGetter", "defineSetter", "toString", "QueryInterface", "window", "document", "location", "history", "locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar", "status", "close", "stop", "focus", "blur", "length", "opener", "frameElement", "navigator", "applicationCache", "alert", "confirm", "prompt", "print", "showModalDialog", "postMessage", "atob", "btoa", "matchMedia", "screen", "innerWidth", "innerHeight", "scrollX", "pageXOffset", "scrollY", "pageYOffset", "scroll", "scrollTo", "scrollBy", "screenX", "screenY", "outerWidth", "outerHeight", "scrollByPages", "sizeToContent", "content", "closed", "crypto", "pkcs11", "controllers", "defaultStatus", "mozInnerScreenX", "mozInnerScreenY", "scrollMaxX", "scrollMaxY", "fullScreen", "back", "forward", "home", "moveTo", "moveBy", "resizeTo", "resizeBy", "updateCommands", "find", "mozPaintCount", "mozRequestAnimationFrame", "mozCancelAnimationFrame", "mozCancelRequestAnimationFrame", "mozAnimationStartTime", "URL", "onafterprint", "onbeforeprint", "onbeforeunload", "onhashchange", "onmessage", "onoffline", "ononline", "onpopstate", "onpagehide", "onpageshow", "onresize", "onunload", "ondevicemotion", "ondeviceorientation", "ondeviceproximity", "onuserproximity", "ondevicelight", "setTimeout", "setInterval", "clearTimeout", "clearInterval", "setResizable", "captureEvents", "releaseEvents", "routeEvent", "enableExternalCapture", "disableExternalCapture", "open", "openDialog", "frames", "onabort", "onblur", "oncanplay", "oncanplaythrough", "onchange", "onclick", "oncontextmenu", "ondblclick", "ondrag", "ondragend", "ondragenter", "ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchange", "onemptied", "onended", "onerror", "onfocus", "oninput", "oninvalid", "onkeydown", "onkeypress", "onkeyup", "onload", "onloadeddata", "onloadedmetadata", "onloadstart", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmozfullscreenchange", "onmozfullscreenerror", "onmozpointerlockchange", "onmozpointerlockerror", "onpause", "onplay", "onplaying", "onprogress", "onratechange", "onreset", "onscroll", "onseeked", "onseeking", "onselect", "onshow", "onstalled", "onsubmit", "onsuspend", "ontimeupdate", "onvolumechange", "onwaiting", "oncopy", "oncut", "onpaste", "onbeforescriptexecute", "onafterscriptexecute", "indexedDB", "performance"]
The global object's prototype chain must end with Object.prototype (ES5 says so, IIRC). But the exact chain is not something that should be standardized in detail.
- If Window.prototype still does exist, should it be empty?
Rather have no Window.prototype if everything currently there becomes "own" in each window object, than have an empty proto as an attractive nuisance. You can't patch if shadowed by an own property, but it might seem like you ought to be able to.
The only writable IDL attributes on Window are:
attribute DOMString name; attribute DOMString status; attribute WindowProxy? opener;
and all of the event handler attributes like "onclick". How do these need to behave if script blithely tries to use variables of the same name? With this:
<script> alert([window.status, typeof window.status]); window.status = "hello"; alert([window.status, typeof window.status]); </script> <script> var status; alert([window.status, typeof window.status]); status = 1; alert([window.status, typeof window.status]); </script>
Gecko and Opera alert:
,string hello,string ,undefined 1,number
Yes, this is more lossage compared to historical norms that WebKit-based browsers still uphold.
The lack of a var initialiser means that the (historically own) status accessor must have its getter or setter invoked. 'var' doesn't replace an existing own property in any scenario, historical or ES5+erratum-fix.
while Chrome and Safari alert:
,string hello,string hello,string 1,string
which seems to indicate that they're setting the IDL attribute.
Did you try IE8, 9 and 10? I suspect they match but can't test.
I guess this is related to whether we want "function onclick(){}" to invoke the setter.
We decided (as Allen points out; related to the var issue but distinct from it) we do not.
But function onmessage(){} in a worker script then doesn't do what some devs seem to like (or at least expect) from Chrome and Safari.
With this:
<body onerror="alert('exception')"> <script> alert([String(window.onclick), typeof window.onclick)]); </script> <script> var onclick = 1; alert([String(window.onclick), typeof window.onclick]); </script>
Gecko, Opera and Chrome alert:
null,object 1,number
which could mean shadowing or treating "onclick" as IDL type "any" and treating non-Function values as null, while Safari alerts:
null,object null,object
which looks like it's invoking the setter but ignoring the assignment of a non-Function value.
Seems a Safari deviation.
Allen Wirfs-Brock wrote:
On Aug 11, 2012, at 6:46 PM, Cameron McCormack wrote:
- If we don't do that auto-forwarding, does Window.prototype still need to exist? What should window.proto be?
Requirements on the global object are specified here: ecma-international.org/ecma-262/5.1/#sec-15.1 It says that the value [[Prototype]] is implementation defined, but in practice in needs to inherit from Object.prototype. So, Object.prototype seems like a strong candidate for being the actual [[Prototype]] value.
That was true in the original DOM in Netscape 2. There was no Window pseudo-constructor or Window.prototype. All the masquerading-as-data-property accessors, e.g. window.status, were "own" properties.
This changed with WebIDL but also IIRC with earlier evolution. I'll let someone else do the hg and cvs archeology to show Gecko's history.
- If Window.prototype still does exist, should it be empty?
I don't seen, right off, why it needs to to exist as anything other than Object.prototype.
Back to the future! ;-)
The only writable IDL attributes on Window are:
attribute DOMString name; attribute DOMString status; attribute WindowProxy? opener;
and all of the event handler attributes like "onclick". How do these need to behave if script blithely tries to use variables of the same name? With this:
<script> alert([window.status, typeof window.status]); window.status = "hello"; alert([window.status, typeof window.status]); </script> <script> var status; alert([window.status, typeof window.status]); status = 1; alert([window.status, typeof window.status]); </script>
Gecko and Opera alert:
,string hello,string ,undefined 1,number
while Chrome and Safari alert:
,string hello,string hello,string 1,string
which seems to indicate that they're setting the IDL attribute. I guess this is related to whether we want "function onclick(){}" to invoke the setter.
(Again, function is different from var and this example uses var, so let's defer function for now.)
global var declarations don't over-write or redefine own properties of the global object. What a JS programmer would expect for any built-in would be:
,string hello,string hello,string 1,number
I don't really see what would be wrong with not doing the automatic string conversion on assignment, but if you really want to do it we can var declarations handle pre-existing own accessor properties in the same way that own data properties are handled.
I think we must. ES5 and 5.1 do, IINM, but the "must" comes from implementations doing so, as shown by Cameron's 1,string. The window.status setter is undisturbed by 'var', and it normalized RHS type to string.
With this:
<body onerror="alert('exception')"> <script> alert([String(window.onclick), typeof window.onclick)]); </script> <script> var onclick = 1; alert([String(window.onclick), typeof window.onclick]); </script>
Gecko, Opera and Chrome alert:
null,object 1,number
which could mean shadowing or treating "onclick" as IDL type "any" and treating non-Function values as null, while Safari alerts:
null,object null,object
which looks like it's invoking the setter but ignoring the assignment of a non-Function value.
see, the subthread that Brendan and I here having about: function onload () {} declarations.
Again, 'function' != 'var'. Cameron's example again uses only 'var'. Happy to talk 'function' too, separately sub-thread.
Do onload and friends need to even exist as global object properties if they haven't been explicitly set? Why can't the triggering mechanism for them simply look to see if they have been created? Why can't they be simple data properties of the global object? Basically, what would a JS programmer do if they were implemented the same mechanism?
As noted, they started out that way 17 years ago. I think WebIDL and interface-based method definition made onload, e.g., predefined on window objects, or more recently on Window.prototype. Was this useful? Was it intended specifically (for window, not just intended generally due to WebIDL's uniform rules for binding its definitions in JS)?
Brendan Eich:
As noted, they started out that way 17 years ago. I think WebIDL and interface-based method definition made onload, e.g., predefined on window objects, or more recently on Window.prototype. Was this useful? Was it intended specifically (for window, not just intended generally due to WebIDL's uniform rules for binding its definitions in JS)?
I don't think it provides any benefit. Uniformity is the only reason the spec says they should be there, currently.
Just to note, since it's tangentially related to the topic at hand, that WebKit (Chrome's WebKit?) currently doesn't handle Proxy functions as event targets (in any form) as noted in this bug report: code.google.com/p/chromium/issues/detail?id=140160. One issue this illustrates with this specific implementation of "on*" is that they are not treated like a normal data property (though that is just one issue highlighted by the bug).
On Sat, Aug 11, 2012 at 6:04 PM, Cameron McCormack <cam at mcc.id.au> wrote:
Are we talking just about attributes here, and not operations? So Window.prototype.open would still exist and there'd be no own "open" property on window?
I won't speak for anyone else, but I'm only talking about attributes. So yes, "open" behaves as you describe.
/ Jonas
-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Brendan Eich Sent: Saturday, August 11, 2012 22:57
As noted, they started out that way 17 years ago. I think WebIDL and interface-based method definition made onload, e.g., predefined on window objects, or more recently on Window.prototype. Was this useful? Was it intended specifically (for window, not just intended generally due to WebIDL's uniform rules for binding its definitions in JS)?
As a developer, the fact that the properties already exist and are set to null is useful for feature-detection. E.g. any of the following will test for the presence of cross-document messaging:
"onmessage" in window window.onmessage !== undefined window.onmessage === null // assuming no badly-behaved third-party scripts
It is also useful strictly from a development POV, because typing window.on
in the Firefox or Chrome console gives a list of supported events.
FWIW, my mental model is probably closest to these being non-magical own data properties, pre-initialized to null, that the browser will read from and use when it fires the corresponding event. They don't make much sense as accessors to me.
On 8/10/12 8:17 PM, Allen Wirfs-Brock wrote:
The least painful solution to the immediate FF issue may be to make indexedDB an own property of the window object rather than an inherited property. Perhaps do that for all variable-like properties (those that down need real get/set logic) of the window object.
indexedDB has a real getter, fwiw: the object is created lazily....
but isn't widespread use of inherited setters to implement DOM properties fairly recent?
No. Gecko has done it all along, and IE and Opera have certainly done it for a good long while; I haven't tested going back more than a few years.
On 8/10/12 9:00 PM, Jonas Sicking wrote:
One potential solution that I think we should keep in mind is to declare that WebIDL properties on global objects doesn't go on the prototype chain, but rather on the global objects themselves. ... It also has the advantage that Chrome already does this
Chrome does this for some properties (attributes) but not others (operations).
I assume you just meant to do this for attributes? Those are the only cases in which var would call setters, obviously.
The var shadowing issue is an issue for operations too, of course, but it sounds like that's being solved on the ES side.
I can live with putting attributes directly on the global. If we ever add an attribute to EventTarget that will be a little weird because hooking the getter or setter on EventTarget.prototype will affect all event targets except the global, but that's life.
On 8/11/12 9:16 PM, Brendan Eich wrote:
As bz and others point out, the object detection w/ var pattern can arise for operations, e.g. for requestAnimationFrame, using the same
var requestAnimationFrame = window.mozRequestAnimationFrame || ... || window.requestAnimationFrame;
pattern. So WebIDL operations (JS methods) on the global would be promoted to "own" too. They'd be configurable, if I recall correctly, and either writable or replaceable. Do I have that right?
This is unnecessary if we stick with the current var wording in 5.1.
If this is done, that will mean hooking EventTarget.prototype.addEventListener won't hook adds on the global, which is a bit weird.
On 8/11/12 9:46 PM, Cameron McCormack wrote:
- If we don't do that auto-forwarding, does Window.prototype still need to exist? What should window.proto be?
That's a good question. What is it in current UAs? At least some don't have a Window at all...
- If Window.prototype still does exist, should it be empty?
EventTarget.prototype wouldn't be empty in any case, so for consistency if Window.prototype exists it should not be empty. At least assuming EventTarget.prototype is on the proto chain at all.
On 8/11/12 9:48 PM, Allen Wirfs-Brock wrote:
I see, this makes perfect sense when onload is viewed simply as a passive property.
That stops being an option once addEventListener also appears, for what it's worth.
It is turning into an active side-effecting registration mechanism that conceptually clashed with function declarations. I wonder why that was necessary.
Because event listener ordering matters, so you have to register the event listener when onload property is set. Now the question is what that listener does: it could just take the thing it's given, or it could dynamically look up "onload" on the global. At some point Gecko used to do the latter; it would be interesting to check why we stopped. It's all in the revision history!
On 8/11/12 10:17 PM, Allen Wirfs-Brock wrote:
Requirements on the global object are specified here: ecma-international.org/ecma-262/5.1/#sec-15.1 It says that the value [[Prototype]] is implementation defined, but in practice in needs to inherit from Object.prototype. So, Object.prototype seems like a strong candidate for being the actual [[Prototype]] value.
That's a non-starter because the global is [NamedPropertiesObject], so at the very least that object (the global scope polluter) needs to live on the proto chain somewhere below Object.prototype.
Do onload and friends need to even exist as global object properties if they haven't been explicitly set?
Yes. It's common for pages to do object-detection for particular events existing in an implementation by checking for the existence of the relevant on* in window. In fact, that's why Gecko switched behavior here, now that you remind me! See bugzilla.mozilla.org/show_bug.cgi?id=659350 and bugzilla.mozilla.org/show_bug.cgi?id=414853 and the numerous duplicates of the latter.
On 8/11/12 10:42 PM, Brendan Eich wrote:
However, implementations (at least Gecko) give window a proto chain that is "interesting":
Indeed. For one thing, we haven't switched Window to the new bindings yet.
This is from a stable-release Firefox Web Console. I believe it matches a real DOM content window. I don't see EventTarget
Because it's not on the proto chain at all; in Gecko as of today Window doesn't inherit from EventTarget; instead "Window implements EventTarget", effectively. Again, we just haven't switched Window to WebIDL yet.
The Global Scope Polluter you see there, though, is in fact defined in WebIDL.
The global object's prototype chain must end with Object.prototype (ES5 says so, IIRC).
Yes.
But the exact chain is not something that should be standardized in detail.
Why not?
On 8/11/12 10:57 PM, Brendan Eich wrote:
I think WebIDL and interface-based method definition made onload, e.g., predefined on window objects, or more recently on Window.prototype. Was this useful?
It's required for web compat, as I said earlier in this thread. They could perhaps be predefined as data properties set to null, but data properties which have as a side effect of setting them to a function object the registration of an event listener... which starts to look a lot like an accessor property to me.
On Sun, Aug 12, 2012 at 6:30 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:
On 8/10/12 8:17 PM, Allen Wirfs-Brock wrote: ...
but isn't widespread use of inherited setters to implement DOM
properties fairly recent?
No. Gecko has done it all along, and IE and Opera have certainly done it for a good long while; I haven't tested going back more than a few years.
Yes, Opera (Carakan) resolves setters by walking the proto chain, but we don't use g/setters to implement DOM properties.
(They're of some use when site patching, however.)
--sigbjorn / sof at opera.com
Boris Zbarsky wrote:
On 8/10/12 9:00 PM, Jonas Sicking wrote:
One potential solution that I think we should keep in mind is to declare that WebIDL properties on global objects doesn't go on the prototype chain, but rather on the global objects themselves. ... It also has the advantage that Chrome already does this
Chrome does this for some properties (attributes) but not others (operations).
I assume you just meant to do this for attributes? Those are the only cases in which var would call setters, obviously.
The var shadowing issue is an issue for operations too, of course, but it sounds like that's being solved on the ES side.
Not sure how it is solved if the JS programmer uses 'var'. Using 'function' will shadow any prototype method (operation) of the same name, but then there's no object detection.
IOW, I think your requestAnimationFrame var+object-detection example is a good one. No difference in principle from the indexedDB case, and possibly only just luck that it has not bitten yet.
I can live with putting attributes directly on the global. If we ever add an attribute to EventTarget that will be a little weird because hooking the getter or setter on EventTarget.prototype will affect all event targets except the global, but that's life.
Is there a WebIDL style guide for helping decide when and where to use attributes, or avoid them?
Boris Zbarsky wrote:
On 8/11/12 9:16 PM, Brendan Eich wrote:
As bz and others point out, the object detection w/ var pattern can arise for operations, e.g. for requestAnimationFrame, using the same
var requestAnimationFrame = window.mozRequestAnimationFrame || ... || window.requestAnimationFrame;
pattern. So WebIDL operations (JS methods) on the global would be promoted to "own" too. They'd be configurable, if I recall correctly, and either writable or replaceable. Do I have that right?
This is unnecessary if we stick with the current var wording in 5.1.
Right. Unfortunately engines have shipped the erratum fix and it is helpful some times. We have a real dilemma. When var should shadow vs. not shadow depends on whether the detected property lives on the global's prototype chain. Thus the interest in moving everything down to be "own" properties on the global.
That way, function declarations clobber, and var leave alone. There's still a problem for getter-less attributes including indexedDB, so something still has to change there!
If this is done, that will mean hooking EventTarget.prototype.addEventListener won't hook adds on the global, which is a bit weird.
I think the primal sin (mine, again) is objects as scopes. But that can't be helped, so elaborating the global object's prototype chain, specifically to add expressiveness such as EventTarget monkeypatching, is going the wrong way. The global needs to be a flat object (only implementation-specific magic protos along a chain ending in Object.prototype).
And we still need to be careful about writability (get-only indexedDB point above).
On 8/12/12 1:27 PM, Brendan Eich wrote:
IOW, I think your requestAnimationFrame var+object-detection example is a good one. No difference in principle from the indexedDB case, and possibly only just luck that it has not bitten yet.
True....
Is there a WebIDL style guide for helping decide when and where to use attributes, or avoid them?
Not that I know of (though there are some things that can't be done with attributes and some that are frowned on; e.g. having attributes that return a new object each time is not so great).
On 8/12/12 1:31 PM, Brendan Eich wrote:
There's still a problem for getter-less attributes including indexedDB, so something still has to change there!
I'm not sure I follow this. If indexedDB is on the global as an own property, what issue remains with it?
The global needs to be a flat object (only implementation-specific magic protos along a chain ending in Object.prototype).
The magic from the global scope polluter isn't even impl-specific.
And we still need to be careful about writability (get-only indexedDB point above).
Ah, you meant setter-less above?
Only a problem in strict mode, right?
Boris Zbarsky wrote:
On 8/12/12 1:31 PM, Brendan Eich wrote:
There's still a problem for getter-less attributes including indexedDB, so
[setter-less]
something still has to change there!
I'm not sure I follow this. If indexedDB is on the global as an own property, what issue remains with it?
A var indexedDB in global scope where the global object has such a get-only accessor will not clobber the existing property, but the var+obj-detection pattern initializes the variable:
var indexedDb = ... || window.indexedDB;
and that will fail in strict mode (thanks, you point that out below):
js> void Object.defineProperty(this, 'foo', {get: function(){return 42}})
js> foo
42 js> var foo = 42
js> "use strict"; var foo = 43
typein:4: TypeError: setting a property that has only a getter
The global needs to be a flat object (only implementation-specific magic protos along a chain ending in Object.prototype).
The magic from the global scope polluter isn't even impl-specific.
Right, HTML5 -- thanks to old IE :-|.
And we still need to be careful about writability (get-only indexedDB point above).
Ah, you meant setter-less above?
D'oh, yes.
Only a problem in strict mode, right?
Right.
Boris Zbarsky wrote:
On 8/11/12 10:42 PM, Brendan Eich wrote:
However, implementations (at least Gecko) give window a proto chain that is "interesting":
Indeed. For one thing, we haven't switched Window to the new bindings yet.
Could you lay out the new chain here, e.g. viaObject.prototype.toString.call(window.proto).slice(8,-1) or better?
I'm hoping my Web Console inspection was accurate for DOM content windows in the current (old) bindings.
This is from a stable-release Firefox Web Console. I believe it matches a real DOM content window. I don't see EventTarget
Because it's not on the proto chain at all; in Gecko as of today Window doesn't inherit from EventTarget; instead "Window implements EventTarget", effectively. Again, we just haven't switched Window to WebIDL yet.
Sure. Still, either way (old or new bindings), the EventTarget attributes and operations go on a prototype (old: Window.prototype, new: EventTarget.prototype, please correct me if I'm wrong). Which makes for the var+obj-detection hazard.
The Global Scope Polluter you see there, though, is in fact defined in WebIDL.
Cool, I guess. Standardized pollution, what a world!
But the exact chain is not something that should be standardized in detail.
Why not?
Good question. If browser vendors can agree on total window proto-chain spec for maximum interop, ok. I had thought there were unobservable (prior to ES5) differences among impls that vendors might not want to change. ES5 reflection may disclose these but not create real interop issues.
Boris Zbarsky wrote:
On 8/11/12 10:57 PM, Brendan Eich wrote:
I think WebIDL and interface-based method definition made onload, e.g., predefined on window objects, or more recently on Window.prototype. Was this useful?
It's required for web compat, as I said earlier in this thread. They could perhaps be predefined as data properties set to null,
Right, Brandon Benvie raised this idea. It goes back to the simpler data (and masquerading native accessor) model of ur-JS/DOM.
but data properties which have as a side effect of setting them to a function object the registration of an event listener... which starts to look a lot like an accessor property to me.
It does, unless there's an equivalent "polling" semantics where no registration is required, just recurring [[Get]] probes (with caching optimizations, whatever -- all unobservable so out of the spec's sight). Is there such an equivalent "polling" or "probing" semantics?
One incompatibility in current implementations is that Object.getPrototypeOf(window) in Opera is Object.prototype whereas the rest have something like Window.prototype (Window doesn't exist in Opera).
An interesting consequence of mutable proto and having the global
variable record be a "regular" object is that mutating its proto has
the same net effect as using with
.
An even more interesting scenario is something like global.__proto__ = Proxy.create(....)
Brandon Benvie wrote:
An even more interesting scenario is something like
global.__proto__ = Proxy.create(....)
Yes, I mentioned proxies on the global's proto-chain (and mutable proto) up-thread, here:
Quote: "We had problems with var being sensitive to the prototype chain, particularly with proxies prototyped in SpiderMonkey and mutable proto. These were arguably implementation bugs, though."
Older thread from January:
On 8/12/12 1:59 PM, Brendan Eich wrote:
Boris Zbarsky wrote:
On 8/11/12 10:42 PM, Brendan Eich wrote:
However, implementations (at least Gecko) give window a proto chain that is "interesting":
Indeed. For one thing, we haven't switched Window to the new bindings yet.
Could you lay out the new chain here, e.g. viaObject.prototype.toString.call(window.proto).slice(8,-1) or better?
I believe the current spec calls something like this at the moment:
global -> Window.prototype -> Global Scope Polluter ->
EventTarget.prototype -> Object.prototype
I'm hoping my Web Console inspection was accurate for DOM content windows in the current (old) bindings.
I think so, yes.
Sure. Still, either way (old or new bindings), the EventTarget attributes and operations go on a prototype (old: Window.prototype, new: EventTarget.prototype, please correct me if I'm wrong). Which makes for the var+obj-detection hazard.
Yep.
Cool, I guess. Standardized pollution, what a world!
Well, everyone except for Gecko implemented it (and even Gecko in quirks mode), and weren't willing to drop it, so... I tried fighting it for a bit, but websites were starting to depend on it and not bother testing, so here we are.
But the exact chain is not something that should be standardized in detail.
Why not?
Good question. If browser vendors can agree on total window proto-chain spec for maximum interop, ok. I had thought there were unobservable (prior to ES5) differences among impls that vendors might not want to change. ES5 reflection may disclose these but not create real interop issues.
Right, if we can't get implementations to agree we may need to revisit. But so far there's agreement at least in principle (modulo the fact that some impls haven't really committed to implementing WebIDL at all, of course).
On 8/12/12 3:02 PM, Brandon Benvie wrote:
An even more interesting scenario is something like
global.__proto__ = Proxy.create(....)
That's basically what the Global Scope Polluter does. That's certainly how I plan to implement it in Gecko in the WebIDL bindings: Window.prototype.proto will be a proxy which will do all the weird stuff the GSP has to do.
Boris Zbarsky wrote:
On 8/12/12 3:02 PM, Brandon Benvie wrote:
An even more interesting scenario is something like
global.__proto__ = Proxy.create(....)
That's basically what the Global Scope Polluter does. That's certainly how I plan to implement it in Gecko in the WebIDL bindings: Window.prototype.proto will be a proxy which will do all the weird stuff the GSP has to do.
However, check me here: the GSP makes "own" global properties on demand, when names of DOM elements that come from name= and id= attributes (or just id=? I forget) are used as identifier expressions.
So at least the GSP is not adding a new tier of proto-properties (again, I hope! Please correct me if I'm mistaken).
On 8/12/12 4:31 PM, Brendan Eich wrote:
However, check me here: the GSP makes "own" global properties on demand, when names of DOM elements that come from name= and id= attributes (or just id=? I forget) are used as identifier expressions.
That's what it does in Gecko's impl right now. It has a bunch of issues, including the properties not going away when the name or id attributes change or the relevant element is removed from the document.
If you want properties to go away, then you need to either explicitly delete the properties (and then you have to be able to tell apart properties you added from properties the page defined!) or you have to use a proxy (in ES5 terms).
Per the current spec proposal, the GSP lives on the proto chain and exposes the various id/name stuff as own properties on itself, with the usual caveats about checking up the proto chain first that DOM named access has. See dev.w3.org/2006/webapi/WebIDL/#NamedPropertiesObject (which Window uses). There was a bunch of previous discussion about this at lists.w3.org/Archives/Public/public-script-coord/2012JanMar/0010.html and following, though it's not clear to me now whether the solution we decided on allows var to shadow these named props as desired after all...
On Sun, Aug 12, 2012 at 8:57 PM, Brandon Benvie <brandon at brandonbenvie.com>wrote:
One incompatibility in current implementations is that Object.getPrototypeOf(window) in Opera is Object.prototype whereas the rest have something like Window.prototype (Window doesn't exist in Opera).
You may want to test with 12.50 Desktop snapshots here. i.e., we've recently altered window's prototype chain to
window -> Window.prototype -> NamedProperties -> Object.prototype
to improve compatibility with others..and spec.
Putting IDL properties on the prototype wasn't done at the same time.
--sigbjorn / sof at opera.com
On 8/12/12 4:55 PM, Boris Zbarsky wrote:
There was a bunch of previous discussion about this at lists.w3.org/Archives/Public/public-script-coord/2012JanMar/0010.html and following, though it's not clear to me now whether the solution we decided on allows var to shadow these named props as desired after all...
And in particular, it's interesting to compare the claims from Ojan in that thread about web developer desires to the practice of what they actually expect that we ran into with indexedDB...
In any case, it does sound to me like putting the GSP on the proto chain is not enough to get the behavior if the ES5.1 behavior stays as-is unless it explicitly checks for props on the global itself a la lists.w3.org/Archives/Public/public-script-coord/2012JanMar/0030.html
Note that data in lists.w3.org/Archives/Public/public-script-coord/2012JanMar/0033.html suggests that IE also implements the erratum to 5.1 we were talking about up-thread. Oh what a tangled web we weave.
But the whole thread is worth reading (even if I was just as confused back then about the spec status of the var shadowing behavior: I thought it was in ES5, not in an erratum to 5.1). If nothing else it contains somewhat of a description of how Gecko and IE implement their global scope polluters.
Boris Zbarsky wrote:
On 8/12/12 4:31 PM, Brendan Eich wrote:
However, check me here: the GSP makes "own" global properties on demand, when names of DOM elements that come from name= and id= attributes (or just id=? I forget) are used as identifier expressions.
That's what it does in Gecko's impl right now. It has a bunch of issues, including the properties not going away when the name or id attributes change or the relevant element is removed from the document.
Did IE4 onward (to modern IE) or any browser that copied name pollution from IE do that? It's hard to keep coherence, as you note.
If you want properties to go away, then you need to either explicitly delete the properties (and then you have to be able to tell apart properties you added from properties the page defined!) or you have to use a proxy (in ES5 terms).
So long as we take the ES5.1 erratum that's already implemented by most engines, I suspect the GSP growing its own proto-tier instead of making global "own" properties on demand is ok. People do not var+obj-detect elements by name (please confirm or deny!). But name collisions if the HTML uses a common id= value and the JS wants that for a global var seem scarily likely.
Per the current spec proposal, the GSP lives on the proto chain and exposes the various id/name stuff as own properties on itself, with the usual caveats about checking up the proto chain first that DOM named access has.
So that the global window.g created (absent a prior extant own property) by var g; and var g = i; given <div id='g'>Gee</div> trump the polluting
proto-g?
See dev.w3.org/2006/webapi/WebIDL/#NamedPropertiesObject (which Window uses). There was a bunch of previous discussion about this at lists.w3.org/Archives/Public/public-script-coord/2012JanMar/0010.html and following, though it's not clear to me now whether the solution we decided on allows var to shadow these named props as desired after all...
I didn't read the whole thread, but I noticed frames came up. They do historically make "own" global props, I did that in 1995 and I think it stuck. Is this changing?
Boris Zbarsky wrote:
Note that data in lists.w3.org/Archives/Public/public-script-coord/2012JanMar/0033.html suggests that IE also implements the erratum to 5.1 we were talking about up-thread. Oh what a tangled web we weave.
Yes, current thinking is that we should take the erratum that major JS engines already fixed, and include it in ES6. But this means we must do something different in WebIDL, probably make own global properties for window-implements-interface-inherited attributes and even operations.
And then (for strict mode) be careful about get-only accessors. This reminds me of [Replaceable], which was for non-writable but configurable data properties that var and function must be able to replace. It's kind of the opposite and only for accessors: var-captures-own-accessor-via-detection, or some such.
Just recapping, tell me if I'm missing something.
On 8/12/12 5:23 PM, Brendan Eich wrote:
Boris Zbarsky wrote:
On 8/12/12 4:31 PM, Brendan Eich wrote:
However, check me here: the GSP makes "own" global properties on demand, when names of DOM elements that come from name= and id= attributes (or just id=? I forget) are used as identifier expressions.
That's what it does in Gecko's impl right now. It has a bunch of issues, including the properties not going away when the name or id attributes change or the relevant element is removed from the document.
Did IE4 onward (to modern IE) or any browser that copied name pollution from IE do that?
I believe every non-Gecko browser does this right last I tested.
So long as we take the ES5.1 erratum that's already implemented by most engines, I suspect the GSP growing its own proto-tier instead of making global "own" properties on demand is ok. People do not var+obj-detect elements by name (please confirm or deny!)
I would hope not. And again, var shadows the GSP in IE; I can't speak to what WebKit-based browsers do here because I haven't tested it recently.
But name collisions if the HTML uses a common id= value and the JS wants that for a global var seem scarily likely.
Yes, using the GSP is more or less a recipe for fail. That's why I'd hoped to restrict it to quirks mode pages. Ah, well.
Per the current spec proposal, the GSP lives on the proto chain and exposes the various id/name stuff as own properties on itself, with the usual caveats about checking up the proto chain first that DOM named access has.
So that the global window.g created (absent a prior extant own property) by var g; and var g = i; given <div id='g'>Gee</div> trump the polluting proto-g?
Assuming the erratum to ES 5.1 applies, yes. If it doesn't, then I'm not sure.
I didn't read the whole thread, but I noticed frames came up. They do historically make "own" global props, I did that in 1995 and I think it stuck. Is this changing?
That's the current plan, I believe. Per current HTML5 spec, frames and ids do exactly the same thing (as in, put properties on the same object, whatever that object is). This doesn't match Gecko, but it may match current IE.
On 8/12/12 5:29 PM, Brendan Eich wrote:
Boris Zbarsky wrote:
Note that data in lists.w3.org/Archives/Public/public-script-coord/2012JanMar/0033.html suggests that IE also implements the erratum to 5.1 we were talking about up-thread. Oh what a tangled web we weave.
Yes, current thinking is that we should take the erratum that major JS engines already fixed, and include it in ES6. But this means we must do something different in WebIDL, probably make own global properties for window-implements-interface-inherited attributes and even operations.
And then (for strict mode) be careful about get-only accessors. This reminds me of [Replaceable], which was for non-writable but configurable data properties that var and function must be able to replace. It's kind of the opposite and only for accessors: var-captures-own-accessor-via-detection, or some such.
Just recapping, tell me if I'm missing something.
The above sounds like a reasonable summary to me. Certainly hits all the high points of the discussion, with the addition that the GSP as currently specced depends on the erratum sticking around.
Boris Zbarsky wrote:
But name collisions if the HTML uses a common id= value and the JS wants that for a global var seem scarily likely.
Yes, using the GSP is more or less a recipe for fail. That's why I'd hoped to restrict it to quirks mode pages. Ah, well.
I was with you on that. I take it "4 out of 5 doctors recommend Trident" (pun!) prevailed?
Boris Zbarsky:
Per the current spec proposal, the GSP lives on the proto chain and exposes the various id/name stuff as own properties on itself, with the usual caveats about checking up the proto chain first that DOM named access has. See dev.w3.org/2006/webapi/WebIDL/#NamedPropertiesObject (which Window uses). There was a bunch of previous discussion about this at lists.w3.org/Archives/Public/public-script-coord/2012JanMar/0010.html and following, though it's not clear to me now whether the solution we decided on allows var to shadow these named props as desired after all...
I think it should, since they're exposed as data properties on the named properties object.
That's basically what the Global Scope Polluter does. That's certainly how I plan to implement it in Gecko in the WebIDL bindings: Window.prototype.proto will be a proxy which will do all the weird stuff the GSP has to do.
In FF 14, I’m getting the following results (if there is an element whose ID is "foo"):
$ "foo" in window
false
$ foo
ReferenceError: foo is not defined
$ window.foo // (*)
[object HTMLDivElement]
Element referenced by ID/NAME in the global scope. Use W3C standard document.getElementById() instead.
$ foo // (**)
undefined
Element referenced by ID/NAME in the global scope. Use W3C standard document.getElementById() instead. @ Web Console:1
$ "foo" in window
true
That looks like what you described: The Global Scope Polluter auto-creates foo after the read access (*). However, (**) puzzles me: A getter for foo seems to be called (as a warning is displayed), but it returns undefined
. How come?
Also interesting: window instanceof EventTarget holds, but EventTarget.prototype is not in protos(window), an array created by: function protos(obj) { var chain = []; while (obj) { chain.push(obj); obj = Object.getPrototypeOf(obj); } return chain; }
Axel
On 8/15/12 3:48 AM, Axel Rauschmayer wrote:
In FF 14, I’m getting the following results (if there is an element whose ID is "foo"):
$ "foo" in window false $ foo ReferenceError: foo is not defined
This is an artifact of how the repl you're using (presumably the Web Console in Firefox) is implemented. In particular, if you're using the Web Console its global is NOT the window, which allows you to declare variables visible in the repl but not leaking to the web page. But it leads to some weird artifacts with the global scope polluter, since there isn't one here, really.
In a web page, the behavior is different, as you can test by using an actual <script> tag.
That looks like what you described: The Global Scope Polluter auto-creates foo after the read access (*). However, (**) puzzles me: A getter for foo seems to be called (as a warning is displayed), but it returns
undefined
. How come?
Because you're testing in a somewhat bizarre environment.
Also interesting: window instanceof EventTarget holds, but EventTarget.prototype is not in protos(window)
Yes, indeed. Host objects can do some interesting things with instanceof. ;) Again, this will be fixed once we move Window to WebIDL.
Travis Leithead wrote:
From: Cameron McCormack [mailto:cam at mcc.id.au]
Brendan Eich:
As noted, they started out that way 17 years ago. I think WebIDL and interface-based method definition made onload, e.g., predefined on window objects, or more recently on Window.prototype. Was this useful? Was it intended specifically (for window, not just intended generally due to WebIDL's uniform rules for binding its definitions in JS)? I don't think it provides any benefit. Uniformity is the only reason the spec says they should be there, currently.
It does provide the monkey-patch benefit for "shared" interfaces (e.g., those shared by inheritance). At the present time, the only one I can think of that [will] act like this is EventTarget (IE10 hasn't yet implemented this hierarchy change).
Do you think it's worth another exception (to the exception whereby inherited properties are flattened to be "own" on the global) that adds EventTarget.prototype to window objects' prototype chains? Presumably in front of (closer to the head of the global object itself) the GSP.
Travis Leithead wrote:
From: Boris Zbarsky [mailto:bzbarsky at MIT.EDU]
On 8/12/12 5:29 PM, Brendan Eich wrote:
Boris Zbarsky wrote:
Note that data in lists.w3.org/Archives/Public/public-script-coord/2012JanMar/00 33.html suggests that IE also implements the erratum to 5.1 we were talking about up-thread. Oh what a tangled web we weave. Yes, current thinking is that we should take the erratum that major JS engines already fixed, and include it in ES6. But this means we must do something different in WebIDL, probably make own global properties for window-implements-interface-inherited attributes and even operations. And then (for strict mode) be careful about get-only accessors. This reminds me of [Replaceable], which was for non-writable but configurable data properties that var and function must be able to replace. It's kind of the opposite and only for accessors: var-captures-own-accessor-via-detection, or some such.
Just recapping, tell me if I'm missing something. The above sounds like a reasonable summary to me. Certainly hits all the high points of the discussion, with the addition that the GSP as currently specced depends on the erratum sticking around.
Allow me to also recap to ensure I understand this thread:
The problem: a few [high profile] sites are using a coding practice that uses feature detection of the following pattern: var [standardized property name] = window.[standardized property name] || window.[implementation-specific property name] || [etc.]
Usually window.[standardized property name] is last -- this matters below.
Firefox is affected by this problem (e.g., the result of the var declaration is undefined) Chrome is not affected by this problem because their var creation algorithm checks the prototype chain for an existing property name
No, Chrome is not affected because their IDL bindings put inherited attributes on the global object as "own" accessors.
IE10 is not affected by this problem because they define both "indexedDb" and "msIndexedDb" and the latter wins--otherwise they would be affected by this problem.
That's true, but see above point about order of || terms.
The reaction by this group is:
- Don't change anything about ES5.1 var declaration and initialization (because we like the behavior, it works well with global scope pollutors in the prototype chain)
No. Rather, the erratum at ecmascript#78 was already fixed by Mozilla, Google, Opera, and I believe IE and Apple. We think it's important for user-defined non-configurable properties on prototypes of the global object that var ignore such properties.
Instead, we think WebIDL must make a special case for inherited properties of the global object: make them "own".
Also, WebIDL-reliant spec authors must be careful not to define get-only accessors (readonly attributes) on the global object or interfaces from which it inherits. This leads to the idea that the global object defaults to [Replaceable] (which may be removed from WebIDL as explicit qualifier syntax) and [Unforgeable] must be used selectively for some few historic exceptions.
- Change WebIDL so that any properties that would mixin to Window (or any ancestors that Window would inherit from) would instead be created directly on the global object (instead of on a prototype of the global).
Yes, but note this is sufficient to keep the fix for erratum ecmascript#78 and not revert to ES5.1 spec.
Additionally, ensure that all of these properties are [Replaceable] meaning that if they are readonly (no setter), a [[Put]] request would instead create a data property of the same name in place of the pre-existing property.
Right.
One side effect as I understand this, would be that: var indexedDb = "test"; alert(indexedDb); would result in "test", and the original indexedDb property would be lost. Is this your understanding as well?
You bet. Who knows whether indexedDB (such a lovely name! snark) was not used as a var name (undeclared, even) in 2003 by some important site in Slovenia?
Another side effect of this proposed change is that: var onload = function(e) { ...} would actually assign the event handler (it does in Chrome today, not in Gecko/IE9/10).
Note that if you take away the 'var ' then Gecko at least assigns the event handler.
Per www.w3.org/TR/html5/browsers.html#the-window-object the onload, etc. attributes are read/write, so not (implicitly in a new world, and certainly not explicitly in the HMTL5 spec) [Replaceable]. So given the change to put inherited attributes on the global as "own" properties, 'var' can't matter. 'var' does not replace an existing own binding in ES5.1 or ES5.1 + the erratum fix.
I was curious just how "bad" the currently reported bug actually is. I ran a query looking for use of var indexedDb and var requestAnimationFrame across our web data index (which is unfortunately about a year old). jQuery uses the requestAnimationFrame a lot--but their use is contained to the jQuery scope closure. Modernizer was also using the var indexedDb technique, but Modernizer also runs in a scope closure and thus is not affected by this issue. In fact, any major library (and a lot of other sites that have adopted the "module pattern") won't be affected by this at all.
We need a lot more testing to know how bad, but things are bad enough IMHO, and this looks like a recurring problem.
So this begs the question: are we overreacting here? Personally, I'd like to avoid making a change to IE in this regard if I can avoid it. I'm not sure I'd recommend a change to WebIDL for this issue if we had found it in the wild--we might just expect sites to adjust their behavior instead (these affected high profile sites could arguably make an easy change to avoid this problem). The two side effects that immediately came to mind above could have worse impliciations than the current issue--especially the [Replaceable] change, since you inadvertently lose the original property in that case.
You get what the author wants, there is no unintended data loss.
Really, I urge you to consider how bad a plan it was (sorry I didn't flag it) to add readonly attributes to Window.prototype. That is a recipe for ongoing web compatibility breakage, whether 'var' is used or just bare assignment. HTML is not going to stop evolving and other specs will probably add predefined globals using "window implements NewInterface".
The ES5.1 erratum fix is important just for 'var' integrity in the face of Object.defineProperty on Object.prototype or any other window prototype chain object. We think we want it to stick in implementations, and to codify it in ES6.
The combination of the bad plan of window prototype chain extension, ad infinitum and with readonly attributes, and the ES5.1 erratum fix, creates an ongoing hazard. Since we want the ES5.1 erratum fix, and since the WebIDL global object prototype chain extension problem creates incompatibility even when 'var' is not used, I believe we should fix WebIDL to have a special case: flatten inherited attributes and operations onto the global as "own" properties.
On 8/15/12 3:32 PM, Travis Leithead wrote:
The problem: a few [high profile] sites are using a coding practice that uses feature detection of the following pattern: var [standardized property name] = window.[standardized property name] || window.[implementation-specific property name] || [etc.] Firefox is affected by this problem (e.g., the result of the var declaration is undefined) Chrome is not affected by this problem because their var creation algorithm checks the prototype chain for an existing property name
And also because in Chrome IDL attributes are placed directly on the object.
One side effect as I understand this, would be that: var indexedDb = "test"; alert(indexedDb); would result in "test", and the original indexedDb property would be lost. Is this your understanding as well?
That's correct.
Another side effect of this proposed change is that: var onload = function(e) { ...} would actually assign the event handler (it does in Chrome today, not in Gecko/IE9/10).
Again, correct.
I was curious just how "bad" the currently reported bug actually is. I ran a query looking for use of var indexedDb and var requestAnimationFrame across our web data index (which is unfortunately about a year old). jQuery uses the requestAnimationFrame a lot--but their use is contained to the jQuery scope closure. Modernizer was also using the var indexedDb technique, but Modernizer also runs in a scope closure and thus is not affected by this issue. In fact, any major library (and a lot of other sites that have adopted the "module pattern") won't be affected by this at all.
bugzilla.mozilla.org/show_bug.cgi?id=770844#c29 claims there is actual site breakage involved. Presumably Jonas can give you more detailed info here; ccing him.
Personally, I'd like to avoid making a change to IE in this regard if I can avoid it.
Are you planning to drop the prefixed name in IE?
On 8/15/12 5:08 PM, Brendan Eich wrote:
Usually window.[standardized property name] is last -- this matters below.
I don't see how it matters at all, actually.
Firefox is affected by this problem (e.g., the result of the var declaration is undefined) Chrome is not affected by this problem because their var creation algorithm checks the prototype chain for an existing property name
No, Chrome is not affected because their IDL bindings put inherited attributes on the global object as "own" accessors.
It's not affected for both reasons, at least in the last version of Chrome I tested.
That's true, but see above point about order of || terms.
Again, the order of the || terms doesn't matter. "X || undefined" and "undefined || X" both evaluate to X as long as X is not falsy.
No. Rather, the erratum at ecmascript#78 was already fixed by Mozilla, Google, Opera, and I believe IE and Apple.
It's not fixed by Apple as of the 2012-08-06 WebKit nightly.
It does seem to be fixed in Chrome dev (22.0.1229.6). It's not fixed in Chrome beta (21.0.1180.79). We'll see if and when the fix makes it to a final release, but in the meantime what's out there in users' hands right now does not have the fix.
Also, WebIDL-reliant spec authors must be careful not to define get-only accessors (readonly attributes) on the global object or interfaces from which it inherits. This leads to the idea that the global object defaults to [Replaceable] (which may be removed from WebIDL as explicit qualifier syntax) and [Unforgeable] must be used selectively for some few historic exceptions.
Note that there is a middle ground between [Replaceable] and [Unforgeable], which is where most props on the global live right now.... I suspect for web compat we may want to default all future stuff to being [Replaceable] but grandfather in the existing non-replaceable things.
On 8/16/12 1:24 PM, Travis Leithead wrote:
Personally, I'd like to avoid making a change to IE in this regard if I can avoid it.
Are you planning to drop the prefixed name in IE?
Eventually, yes. But I was referring to the way we build our type system which is a much more wide-spread and potentially disruptive change.
Sure. It's just easier to argue for "no changes to the specs" if you're not planning on dropping the prefixed properties, since then the situation doesn't actually affect you to start with... Given that you are, we still need a solution for when you do that.
Boris Zbarsky wrote:
On 8/15/12 5:08 PM, Brendan Eich wrote:
Usually window.[standardized property name] is last -- this matters below.
I don't see how it matters at all, actually.
It could matter if there's a stray indexedDB global lurking (set by script previously) but the var indexedDB = ... code in question wants the msIndexedDB on IE.
It could matter if a browser is like recent Firefoxes, and it defines both prefixed and unprefixed, but it also has the ES5.1 erratum fix so the var indexedDB by itself overrides an inherited Window.prototype.indexedDB. Then you want the prefixed one first.
Firefox is affected by this problem (e.g., the result of the var declaration is undefined) Chrome is not affected by this problem because their var creation algorithm checks the prototype chain for an existing property name
No, Chrome is not affected because their IDL bindings put inherited attributes on the global object as "own" accessors.
It's not affected for both reasons, at least in the last version of Chrome I tested.
Chrome -- really V8 -- doesn't have the ES5.1 erratum fix?
That's true, but see above point about order of || terms.
Again, the order of the || terms doesn't matter. "X || undefined" and "undefined || X" both evaluate to X as long as X is not falsy.
Not always -- see above.
No. Rather, the erratum at ecmascript#78 was already fixed by Mozilla, Google, Opera, and I believe IE and Apple.
It's not fixed by Apple as of the 2012-08-06 WebKit nightly.
It does seem to be fixed in Chrome dev (22.0.1229.6). It's not fixed in Chrome beta (21.0.1180.79).
Ah, thanks.
We'll see if and when the fix makes it to a final release, but in the meantime what's out there in users' hands right now does not have the fix.
Rats. This makes Firefox have two "kick me" signs taped to its back.
Also, WebIDL-reliant spec authors must be careful not to define get-only accessors (readonly attributes) on the global object or interfaces from which it inherits. This leads to the idea that the global object defaults to [Replaceable] (which may be removed from WebIDL as explicit qualifier syntax) and [Unforgeable] must be used selectively for some few historic exceptions.
Note that there is a middle ground between [Replaceable] and [Unforgeable], which is where most props on the global live right now.... I suspect for web compat we may want to default all future stuff to being [Replaceable] but grandfather in the existing non-replaceable things.
Whatever compat gods require. Hard to know whom they'll zap if a change is tried, or when data collection is "done". This often leads to taking the safe route and complicating the spec (in this case grandfathering as you suggest).
Travis Leithead wrote:
I still have my doubts about the magnitude of this problem,
Which problem? Clearly not all stable browsers have rolled out the ES5.1 erratum fix whereby var ignores the prototype chain. Also clearly, WebKit-based browsers bind attribiutes as "own" properties. We won't know unless and and until all browsers take the heat we're taking.
Not sure you're taking this heat yet either!
Furthermore, independent of this issue, I raised the (in hindsight untenable) injection of readonly attributes onto the global object or its prototype chain. This is just bad for backward compatibility on principle, and based on 16 years of experience. It's why we have [Replaceable]. So I think we should do something along the suggested lines (making that the default).
but if I'm wrong then I'd at least like to be able to grandfather-in some legacy APIs like addEventListener (or other APIs that make sense to have a single shared entry point for easy monkey patching).
Is this a used use-case? There's no grandfathering if WebKit-based browsers and Opera have never done it.
Are you guys using EventTarget.prototype.addEventListener = ... to affect window's event listening?
On Wed, Aug 15, 2012 at 8:16 AM, Boris Zbarsky <bzbarsky at mit.edu> wrote:
On 8/15/12 3:48 AM, Axel Rauschmayer wrote:
In FF 14, I’m getting the following results (if there is an element whose ID is "foo"):
$ "foo" in window false $ foo ReferenceError: foo is not defined
This is an artifact of how the repl you're using (presumably the Web Console in Firefox) is implemented. In particular, if you're using the Web Console its global is NOT the window, which allows you to declare variables visible in the repl but not leaking to the web page. But it leads to some weird artifacts with the global scope polluter, since there isn't one here, really.
I'd like to add that this is a genuine bug in the Web Console, not the
way we meant it to behave.
window.Object instanceof Object
is meant to be true.
See bug bugzilla.mozilla.org/show_bug.cgi?id=774753 about it. Use Firebug's console to avoid running into those issues.
Thaddee Tyl wrote:
From: Brendan Eich [mailto:brendan at mozilla.com]
Travis Leithead wrote:
I still have my doubts about the magnitude of this problem,
Which problem? Clearly not all stable browsers have rolled out the ES5.1 erratum fix whereby var ignores the prototype chain. Also clearly, WebKit- based browsers bind attribiutes as "own" properties. We won't know unless and and until all browsers take the heat we're taking.
Not sure you're taking this heat yet either!
No, we seemed to have lucked out in that regard.
Is that because you haven't unprefixed msIndexedDB yet, or you haven't shipped the ES5.1 erratum fix yet, or you don't do what WebIDL says, or some combination?
Sorry to ask, I'm pretty sure someone said what IE does but I've lost track. Figure others want to know too. Thanks,
On 8/16/12 10:06 PM, Brendan Eich wrote:
Is that because you haven't unprefixed msIndexedDB yet, or you haven't shipped the ES5.1 erratum fix yet, or you don't do what WebIDL says, or some combination?
Sorry to ask, I'm pretty sure someone said what IE does but I've lost track. Figure others want to know too. Thanks,
IE has the erratum fix, but hasn't dropped msIndexedDB yet.
On Aug 10, 2012, at 6:30 AM, Boris Zbarsky wrote:
I'm going to take a try setting this story straight:
Neither ES5 or ES5.1 does a "own" check for either global var or function declarations. See 10.5 step 5.c/8.b each of which calls HasBinding 10.2.1.2.1 which does a [[HasProperty]] which, if necessary, looks up the [[Prototype]] chain.
The change made in ES5.1 (renumbered existing ES5 10.5 step 5.e as 5.f and inserted a new step 5.e + substeps related to dealing with function declarations that over-wrote accessor properties (both own or inherited) of the global object as well function declarations that tried to redefined non-configurable global object properties. This change was motivated by bugzilla.mozilla.org/show_bug.cgi?id=577325 and the es-discuss thread referenced in that bug.
Note that the ES5.1 change only related to function declarations. ES5.1 did not make any changes to the specified semantics of global var declarations.
Subsequent discussion of Mozilla bug 577325 lead to ecmascript#78 . It proposes an errata (subsequently approved by TC39) to ES5.1's 10.5 that extends the function declaration fix to apply to var declarations. It also changed the check for a pre-existing global property to a "own" check. This latter change is presumably what has been implemented in FF and is causing the indexedDB issue.
These post ES5.1 errata was based upon these desired semantics:
"variable" accesses that bind to inherited properties of the global object should return the current value of the inherited property. (note such "variable" accesses may be to properties created by function declarations)
"variable" assignments to inherited properties of the global object should be equivalent to a [[Put]] to the global object. Whether or not a own property is created depends upon the [[Writable]] attribute of the inherited property and the extensible internal property of the global object.
global function and var declarations always create own properties of the global object. If an inherited property of the same name already exists it is over-ridden with an own property.
The declaration instantiation rules relating to pre-existing bindings only consider own properties of the global object. Inherited properties of the global object have no effect upon the processing of function and var declarations.
Supporting requirements 3&4 are where the "own" property checks were introduced.
However, I don't thing we can just drop them without some careful thought. If we did that, we would reintroduce problems related to global declarations firing inherited setters and also interactions between inherited property attributes and global declarations.
It feel like we are still playing semantics ping-pong with compat. bug paddles. I'm not sure that there is an ideal solution. Perhaps we need to consider var and function declarations separately. Perhaps we need be have different redeclaration rules for own and for inherited global object properties. In any chase I think we need to do a more care analysis then just fixing this bug and it needs be be coordinated between the ES spec. and WebIDL.