Open Bug 291083 Opened 19 years ago Updated 2 years ago

Sort out event client coordinates and their usage in XUL

Categories

(Core :: DOM: Core & HTML, defect, P5)

defect

Tracking

()

People

(Reporter: bzbarsky, Unassigned)

References

Details

At the moment, we seem to generally assume, in various XUL interfaces, that
various coordinates are "client" coordinates.  There are several problems with this:

1)  In the DOM spec, clientX and clientY on event objects are not well-defined,
    since it's not clear what the "DOM implementation's client area" is.  Is it
    the coordinate space with its top left at the top left of the CSS initial
    containing block for the current document (so top left of the current
    document viewport)?  Or is it the coordinate space of the outermost viewport
    when frames are involved?  None of this is well-defined.  In practice I
    believe implementations generally (see item #2) use the top left of the
    current document viewport.
2)  In Mozilla, clientX and clientY behave weirdly when an event is targeted at
    a node inside a popup.  They try (and fail, actually, if the popup is in a
    subframe...) to be relative to the top-left hand corner of the _popup_.
    This causes various issues if people assume the DOM spec behavior (see bug
    290921).

As a result of #2, currently given an event you can't tell, from JS, where it
actually happened in "your" client coordinates; and in any case you can't tell
without examining the target node of the event.  We have nasty hacks around this
to make context menus and tooltips "work" by setting the popupNode on the XUL
document and then assuming that the coordinates we're asked to show a popup at
are in the client coordinate system of the ownerDocument of that node.  Note
that this breaks if the target node is within a popup, per item #2 above.

My proposal for sanity in this is as follows:

A)  Remove the special hack for popups -- make client* always return coordinates
    relative to the document's viewport.
B)  More significantly, always return the coordinates relative to the viewport
    of the document we're _dispatching_ the event in.  For content, where events
    don't cross document boundaries, this isn't a change at all.  For chrome,
    where events bubble in from content, we'd do coordinate conversion as the
    event crosses the boundary....  This is a little more work on the back-end
    side, but as a result we're guaranteed that all event consumers will always
    see event client coordinates in _their_ client coordinate system (and hence
    be able to meaningfully pass them to other things in the same chrome
    document).

