Figuring out the behavior of WindowProxy in the face of non-configurable properties

Boris Zbarsky bzbarsky at mit.edu
Thu Dec 4 16:32:18 PST 2014


On 12/4/14, 1:36 PM, Travis Leithead wrote:
>> Note that "window" is not the global.  It's a proxy whose target is the global.
>
> Yes, but within a browser UA, there is no way to get a reference to the naked global because all entry-points return window proxies ;-)

Well, no way from web script.  The browser internals can do it, 
presumably, right?

>> Well, good question.  If we don't do this restriction (by which I assume
>> defineProperty throwing; I assume getOwnPropertyDescriptor claiming
>> configurable always is less controversial), what do we want to do?
>
> As I look back on your original message, I fail to see what the problem is. You seem to think that the window proxy is referring to the same window object before and after the navigation.

The window proxy object identity does not change before and after the 
navigation.

The window object the proxy is pointing to changes.

> In fact, in most implementations that I'm aware of, there is the concept of the "inner" and "outer" window.

Yes, I'm well aware.

> The "outer" window is the window proxy, which is the object that implements the cross-origin access control.

In Gecko, the cross-origin access control is actually implemented using 
a separate security membrane proxy whose target is the "outer" window. 
But sure.

> In IE's implementation, the window proxy has no storage as a typical JS var--it's only a semi-intelligent forwarder to its companion "inner" window.

That's an IE implementation detail.  In Gecko, the "window proxy" is a 
JS proxy object with a proxy handler written in C++.  That, too, is an 
implementation detail.

What matters here is what JS consumers see.  Consumers typically (there 
are some exceptions involving scope chains) just see the window proxy, yes?

So when a script does:

   Object.defineProperty(frames[0], "foo", { value: true; });

It is defining a property on frames[0].  The fact that this is actually 
a proxy for some other object (the global inside that iframe) is 
somewhat of an implementation detail, again.  From the consumer's and 
the spec's point of view, frames[0] is something with some internal 
methods ([[GetOwnProperty]], [[DefineOwnProperty]], etc) which are 
implemented in some way.  Still from the spec's point of view, the 
implementation of these internal methods must satisfy 
<http://people.mozilla.org/~jorendorff/es6-draft.html#sec-invariants-of-the-essential-internal-methods>.

> So, in your code sample, your "defineProperty" call forwarded to the "inner" window where the property was defined.

Sure.  I understand that.  As in, the proxy's [[DefineOwnProperty]] 
invoke's the target's [[DefineOwnProperty]].

> After the navigation, the "inner" window was swapped out for a new one (and whole new type system at that) which the existing window proxy ("outer" window) now refers.

Sure.

> This gave the appearance of the non-configurable property disappearing

This isn't about "appearance".  The relevant spec invariant for 
[[GetOwnProperty]], for example, is:

   If P’s attributes other than [[Writable]] may change over time or
   if the property might disappear, then P’s [[Configurable]] attribute
   must be true.

And Object.getOwnPropertyDescriptor is clearly defined to invoke 
[[GetOwnProperty]].

So when a page does Object.getOwnPropertyDescriptor(window, "foo") this 
is invoking the window proxy's [[GetOwnProperty]].  That's allowed to do 
all sorts of stuff as long as it preserves the invariants involved, 
including the one I quote above.  The fact that the "disappearing" is 
due to the target changing is an implementation detail of the window proxy.

> but in reality it would still be there if you could get a reference to the "inner" window

Which doesn't matter, because the consumer is not interacting with the 
"inner" window.

> *I wonder if you can capture the inner window in a scope chain or closure somehow

Sure, for a scope chain.  Testcase at 
https://web.mit.edu/bzbarsky/www/testcases/windowproxy/use-old-window-1.html 
shows "OLD WINDOW" on the first line in Firefox, Chrome, and Safari.  In 
IE11 it throws a "Can't execute code from a freed script" exception; I 
can't find anything in the specs that allows that, fwiw.

> so that you could observe that "foo" is still there even though you can't directly see it anymore?

Absolutely.

> I think that might work if the executing code was defined in the old iframe's environment and executed after navigation...

Right.

But we're not talking about indirect probes like this here, just about 
the basic invariants object internal methods are supposed to preserve.

-Boris


More information about the es-discuss mailing list