How are executionContext objects related to nsIDOMWindows? When I've
poked around with jsd.enumerateContexts() I found jsdIContext-s with
nsiDOMWindow globalObjects and some "other stuff". Some of the other
stuff has
var isTimer = (jscontext.privateData instanceof nsITimerCallback);
but I could find nothing else useful.
"Rubbing the electric lamp is not particularly rewarding. Anyway,
nothing exciting happens."
jjb
From my brief look at the relevant jsd guts just now, it looks like an
executionContext more or less corresponds to a JSContext. That
JSContext may or may not be one for a DOM window, of course. It might
also be a sandbox, a JS component scope, a subscript loader context, a
DOM worker, the safe context, etc. See the various callers of
JS_NewContext.
Setting scriptsEnabled only works (doesn't throw) if the JSContext has
an associated nsIScriptContext. In that case it might or might not
correspond to a window, but if it has script running on it, then it
_probably_ does. Nothing guarantees that, but I think in practice it's
true in Firefox (since the only nsIScriptContext things around should be
the XBL and XUL proto globals/context, which compile script but don't
run it, and window contexts).
> When I've
> poked around with jsd.enumerateContexts() I found jsdIContext-s with
> nsiDOMWindow globalObjects and some "other stuff". Some of the other
> stuff has
> var isTimer = (jscontext.privateData instanceof nsITimerCallback);
> but I could find nothing else useful.
The privateData in this case is more or less guaranteed to be an
nsIScriptContext.... That said, the only nsIScriptContext I see around
that implements nsITimerCallback is nsPythonContext. I really doubt
you're seeing those here!
So if the above was supposed to mean that the RHS of that assignment is
true, then I have no idea why that happens for you.
Hope the above helps; not sure it does, but then I'm not sure what the
question was, past "what exactly is a jsdIContext?"
-Boris
According to what we are seeing, this.onBlur is called while our
executionContext.scriptsEnabled is false:
win.addEventListener("blur", this.onBlur, true);
where win is a XUL browser element that corresponds to our executionContext.
So can the eventListener be run in an executionContext other than the
'win' content? (FF 3.6)
>
...
>> var isTimer = (jscontext.privateData instanceof nsITimerCallback);
>> but I could find nothing else useful.
>
> The privateData in this case is more or less guaranteed to be an
> nsIScriptContext.... That said, the only nsIScriptContext I see around
> that implements nsITimerCallback is nsPythonContext. I really doubt
> you're seeing those here!
I guess there is another one, very common lurking. In fact I think these
are setTimeouts? (I did this investigation back in FF3.0 I think; and
this is just FYI, not about the real problem I have above).
jjb
Corresponds in what sense? And I really doubt a <xul:browser> is
stashed in a variable named |win|; I'd think that's a Window object....
> So can the eventListener be run in an executionContext other than the
> 'win' content? (FF 3.6)
In the above situation, this.onBlur will be called on win's JSContext.
> I guess there is another one, very common lurking. In fact I think these
> are setTimeouts? (I did this investigation back in FF3.0 I think
Oh, ancient history. I had checked 1.9.1 and m-c, but not 1.9.0. In
1.9.0, nsJSContext implemented nsITimerCallback. That's all gone in
1.9.1 and later; it's using a callback function instead.
-Boris
P.S. If what you really meant is that |win| is a Window whose JSContext
is the thing your executionContext reflects then you may well just be
seeing <https://bugzilla.mozilla.org/show_bug.cgi?id=409737>. Disabling
script on an nsIScriptContext disables attempts to compile/run new
scripts or run timeouts; it doesn't affect execution of existing scripts
that are in the middle of running and doesn't affect
addEventListener-added event listeners, or other JS-implemented XPCOM
interface kinda things, per the bug cited above. Oh, and not sure about
XBL constructors, for that matter....
-Boris
Corresponds in the sense that "|win| is a Window whose JSContext
is the thing your executionContext reflects"
>
>> So can the eventListener be run in an executionContext other than the
>> 'win' content? (FF 3.6)
>
> In the above situation, this.onBlur will be called on win's JSContext.
But to your point below, this executionContext.scriptsEnabled call
fails, so it's worse than another context.
> P.S. If what you really meant is that |win| is a Window whose JSContext
> is the thing your executionContext reflects then you may well just be
> seeing <https://bugzilla.mozilla.org/show_bug.cgi?id=409737>. Disabling
> script on an nsIScriptContext disables attempts to compile/run new
> scripts or run timeouts; it doesn't affect execution of existing scripts
> that are in the middle of running and doesn't affect
> addEventListener-added event listeners, or other JS-implemented XPCOM
> interface kinda things, per the bug cited above. Oh, and not sure about
> XBL constructors, for that matter....
This is real bad. It means we cannot trust anything we see in
Firebug/Chromebug because the state can be changing while we examine a
snapshot.
jjb
Can you just put the debuggee window in the mode it's in during sync
XHR, where events are buffered instead of being delivered?
-Boris
"You" probably being the jsd guts, since I think this involves nsIDocument.
-Boris
Exactly. That is why I tell web developers that if they are seeing
something that defies logic, then to switch to another browser/
debugger to verify.
In the cases of JS frameworks like ExtJS, jQuery, etc., they have
their own addListeners so it looks to the browser like it is always
the same function being called (thus the limited usefulness of seeing
the JS code in eventbug too). With all events pouring through the same
function, and continuing to do so after stopping in the debugger, you
can imagine the mess. Thankfully, they all try and normalize events
between browsers -- so they copy out the important info, and leave a
reference to the original event. This tends to protect many users from
noticing.
Also, most of the time you can live with some other event running.
However, events that happen in pairs (I'm thinking mouseover/mouseout,
etc), tend to share data, and stuff breaks down. Not to mention all
the side effects of other code running. I don't even know what happens
when you are stopped on a breakpoint and some other code runs and has
a JS error. Or infinite loop.
I wonder if this is at all related to Firefox showing the long running
script dialog when you are stopped at a breakpoint...
-steve roussey-
But that would mean chaos when the developer continues execution after
debugging. The developer pushes "continue" and a huge slug of events arrive.
Maybe we could walk the event listener info list, remove them, debug,
add them.
jjb
You already have that "problem" on any page that has an async XHR in
flight, or any other network activity, at the moment when you pause the
script. As soon as you unpause, it'll receive all the buffered-up
network events all at once. I don't see why this is an issue.
> Maybe we could walk the event listener info list, remove them, debug,
> add them.
For everything in the web page?
Seriously, you might want to check with smaug, but I suspect the sync
XHR mode might be what you want here. Depending on what you want, of
course.
-Boris
How about blur, mouseover?
>
>> Maybe we could walk the event listener info list, remove them, debug,
>> add them.
>
> For everything in the web page?
Why not yes?
>
> Seriously, you might want to check with smaug, but I suspect the sync
> XHR mode might be what you want here. Depending on what you want, of
> course.
I want what jsdIContext.scriptsEnabled promises:
|true| if this context should be allowed to run scripts, |false|
otherwise.
But there must be something else going on. I guess the event queue must
purge events or the events must arrive on the context and be discarded
when scriptsEnabled == false.
jjb
I'd suggest just removing them. That seems to mimic what it means to
have the JS engine "stopped". No one really wants 100 mouseover events
to flood in when you have a breakpoint on a mouseover event, and move
your mouse across the page to the debugger. It is non-intuitive to hit
continue and then be right back at the breakpoint for something you
didn't just do.
-steve--
How about looking at the code before saying things that are completely
irrelevant? In the sync XHR mode, not _all_ events are buffered. Most
are just completely ignored. In particular, mouseover is not buffered.
What's buffered, as far as I can tell, are focus, blur, mousedown,
mouseup, and some keyboard events.
-Boris
What about them?
>> For everything in the web page?
>
> Why not yes?
Because that sounds like a security nightmare. Furthermore, you
wouldn't be able to remove the chrome event listeners, so if you click
in the page chrome might still run script on that page. That might run
on the chrome jscontext, of course, but can definitely change the page.
> I want what jsdIContext.scriptsEnabled promises:
> |true| if this context should be allowed to run scripts, |false| otherwise.
No, what you want is to completely freeze the state of the DOM, layout,
and script data structures on the page. .scriptsEnabled doesn't promise
that, nor deliver it.
Not least because a context being not allowed to run scripts says
nothing about scripts on the page running: they can be called on some
other context (a simple cross-window function call will do the trick).
> But there must be something else going on. I guess the event queue must
> purge events or the events must arrive on the context and be discarded
> when scriptsEnabled == false.
I'm not sure what you're trying to get at here.
-Boris
I _meant_ to say performance nightmare. Though it'd obviously need a
security review too, of course; I suspect it can probably be done
safely, though.
-Boris
I am confused because Firebug works pretty well for several years then
suddenly we learn that a fundamental aspect was broken the whole time.
It does not add up.
jjb
My bad. You wouldn't happen to know off the top of your head where to
look for the sync XHR mode code is so I can look it up?
I'm not even sure about getting rid of them now.
What happens to code for mouseover/mouseout when it is written such
that they always happen in pairs? That there will be a mouseout before
another mouseover on the same (or not the same, but related) element?
I always thought it was strange that things like events, event
listers, and visual stuff like hover still work when the debugger has
things stopped.
My $0.02
-steve--
Look for callers of SuppressEventHandling(), EventHandlingSuppressed().
> What happens to code for mouseover/mouseout when it is written such
> that they always happen in pairs? That there will be a mouseout before
> another mouseover on the same (or not the same, but related) element?
Check with smaug?
-Boris
Chances are:
1) It didn't actually work as well as you thought it did.
2) The hard cases that cause it to completely fall down
are comparatively rare.
At the very least. Possibly something else going on too.
-Boris
Ok this helps a lot. I don't know what sync XHR mode is but,
nsIDOMWindowUtils is a script callable interface for nsIDOMWindows:
http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/base/nsIDOMWindowUtils.idl#328
The other function seems to be C++.
jjb
Unfortunately a quick test of this idea fails for Chromebug debugging
Firebug. The problem I believe is that the JS code is in browser.xul, so
Chromebug calls suppressEventHandling() on browser.xul, but the onBlur()
functions are added to Firebug's panels which are browser elements
inside of browser.xul. They may not count as a "subwindow". The general
case may be hard to sort out, but Firebug on web pages may still work.
jjb
Like quick tests often are, that result was just bogus, the interface
has to be obtained through a different incantation,
getInterface(nsIDOMWindowUtils). Events are suppressed during debugging
now. Thanks Boris once again.
jjb
I think people give Firebug a bit of leeway. It is an extension after
all, written in JS itself. I don't know if people expect the same kind
of correctness from it as they would if it were part of Firefox itself
(IE4 and Script Debugger nailed correctness a long time ago). Firebug
is so feaking usable, we all overlook any of its flaws...
Not to mention the people that know what is going on probably find it
in their own interests to just hack around this issue. It is like all
the Fx bugs related to display:none that have been around near a
decade. Real developers know that it is a lot of work to create real
bug reports. Casual developers won't be able to tell you what the
problem really is. Bug reports are a bad indicator of importance
anyway. Hearing people curse at their PCs can be more telling. ;)
> Chances are:
>
> 1) It didn't actually work as well as you thought it did.
> 2) The hard cases that cause it to completely fall down
> are comparatively rare.
3) Lots of people use helper JS libs like ExtCore, ExtJS, jQuery, YUI,
etc. They all have work-arounds to some degree.
-s
Uh... example?
-Boris
Hey, I didn't even know we had that exposed on nsIDOMWindowUtils. I was
figuring we'd need to add more black magic into jsd. Good to know we
don't. ;)
-Boris
I don't want to waste your time. There are bug reports already out
there. But take a div and try throwing a flash object or an iframe set
to design mode and then set the div to display none and back. The
videos get reset to the beginning (or whatever the flash object was)
and design mode changes in a display none (worse -- in a block that
switches to display none during the asyncronous design mode change) is
just a bag of hurt. The flash issue has been around a decade, and
it's hard to worry too much about display none and design mode when
creating a wysiwyg editor has so many other freaky bugs to worry
about. Display:none has some other weird side effects on stuff. I'd
have to check on code I've written to remember. Though, that's the
point. I've gotten over that hurdle. So I'm ahead of those who have
not. So I no longer care that they get fixed.
And it's fun to see when projects get to the point where they come up
with workarounds. Leaking anon divs into user's js code comes to mind.
You can look at comit times for that workaround in jquey, dojo, extjs,
mootools, etc to see when their code became useful enough and used
enough to show the bug and demand a fix. Last I looked, only ExtJs
found a workaround that got around the security context error and this
doesn't need a try/catch. That bug in Fx is now fixed, btw.
Even better, John already comitted a fix to svn! Woohoo!
-Steve--
Ah, ok. So not display:none per se, but issues where state is stored in
frames. Sure. That makes a lot more sense.
-Boris