Closed Bug 410820 Opened 17 years ago Closed 13 years ago

<svg> elements respond to mouse events when they should not

Categories

(Core :: SVG, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: christian, Assigned: jwatt)

References

(Depends on 1 open bug)

Details

Attachments

(2 files, 1 obsolete file)

User-Agent:       Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6
Build Identifier: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9b3pre) Gecko/2008010404 Minefield/3.0b3pre

An SVG element consumes mouse events so it is impossible for html elements behind the SVG to receive those events.

In Safari, SVG elements don't consume mouse events by default, and the behavior can be controlled with the pointer-events setting, which is very useful. I wish Firefox would behave like Safari in this regard.



Reproducible: Always

Steps to Reproduce:
1. Open attachment with Firefox
2. Try to click the link behind the triangle (Doesn't work - good)
3. Try to click the link in the red rectangle next to the triangle


Actual Results:  
You can't click on the link anywhere in the rectangle.



Expected Results:  
I should be able to click on the link in the rectangle, either by default (this is Safari's behavior), or at least if pointer-events is set to "none".



This may be related to 398588
I don't know whether or not this is a bug, and (if so) whether it's a
bug in SVG.  Reclassifying on the assumption the description is
correct.

Also, the description from comment #0 fits recent trunk nightlies
(2007-12-13) on Windows and Linux.  So this problem (if it is a
problem) isn't specific to OS X.
Component: General → SVG
OS: Mac OS X → All
Product: Firefox → Core
QA Contact: general → general
Hardware: Macintosh → All
Version: unspecified → Trunk
Summary: SVG elements consume mouse events → <svg> elements respond to mouse events when they should not
I'd hate to see Firefox 3 go out without at least supporting the pointer-events="none" case.
Assignee: nobody → jwatt
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Attachment #304607 - Flags: superreview?(roc)
Attachment #304607 - Flags: review?(longsonr)
Shouldn't child SVG elements be able to override pointer-events to receive events themselves? This would break that, which would be pretty bad. Wouldn't GetFrameForPointSVG be a better place to fix this?

I'm not sure that just supporting 'none' is better than not supporting the property at all. Is there a bug on a full implementation of pointer-events?
In any case, I think this is less important than any SVG regressions we have.
> Shouldn't child SVG elements be able to override pointer-events to receive
events themselves?

I'm not sure about that.  The spec http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty says that a value of 'none' says "The given element does not receive pointer events."  

Based on capture/bubble phases, wouldn't that just "rebound" the mouse event at the capture stage and prevent child elements from being able to receive it?  Put another way, a child of a <g> element is a part of that <g> element, so by definition it would also not receive pointer events if it's parent has pointer-events="none".

By the way, in case there's some wild chance you don't already know, there is some pointer-events functionality in Firefox already.  See icons and their outlines at http://www.codedread.com/menu.svg 

But it sounds like the intention of this bug is allow the mouse events to go _through_ the SVG to underlying elements if the authors want to - which we do (think of "decorative overlays" of SVG that lay atop the entire client area).  Sounds like at the moment the events are being "swallowed" by the SVG without getting a chance to continue propagating to the rest of the compound document.

Someone please correct my understanding.
(In reply to comment #6)
> I'm not sure about that.  The spec
> http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty says that a
> value of 'none' says "The given element does not receive pointer events."  

What about its children, if they override pointer-events? This should be clear from the spec, if the spec doesn't say, it should be fixed.
It's very possible I'm misinterpreting something here, but if the parent element does not receive a pointer event, even at capture phase, how could its children receive that event (regardless of what its pointer-events attribute says.  

Capture phase goes down the DOM right?  If a parent element can't receive the event, that means its children are automatically exempt from receiving the event too, no?
(In reply to comment #8)
> Capture phase goes down the DOM right?  If a parent element can't receive the
> event, that means its children are automatically exempt from receiving the
> event too, no?

Not necessarily.

The way events work is:
1) figure out which element is the target
2) process capturing phase
3) process bubbling phase

pointer-events would affect step 1.

Note that if element A has child B which extends outside A's bounds, and we mouse over the sticking-out part of B, then A can still capture the event during the capture phase. Event capturing isn't really related to determining the event target.

Since it would be incredibly useful to be able to use pointer-events:none on a container and then pointer-events:fill (or something else) on selected children of the container, I'd be surprised if pointer-events:none prevented all descendants from receiving events.
(In reply to comment #4)
> Shouldn't child SVG elements be able to override pointer-events to receive
> events themselves? This would break that, which would be pretty bad. Wouldn't
> GetFrameForPointSVG be a better place to fix this?

Yes, SVG children should be able to override pointer-events, and they still can. GetMouseThrough is only used as a post-HitTest check on the frame returned, so it only has an effect if the frame matched is the nsSVGOuterSVGFrame, and it has no effect if the frame matched is an SVG child.

http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/layout/base/nsDisplayList.cpp&rev=3.39&mark=351#346 

It's not possible to implement this in GetFrameForPointSVG because the HitTest match occurs as a result of hit testing the *background and border* nsDisplay, which happens after GetFrameForPointSVG has returned nsnull.
I'm wondering if the thing to do is have nsSVGOuterSVGFrame::BuildDisplayList not call DisplayBorderBackgroundOutline but instead do what that method does but create a class derived from nsDisplayBorder i.e. nsSVGDisplayBorder. You would then implement HitTest in that class to do what you want.
OK, the scenario where this doesn't work is something like

<svg>
  <rect width="100%" height="100%">
  <rect width="100%" height="100%" style="pointer-events:none;"/>
</svg>

nsDisplaySVG::HitTest returns the second rect, which returns true for mouse-through, and then we ignore all the SVG instead of falling back to the second rect.
@roc: Thanks for clarifying that ... I think the spec should be updated to say pointer-events:none means that the element cannot be the target of an event, which some clarifying text that this doesn't effect bubbling/capture
roc: I'm not sure why you think that. First of all, nsDisplaySVG::HitTest will not return the second rect since it has |pointer-events:none| - nsDisplaySVG::HitTest will return the first rect. Regardless of which rect it returns, the rect will not return true for GetMouseThrough as you fear though since I've implemented that method on nsSVGOuterSVGFrame, not on nsSVGPathGeometryFrame. (I.e. the variable 'f' at the bonsai link points to the frame for the <rect>, not the frame for the <svg>). 

I tested this just to confirm that I'm not on crack - mouse events go to the first rect as expected.

longsonr: perhaps, but GetMouseThrough seems purpose built for this so that seems unnecessarily complex.
Okay...

Wouldn't it be just as easy and make more sense to implement pointer-events in nsDisplayBackground and nsDisplayBorder for all elements?
We could do that, but since pointer-events isn't defined in a CSS spec I don't think we should. The reason I'm only trying to implement 'none' here is that pointer-events isn't defined in terms of the normal CSS model. How 'none' should work is obvious, but it's unclear to me how all the other values should work in the normal CSS context where 'border' and 'background' matter, and not 'stroke' and 'fill'. While I think it's safe and very valuable to implement the 'none' value for outer <svg>, I think clarification of the other cases in a generic CSS context should be more carefully done.

Also, note that mPointerEvents is a member of nsCSSSVG, not nsCSSStruct, so your suggestion would require some juggling of code and therefore isn't quite as simple. ;-)
(In reply to comment #14)
> 
> longsonr: perhaps, but GetMouseThrough seems purpose built for this so that
> seems unnecessarily complex.
> 

Yes but it's a dead end solution. You can't extend it for other pointer event types because you don't have a mouse position. If you use your own class with its own HitTest you have something that could be used to implement all pointerEvent types.
We actually do want to use pointer-events for all elements ... HTML needs it. Mapping "fill" to mean the padding-box and "stroke" to mean the border seems completely logical. But we don't want to do this for 1.9.

I don't think we want to do it for SVG for 1.9 either. We've got more important bugs to fix.
Well "we" may not want to, but I do and so do people on the SVG and CDF WGs (and we'd pass more tests). This patch may not be the correct fix going forward, but it's safe, adequate for 1.9, and it's in hand. IMO it would be a frustrating waste not to take it now that it's here.

Yes, I agree we have more important bugs, and I'm working on some of them. I thought this was a quick and useful patch and hadn't realized the confusion it would generate.
Attachment #304607 - Flags: superreview?(roc) → superreview+
I've looked at this again and I'm happy now with GetMouseThrough as a mechanism. I didn't completely grasp that we're hitting the CSS background to the outer svg frame which covers the whole frame so the mouse position is irrelevant.

I think something like this would be the least surprise solution i.e. just treat it the background like an image.

PRBool
nsSVGOuterSVGFrame::GetMouseThrough() const
{
  // http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty
  switch (GetStyleSVG()->mPointerEvents) {
  case NS_STYLE_POINTER_EVENTS_NONE:
    return PR_TRUE;
  case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
  case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
  case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
  case NS_STYLE_POINTER_EVENTS_VISIBLE:
    if (!GetStyleVisibility()->IsVisible())
      return PR_TRUE;
    break;
  case NS_STYLE_POINTER_EVENTS_PAINTED:
  case NS_STYLE_POINTER_EVENTS_FILL:
  case NS_STYLE_POINTER_EVENTS_STROKE:
  case NS_STYLE_POINTER_EVENTS_ALL:
    break;
  default:
    NS_ERROR("not reached");
    break;
  }
  return PR_FALSE;
}

r=longsonr assuming that you update the patch to deal with visibility too.
Attachment #304607 - Flags: review?(longsonr) → review+
I think this has come up again in the svg developers yahoo group. Have you any plans to finish this off and land it Jonathan?
Darn! Yes, I'd like to check this in. :-( What do you think roc?
Address comment 20 and you're good to land then.
Longsonr, things are not as simple as your code in comment 20 would hope. See the comments inline in the patch.

Roc, the reason that the code to handle pointer-events can't be located in nsSVGOuterSVGFrame::GetFrameForPoint is because separate nsDisplay items are created for border and background (by the DisplayBorderBackgroundOutline call in nsSVGOuterSVGFrame::BuildDisplayList). It's these nsDisplay objects that catch the pointer event. In comment 11 longsonr suggests an alternative approach, but I'm not really keen on the code duplication (and risk of getting out of sync that that entails). I think for the moment this patch get's round the concern of breaking more people in future, but I'm happy to knock up something else if you prefer.

The only big question is whether 'background:rgba(0,0,0,0)' (the computed value for 'background:transparent', which is the default) should catch events given 'pointer-events:visiblePainted' (the default) or not. If it should, then I'm doing the wrong thing here (and this bug isn't quite so urgent since we won't be changing the default behavior in future).
Attachment #304607 - Attachment is obsolete: true
Attachment #373884 - Flags: review?(roc)
I don't really like this because it's carving out an exception for <svg> elements as the only replaced elements which can be transparent to events. I think we should solve this bug by making pointer-events extend to all elements --- bug 380573.
Attachment #373884 - Flags: review?(roc)
Depends on: 380573
This bug is a showstopper for our application on Firefox as we currently do not have any acceptable workaround. I would like to express the severity of the impact that this bug has on us, but at the same time ask if there are any workarounds that would cause the original test-case (attached by Christian Pekeler) to exhibit the desired behavior.
Bug 508179 landed so you should be able to set pointer-events:none on the outermost <svg> element to get what you want.
That will be in Firefox 3.6. No solution for earlier versions I'm afraid.
Thanks Robert and Jonathan, this bug still depends on 380573, so I wasn't tracking 508179.
(In reply to comment #26)
> I don't really like this because it's carving out an exception for <svg>
> elements as the only replaced elements which can be transparent to events. I
> think we should solve this bug by making pointer-events extend to all elements
> --- bug 380573.
You could think about a different way:  SVG falls within the SVG namespace, thus it follows SVG rules.  (Or do you think it would be a good idea to get this clarified in the HTML5 spec?  There is an ongoing discussion about whether such clarification is necessary.)

------
On another note, I would also like to add that it is very important to the future of SVG usage that the SVG tag does not act like an invisible barrier over HTML content underneath... so there is some urgency not to let this issue lag too long to be fixed (as webmasters have to make sure enough users have the feature).

Essentially, the svg tag should act just like a div with visibility=none; nothing gets rendered on-screen, thus, it cannot trigger any events or block content underneath (the exception, of course, is that child elements can render to screen if they are SVG graphical elements).

----------
The specs:
After having discussed this issue at some length, I think it is pretty safe to say that the base svg tag (when embedded) in a HTML document should not block content underneath, should not be the trigger for any events, etc....  At least everything suggests that from the SVG spec side.  Do you think that it would be useful to add a clarifying note to the HTML5 spec?


For reference:
Ongoing whatwg discussion: http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2010-August/027661.html
Ongoing www-svg@w3.org discussion: 
http://lists.w3.org/Archives/Public/www-svg/2010Aug/0004.html
(In reply to comment #30)
> Thanks Robert and Jonathan, this bug still depends on 380573, so I wasn't
> tracking 508179.

Would it be appropriate to ask if maybe this might not depend on Bug 380573?

I don't know if there are implementation details that are causing issues; however, from the spec side of things, the two are not related.

Let me explain...
From the spec side:
Technically, "pointer-events" do not apply to svg tags.  "pointer-events" only apply to graphical svg elements (like rect) - that are rendered on screen.  You can add the pointer-events property to the svg tag, but it has zero effect on it.

As a simple proof of this: place an svg element within another svg element.  Note that the child svg element cannot trigger an event directly (be an event target) no matter what you do.

A simple way to think about it is that the svg tag acts very similar to a div with visibility=none.  Just like the div, nothing gets rendered to screen; just like the div, it does not create an invisible barrier that blocks content underneath;, just like the div it cannot receive events or interact with the user since nothing is rendered on-screen.  Essentially, the svg tag is a non-graphical element that does not show up as anything to the user.

Of course all of this is from the spec side....  The real question is if there are implementation problems in Gecko that require Bug 380573 to be fixed first.  Since, I don't know anything about that part... I thought I might just ask. :)
Sorry for the rather poor explanation.  I've summed up this issue on the SVG mailing list here: http://lists.w3.org/Archives/Public/public-fx//2010JulSep/0077.html

They are planning to discuss it at the upcoming W3 meeting in a week or two.  Will anyone from Mozilla be there?

Note: the explanation at that link might help frame the issue better.
The conversation in this bug has gotten rather long and at times very confused, so I've decided to open bug 629176 for discussing/reevaluating the default handling of pointer events on <svg> inline in HTML. If you're interested in the issue, please read the opening comment in full before commenting.

As for this bug, the original issue and testcase from comment 0 and comment 1 have been fixed by bug 508179, so I'm closing it as fixed.
Status: ASSIGNED → RESOLVED
Closed: 13 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: