Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

var, window, and object.watch()

44 views
Skip to first unread message

John J. Barton

unread,
Oct 2, 2009, 1:47:36 PM10/2/09
to
I asked this over in .platform and Boris suggested that folks reading
here may know more about the .watch() issue.

For Firebug 1.5 we are adding break on DOM object property change (along
with break on HTML, XHR, and Error). But one important case fails:
properties declared with 'var' at window level. Is there something
different about a property declared with var vs one just attached to the
window?

This code:
--------
function dumpIt(prop, oldval, newval)
{
window.dump("changed: "+prop+" from "+oldval+" to "+newval+"\n");
return newval;
}

var avar = "avar";
window.watch("avar", dumpIt);

window.dump("window.avar: "+window.avar+"\n");
window.dump("avar: "+avar+"\n");

window.dotProp = "dotProp";
window.watch("dotProp", dumpIt);

window.dump("window.dotProp: "+window.dotProp+"\n");
window.dump("dotProp: "+dotProp+"\n");

avar = "avar "+(new Date()).getTime();
window.dotProp = "dotProp "+ (new Date()).getTime();
window.avar = "window.avar "+(new Date()).getTime();

window.dump("window.avar: "+window.avar+"\n");
window.dump("avar: "+avar+"\n");
--------
produces:
--------
window.avar: avar
avar: avar
window.dotProp: dotProp
dotProp: dotProp
changed: dotProp from dotProp to dotProp 1254504382424
changed: avar from avar to window.avar 1254504382425
window.avar: window.avar 1254504382425
avar: window.avar 1254504382425

jjb

Wes Garland

unread,
Oct 2, 2009, 3:12:31 PM10/2/09
to John J. Barton, dev-tech-...@lists.mozilla.org
John:

Possible partial answer?

From https://developer.mozilla.org/en/JS_SetOptions, for JSOPTION_VAROBJFIX:

Make JS_EvaluateScript<https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_EvaluateScript>
() use the last object on its obj param's scope chain (that is, the global
object) as the ECMA "variables object".

This flag is recommended. Without it, the two scripts "x = 1" and "var x =
1", where no variable x is in scope, do two different things. The former
creates a property on the global object. The latter creates a property on
obj. With this flag, both create a global property.

This flag was added, IIRC, to bring jsapi into ES3 conformance.
Wes

--
Wesley W. Garland
Director, Product Development
PageMail, Inc.
+1 613 542 2787 x 102

Boris Zbarsky

unread,
Oct 2, 2009, 3:34:03 PM10/2/09
to
On 10/2/09 1:47 PM, John J. Barton wrote:
> var avar = "avar";
> window.watch("avar", dumpIt);

Wes' reply made me realize something.... This is setting a watch on the
outer window. |var avar| declares avar on the inner window.

> avar = "avar "+(new Date()).getTime();

And this unqualified assignment assigns to the script's global object,
so the inner window.

> window.avar = "window.avar "+(new Date()).getTime();

Whereas this assigns on the outer window, with a setter that actually
forwards the set to the inner.

I wonder whether watch() should innerize the object it's called on...
Actually watching things on the outer window seems pretty useless to me.
Alternately, whatever looks up watch stuff on property sets should
outerize the object it has. I bet this is all extra-special fun when a
single outer window has two different inners that are both running
script and setting properties; what exactly is the desired watch()
behavior in that case?

-Boris

John J. Barton

unread,
Oct 2, 2009, 11:30:53 PM10/2/09
to

Well I don't know about inner/outer. The desired behavior is for any
change to X to trigger the window.watch(X) because any change to X
changes window.X. Anything else makes the watch-based feature a bug:
it's not reliable and we have no test to tell users when it will fail.

jjb

Boris Zbarsky

unread,
Oct 3, 2009, 2:33:45 PM10/3/09
to
On 10/2/09 11:30 PM, John J. Barton wrote:
> Well I don't know about inner/outer. The desired behavior is for any
> change to X to trigger the window.watch(X) because any change to X
> changes window.X.

This last claim is in fact false, at least in Gecko. A change to X in a
script running inside a global (inner window) that is no longer the
current inner for the outer will not change the value of window.X (which
always delegates to the current inner), nor vice versa. Hence the
questions I raised in my previous mail. Attached is a simple testcase
that demonstrates this pretty clearly.

Maybe I should explain inner/outer windows briefly.

Every web page has a global object. This is an inner window, and has
the same lifetime as the page. Script can never get a direct reference
to this object. Unqualified global variable assignments (whether using
var or not) are assignments to properties of this object. This object
knows what its outer window object is.

There is also the object you reference as |window|. This is the outer
window object, and has the same lifetime as the <browser> or <iframe> or
<frame> that holds the rendering area in question. This is typically
longer than the page lifetime. This object knows what its current inner
window object is. A property get or set on this object just gets or
sets the property on its current inner window.

Note that there is a one-to-many relationship of outer window objects to
inner ones. It is quite possible to have more than one inner window
"alive" at the same time for a given outer, however only one will be the
_current_ inner. Again, the attached testcase demonstrates such a
situation.

Given the above, how exactly should watch() work?

-Boris

window-inner-outer.html

Nickolay Ponomarev

unread,
Oct 3, 2009, 6:05:44 PM10/3/09
to dev-tech-...@lists.mozilla.org
On Sat, Oct 3, 2009 at 10:33 PM, Boris Zbarsky <bzba...@mit.edu> wrote:

> Maybe I should explain inner/outer windows briefly.

> [snip]
>
Anyone has time to update
https://developer.mozilla.org/En/SpiderMonkey/Split_object with this
description?

Nickolay

John J. Barton

unread,
Oct 4, 2009, 12:03:02 AM10/4/09
to
Boris Zbarsky wrote:
> On 10/2/09 11:30 PM, John J. Barton wrote:
>> Well I don't know about inner/outer. The desired behavior is for any
>> change to X to trigger the window.watch(X) because any change to X
>> changes window.X.
>
> This last claim is in fact false, at least in Gecko. A change to X in a
> script running inside a global (inner window) that is no longer the
> current inner for the outer will not change the value of window.X (which
> always delegates to the current inner), nor vice versa. Hence the
> questions I raised in my previous mail. Attached is a simple testcase
> that demonstrates this pretty clearly.
>
> Maybe I should explain inner/outer windows briefly.
>
> Every web page has a global object. This is an inner window, and has
> the same lifetime as the page. Script can never get a direct reference
> to this object. Unqualified global variable assignments (whether using
> var or not) are assignments to properties of this object. This object
> knows what its outer window object is.

We want to issue obj.watch('x', foo) on unqualified global variable
assignments, but we can never get a reference 'obj'.


>
> There is also the object you reference as |window|. This is the outer
> window object, and has the same lifetime as the <browser> or <iframe> or
> <frame> that holds the rendering area in question. This is typically
> longer than the page lifetime. This object knows what its current inner
> window object is. A property get or set on this object just gets or
> sets the property on its current inner window.

Then it sounds like window.watch('x', foo) should fire foo() when the
inner window property 'x' changes. That would cause the unqualified
global variable assignments to to fire foo() and the window.qualified
assignments as well.

>
> Note that there is a one-to-many relationship of outer window objects to
> inner ones. It is quite possible to have more than one inner window
> "alive" at the same time for a given outer, however only one will be the
> _current_ inner. Again, the attached testcase demonstrates such a
> situation.
>
> Given the above, how exactly should watch() work?

The only alternative to the above I see is such that the following code
issues two alerts rather than one:

var oldhref = window.location.href;
window.location = "data:text/html,<body>";
while (window.location.href == oldhref) {
var xhr = new XMLHttpRequest();
xhr.open("GET", oldhref, false);
xhr.send();
}
x = 4;
window.xx = 6;
watch('x', alert);
window.watch('xx', alert);
x = 5;
window.xx = 7;

However, this version would cause us to issue two watch() calls for
window properties, since 'x' -- from Firebug's point of view -- is
ambiguous. We can only know about 'x' by enumerating the only window we
have, so we can only have one 'x'. Thus we would have to cover both
paths with the same function. Plus it adds to the mystery, rather than
avoiding it.

jjb

Boris Zbarsky

unread,
Oct 4, 2009, 10:47:15 AM10/4/09
to
On 10/4/09 12:03 AM, John J. Barton wrote:
> We want to issue obj.watch('x', foo) on unqualified global variable
> assignments, but we can never get a reference 'obj'.

Correct.

> Then it sounds like window.watch('x', foo) should fire foo() when the
> inner window property 'x' changes.

You say "the" but there's more than one. Do you mean "the current inner
window"? Do you mean "any inner window, whether it's the current one or
any future one"? Do you mean something else?

Furthermore, are you also interested in assignments that happen when
there is no inner window at all (e.g. after the window has been closed)?

> The only alternative to the above I see is such that the following code
> issues two alerts rather than one:

Is the suggestion that unqualified |watch()| would watch the inner while
qualified watch() would watch the outer?

-Boris

Boris Zbarsky

unread,
Oct 4, 2009, 11:16:36 AM10/4/09
to
On 10/3/09 6:05 PM, Nickolay Ponomarev wrote:
>> Maybe I should explain inner/outer windows briefly.
>> [snip]
>>
> Anyone has time to update
> https://developer.mozilla.org/En/SpiderMonkey/Split_object with this
> description?

That page seems to contain all the relevant information already, except
the "getters/setters forward to the inner", which it discusses as
something that's not part of the JSAPI inner/outer setup but specific to
particular pairs of inner/outer objects...

-Boris

John J. Barton

unread,
Oct 4, 2009, 12:46:28 PM10/4/09
to
Boris Zbarsky wrote:
> On 10/4/09 12:03 AM, John J. Barton wrote:
>> We want to issue obj.watch('x', foo) on unqualified global variable
>> assignments, but we can never get a reference 'obj'.
>
> Correct.
>
>> Then it sounds like window.watch('x', foo) should fire foo() when the
>> inner window property 'x' changes.
>
> You say "the" but there's more than one. Do you mean "the current inner
> window"? Do you mean "any inner window, whether it's the current one or
> any future one"? Do you mean something else?

Which ever is easier. Because we have no means of dealing with the
inner/outer window stuff, I'm really only interested in the case where
it won't matter.

>
> Furthermore, are you also interested in assignments that happen when
> there is no inner window at all (e.g. after the window has been closed)?

No, we can't do anything useful with Javascript in closed windows in
Firebug.

>
>> The only alternative to the above I see is such that the following code
>> issues two alerts rather than one:
>
> Is the suggestion that unqualified |watch()| would watch the inner while
> qualified watch() would watch the outer?

Yes, that was my suggestion. I don't think it is a good one.

jjb

Boris Zbarsky

unread,
Oct 4, 2009, 12:53:57 PM10/4/09
to
On 10/4/09 12:46 PM, John J. Barton wrote:
>> You say "the" but there's more than one. Do you mean "the current
>> inner window"? Do you mean "any inner window, whether it's the current
>> one or any future one"? Do you mean something else?
>
> Which ever is easier. Because we have no means of dealing with the
> inner/outer window stuff, I'm really only interested in the case where
> it won't matter.

Well, the two approaches above have wildly different behavior. In
particular, one will keep watching across page loads (modulo any
watchpoint-clearing we do, of course) and one won't. Which one of those
do you want?

>> Is the suggestion that unqualified |watch()| would watch the inner
>> while qualified watch() would watch the outer?
>
> Yes, that was my suggestion. I don't think it is a good one.

Agreed. ;)

-Boris

John J. Barton

unread,
Oct 4, 2009, 1:58:47 PM10/4/09
to
Boris Zbarsky wrote:
> On 10/4/09 12:46 PM, John J. Barton wrote:
>>> You say "the" but there's more than one. Do you mean "the current
>>> inner window"? Do you mean "any inner window, whether it's the current
>>> one or any future one"? Do you mean something else?
>>
>> Which ever is easier. Because we have no means of dealing with the
>> inner/outer window stuff, I'm really only interested in the case where
>> it won't matter.
>
> Well, the two approaches above have wildly different behavior. In
> particular, one will keep watching across page loads (modulo any
> watchpoint-clearing we do, of course) and one won't. Which one of those
> do you want?

For Firebug it has to be "won't watch across page loads", because as far
I understand it there is no way a action in on one page can create state
for a subsequent page such that the change in the second page would have
importance for the code on the first. In other words, if I set a
breakpoint on 'x' and it fired when I loaded a new page, I would be very
confused (and I don't think Firebug would be in a pretty state either).
Whether or not this can happen isn't so important: we can't deal with it
if it does.

For Chromebug I have no idea what any of this means. There we are
'outside' of the windows; I don't know if that alters what we can see or
not. From extensions can I get to obj.x for the outer window, so that I
can create obj.watch('x')?

jjb

Boris Zbarsky

unread,
Oct 5, 2009, 12:41:37 PM10/5/09
to
On 10/4/09 1:58 PM, John J. Barton wrote:
> For Firebug it has to be "won't watch across page loads"

OK. Sounds like you want watch() forwarded to the inner object. I've
filed https://bugzilla.mozilla.org/show_bug.cgi?id=520572

> For Chromebug I have no idea what any of this means. There we are
> 'outside' of the windows; I don't know if that alters what we can see or
> not. From extensions can I get to obj.x for the outer window, so that I
> can create obj.watch('x')?

There should be no differences between chromebug and firebug here, I'd
think.

-Boris

Brendan Eich

unread,
Oct 5, 2009, 8:46:52 PM10/5/09
to
Making watch "innerize" should fix the bug, and provided there's a
security check(!) should be safe.

But has anyone tried just watch(...) instead of window.watch(...)? The
window predefined global property is bound to the outer window, but an
unqualified watch call ought to pass the inner window to the native
watch method as its |obj| parameter.

/be

Boris Zbarsky

unread,
Oct 5, 2009, 9:16:12 PM10/5/09
to
On 10/5/09 8:46 PM, Brendan Eich wrote:
> But has anyone tried just watch(...) instead of window.watch(...)? The
> window predefined global property is bound to the outer window, but an
> unqualified watch call ought to pass the inner window to the native
> watch method as its |obj| parameter.

That really doesn't work if you want to have a privileged script doing
the watching, since the only way to achieve the above is to inject a
<script> into the window being debugged, right?

-Boris

Brendan Eich

unread,
Oct 5, 2009, 9:34:10 PM10/5/09
to

Sure, we need a fix to innerize -- but it seemed like an easy way to
test the hypothesis.

/be

John J. Barton

unread,
Oct 6, 2009, 12:19:46 AM10/6/09
to
Brendan Eich wrote:
> Making watch "innerize" should fix the bug, and provided there's a
> security check(!) should be safe.
>
> But has anyone tried just watch(...) instead of window.watch(...)? The

Yes, see my post of 10/3 with this subject. Sorry, no dice, if my code
is correct.

jjb

Blake Kaplan

unread,
Oct 6, 2009, 3:19:36 PM10/6/09
to
Brendan Eich <bre...@mozilla.org> wrote:
> But has anyone tried just watch(...) instead of window.watch(...)? The

This won't work because in order to get the watched object, we call the
thisObject hoook on 'this', which outerizes and wraps.
--
Blake Kaplan

Brendan Eich

unread,
Oct 6, 2009, 3:24:48 PM10/6/09
to
On Oct 6, 12:19 pm, Blake Kaplan <mrb...@gmail.com> wrote:

> Brendan Eich <bren...@mozilla.org> wrote:
> > But has anyone tried just watch(...) instead of window.watch(...)? The
>
> This won't work because in order to get the watched object, we call the
> thisObject hoook on 'this', which outerizes and wraps.

D'oh! I forgot that the thisObject hook outerizes but I should've
remembered.

The only way forward is to fix watch to innerize, with security
checking.

/be

0 new messages