Thoughts?  I think we should be able to do A pretty painlessly in 1.9... B may
be a little more painful, but I think it makes our platform _much_ more usable.
 Question is how much existing XUL content it would break.... :(
Blocks: 287357
Note:  Once this (especially the issue with popups) is fixed, the hacks that had
to be added to work around this in bug 290921 and bug 290878 should probably be
backed out...
Chrome isn't generally interested in event coordinates from content, of course
context menus and tooltips are a special case but I'm assuming you'll fix them.
> Chrome isn't generally interested in event coordinates from content

Are you sure?  Not even extensions?  And not parts of Firefox like the
autoscroll thingie?
my chrome is *very* interested in those coordinates, and i can't wait to deal
with the fallout from this.
So will this make life a lot harder for you, in general?  Right now, the problem
is that various operations that pass coords back into the layout engine (via box
objects) have no way to indicate what coord system the coords are in...

Thinking about it some more, though, if you want to use event coords but pass to
box objects in a _different_ document (eg the event target's box object) things
will fail with my proposal.  Right now they fail in reverse -- if you want to
pass the coords to box objects in your own document.

Any way we can reconcile the two?  Should we just nix all this client coord
stuff and use screen coords everywhere?  ;)
One way to solve it is to have the ability to convert coordinates between
documents. So that given DocA, DocB and and Ax,Ay (coordinates in DocA) you can
calculate Bx,By (coordinates in DocB).

This way you can not only pass coordinates to a box-object in any document. Not
just your own or the doc where the event occured, but also a third document.

The function should probably live on the view. So something like

windowB.convertCoordinates(windowA, Ax, Ay)

would return some object containing Bx and By. Or to avoid dealing with
returning a touple:

windowB.convertCoordinateX(windowA, Ax)
windowB.convertCoordinateY(windowA, Ay)
I guess we could do that (turn both into screen coordinates, etc).  Problem with
adding these on window, of course, is global scope pollution.... :(

If we do that, I'd say that any time a boxobject method is called the passed-in
coordinates should be client coords in the boxobject's document.
Definitely, ment to include that in my proposal. Sucks about global namespace
pollution, how have we delt with that before?
We created this windowUtils thing... But I think we want boxobjects usable from
unprivileged script, so that won't fly here.
i get events that came from content in chrome. i generally try to do all my math
relative to the original content window (since that's the only place where it
might make sense). if you make it so that when i ask for coordinates on the
content window's root elements, i get things that are relative to the chrome
window root just as events are going to be relative to the chrome window root,
then i'd probably be ok. but i don't think that's your plan, and you still
pretty much screw domi, since it really wants a way to provide content relative
coords, as they're again the only things that make sense to web developers.

i'm not really sure how well you'll screw me, i'm still trying to recover from
various other messes, and haven't looked at any of this code in months (it's
just worked for us for a while, so i've been dealing w/ gc crashers, which are
half fixed on some 1.7.x branch - introducing nifty regressions). of note, my
code needs to work in 1.7a, 1.8a5, and whatever new world you create, so if you
do make the coordinates misbehave, i need an easy and safe way to recognize that
i'm in the new world.

if i'm lucky, i should have time next week to try to figure out how all of these
changes actually affect my code. atm i'm just raising yellow flags because it
sure sounds like i'll be broken. - thankfully my old releases of code aren't
required to work in the new world, although i am always disappointed when i lose
that ability, because for regression testing and various other reasons, it's
*good* to be able to pull older versions from cvs and just use them.
> i get events that came from content in chrome. i generally try to do all my
> math relative to the original content window (since that's the only place
> where it might make sense).

Just to make it clear, the problem I am trying to solve is that various APIs
want coordinates but have no way to indicate what window the coordinates are
relative to.  So in the end we have to come up with a convention for these APIs
that is intrinsically defined in terms of the API itself.  That is, "coordinates
passed to this box object are in the client coordinate system of its document".
 Either that, or all methods that take coordinates need to take an explicit
coordinate system argument in some form.

Now operating under the assumption that coordinates to be passed to internal
APIs will always be in the coordinate system of the callee, we need to get those
coordinates into that coordinate system somehow.  This means converting, either
explicitly or implicitly, into the right coordinate system, somewhere.  Where
the conversions should happen depends on the usage patterns we have.  I'm also
fine with saying that event coordinates are in the client coordinate system of
the defaultView of the ownerDocument of the target, and implementing explicit
conversion functions as sicking proposed.   This may be less painful, in
general.  This does mean that chrome consumers need to do more work in some
cases; possibly less work in others.  What I'm looking for is a sense, in
general, of which approach would break the least and help the most.

At least in part, this depends on what the event flow in chrome actually is.  Do
events bubble from one chrome document to another?  Or capture like that?  Or is
it a direct jump from content to the chrome doc, with subdocs in chrome not
jumping at all?

Note that whatever we do I do NOT plan to remove any functionality.  Some of it
may just take a little more work than now.  As much as possible should take
less.  And some things that are currently completely impossible, in general,
such as reliably showing a popup at the point where the mouse was clicked,
should become possible.
(In reply to comment #11)
>At least in part, this depends on what the event flow in chrome actually is.
Basically events propagate to the parent chrome frame element, thus they only
jump over content subdocuments, see nsFrameLoader::EnsureDocShell().
Oh, I see.  And then they actually bubble through all nodes...  Given that and
XBL retargeting (which will change the target), we can't even use event.target
to reliably figure out where the event happened in today's world -- consider an
<iframe> in the anonymous content a binding hooks up.  So if we decide that the
client coords are in the target's client coord system, we still have to mess
with them when we retarget.
(In reply to comment #13)
>consider an <iframe> in the anonymous content a binding hooks up.
Well we have .originalTarget of course, but if I've written the event
retargetting code correctly, then events only get retargetted in three cases:
a) when the current target is native anonymous
b) when the new target will be the old target's XBL binding node
c) if not DOM event has been created, then when the new target uses XBL
Hmm... could case c) apply to an anonymous iframe? I hope not :-/
> b) when the new target will be the old target's XBL binding node

