Open Bug 291083 Opened 20 years ago Updated 3 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.