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
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
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
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
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
> 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
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
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
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
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
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
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
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
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
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
Sure, we need a fix to innerize -- but it seemed like an easy way to
test the hypothesis.
/be
Yes, see my post of 10/3 with this subject. Sorry, no dice, if my code
is correct.
jjb
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
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