This is exactly the case I was thinking of.  Once an event bubbles out of the
XBL binding, the target will be the bound node, but coords will be relative to
the originalTarget, in a subframe somewhere.
I said the *old target*'s binding node, not the frame element's binding node.
> I said the *old target*'s binding node, not the frame element's binding node.

Wait.  You mean we don't always retarget events as they cross binding
boundaries?  That sounds like a bug we should fix....
(In reply to comment #17)
>>I said the *old target*'s binding node, not the frame element's binding node.
>Wait.  You mean we don't always retarget events as they cross binding
>boundaries?  That sounds like a bug we should fix....
Quite the reverse, that was the fix to a bug. We don't want to retarget events
just because they pass though an element with an XBL binding, we only retarget
events when they escape the anonymous content on which they were dispatched.
But it did.  The event was dispatched on a node which is in a document that's
included by an iframe that is anonymous content.  So if the event gets out of
the bindingParent of the iframe while still targeted into the iframe's document,
that seems wrong to me...
It's a tricky question actually. If you have an iframe that's an anonymous child
of a <h1> you'd probably want to retarget it since you wouldn't expect there to
be an embedded document inside there. But for an element like <browser> you
probably don't want to retarget just because the element happens to be
implemented using an anonymous iframe.

Maybe iframe should have an attribute specifing whether to retarget events
comming out of it or not. The attribute would be ignored if the iframe isn't
anonymous.
OK.  So back to the original question, then.  Given events bubbling through a
series of nested frames, and event retargeting aside, what's the most useful
coordinate system for the events.

Put another way, which way will code have to do the fewest conversions?

Note, just in case it wasn't clear, that if a parent document adds an event
listener to a node in the _child_ document, that event listener should see an
event in the child document's coordinates, no questions asked.  It's only when
an event bubbles up to the parent document that I'm considering changing coords.
IMHO the logical coordinate system is the one of the document where the listener
is attached.

I don't know if that's what will give the fewest conversions though.
(In reply to comment #19)
>But it did.
I think contentAreaClick.js relies on events not getting retargetted as they
bubble out of the tabbrowser's anonymous browsers.

(In reply to comment #20)
>If you have an iframe that's an anonymous child of a <h1>
Events don't bubble out of content frames, they go straight to chrome.
Blocks: 291390
> I think contentAreaClick.js relies on events not getting retargetted

Right.  That was sicking's example....

> Events don't bubble out of content frames

Who said the <h1> wasn't chrome?
Or that the <browser> isn't content?
(In reply to comment #25)
>Or that the <browser> isn't content?
Then it won't see any subdocument events anyway.
I think sicking's point was that it shouldn't so much matter what's chrome and
whats content.  Effectively, I think we want to treat the event bubble from
content to chrome (and from one chrome document to another, for that matter) as
just a form of retargeting.... Except of course people really do expect to have
event.target be the "original target" here, with the result that as things stand
behavior for an iframe in a XUL binding will depend on whether the binding is
attached to content or chrome.  :(
(In reply to comment #27)
>as things stand behavior for an iframe in a XUL binding will depend on
>whether the binding is attached to content or chrome.  :(
You mean as regards to whether the binding will see the subdocument's events?
No, as regards whether an ancestor of the bound element will see the
subdocument's events.
Blocks: 212081, 290878, 290921
Blocks: 319062
Assignee: general → nobody
QA Contact: ian → general
Priority: -- → P5
Component: DOM → DOM: Core & HTML
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.