Bug 27183 - REGRESSION: document.documentElement.getScreenCTM() returns incorrect matrix.
: REGRESSION: document.documentElement.getScreenCTM() returns incorrect matrix.
: WebKit
: 528+ (Nightly build)
: Macintosh Mac OS X 10.6
: P2 Major
Assigned To:
: http://www.w3.org/TR/SVG/types.html#I...
  Show dependency treegraph
Reported: 2009-07-11 15:11 PST by
Modified: 2010-04-01 01:30 PST (History)

SVG test document. (2.06 KB, image/svg+xml)
2009-07-11 15:11 PST, Glenn Brauen
no flags Details
SVG file showing workaround (2.50 KB, image/svg+xml)
2009-09-23 11:29 PST, Glenn Brauen
no flags Details
SVG File to Dump Computed CTMs to console. (1.53 KB, image/svg+xml)
2009-09-23 12:10 PST, Glenn Brauen
no flags Details
Initial patch (32.01 KB, patch)
2010-03-31 09:11 PST, Nikolas Zimmermann
no flags Review Patch | Details | Formatted Diff | Diff
Updated patch (31.94 KB, patch)
2010-03-31 09:36 PST, Nikolas Zimmermann
krit: review+
Review Patch | Details | Formatted Diff | Diff


You need to log in before you can comment on or make changes to this bug.

Description From 2009-07-11 15:11:49 PST
Created an attachment (id=32619) [details]
SVG test document.

The following javascript should convert the clientX/Y coordinates for a mouse event to SVG coordinate space:

var m = document.documentElement.getScreenCTM();
var p = document.documentElement.createSVGPoint(); 
p.x = evt.clientX;
p.y = evt.clientY; 
p = p.matrixTransform(m.inverse());

In Safari 4.0.2 and all webkit nightlies since r43284 (May 6, 2009) this coordinate transformation does not work properly.  The transformation depends on how the SVG viewport fits in the browser window.  If the SVG viewport exactly matches, the transformation seems to be alright but if you stretch the browser window horizontally or vertically, the transformation is off.

I'll attach a SVG document that demonstrates the problem by displaying a tooltip showing the SVG coordinates for mouse events when the cursor is positioned over a rectangle in the document.

What works, what doesn't:

Defect appeared in latest Safari 4 (4.0.2).  Safari 4.0.1 was fine.
Firefox 3 is fine.

Also tested webkit nightlies:
r45754 (July 11, 2009): broken
r45423 (July 1, 2009): broken
r44341 (June 4, 2009): broken
r43796 (May 16, 2009): broken
r43436 (May 9, 2009): broken
r43284 (May 6, 2009): broken
r43163 (May 3, 2009): fine
r43110 (May 1, 2009): fine

To reporoduce: 

Download and open the attached SVG (ScreenCoordinates.svg).  Mouseover the rectangle and check the position and content of the tooltip displayed - it should be located on (above and to the right of) the cursor.
------- Comment #1 From 2009-07-17 14:19:30 PST -------
Bumped to Major.  This really breaks any web app that wants to place things in SVG based on mouse events (tooltips are an obvious case).  I've also seen HTML divs positioned absolutely based on the location of SVG objects - and this is now broken in Safari 4.0.2 as well.  I suspect that is the inverse computation from what this bug describes.
------- Comment #2 From 2009-08-13 08:14:41 PST -------
Still broken in Safari 4.0.3.
------- Comment #3 From 2009-08-27 14:12:16 PST -------
Problem exists on PowerPC and Intel Mac platforms and on both OS X 10.4 and OS X 10.5.
------- Comment #4 From 2009-09-17 11:25:52 PST -------
I have the same problem on Safari Version 4.0.3 (6531.9) on OS X 10.6.

Could someone with CANCONFIRM please take a look at this?
------- Comment #5 From 2009-09-23 11:29:57 PST -------
Created an attachment (id=40007) [details]
SVG file showing workaround

I have attached an SVG file showing a workaround to this bug.  The difference is shown below and can be summarized as follows:

- The Coordinate Transformation Matrix (CTM) returned by document.documentElement.getScreenCTM() is wrong.
- Separately applying document.documentElement.currentTranslate, document.documentElement.currentScal, and a CTM computed from the event target to the SVG root node produces the correct transformation of the event clientX/clientY coordinates.

diff ScreenCoordinates.svg ScreenCoordinates_workaround.svg
>       function computeCumulativeCTMFromRoot(el, svgNode) {
>               var ctm = el.getCTM();
>               while ((el = el.parentNode) != svgNode) {
>                       ctm = el.getCTM().multiply(ctm);
>               }
>               return(ctm);
>       }
<                * Apply screen CTM transformation to the evt screenX and screenY to convert the event
>                * Apply screen translate, scale and a CTM transformation
>                * computed from the event target up to the enclosing SVG node to convert the event
<               var m = document.documentElement.getScreenCTM();
>               var trans = evt.target.ownerDocument.documentElement.currentTranslate;
>               var scale = evt.target.ownerDocument.documentElement.currentScale;
>               var m = computeCumulativeCTMFromRoot(evt.target, document.getElementById("main"));
<               p.x = evt.clientX;
<               p.y = evt.clientY; 
>               p.x = (evt.clientX - trans.x) / scale;
>               p.y = (evt.clientY - trans.y) / scale;
------- Comment #6 From 2009-09-23 12:10:41 PST -------
Created an attachment (id=40010) [details]
SVG File to Dump Computed CTMs to console.

I have added an attachment that dumps the computed CTMs to the console log for comparison.  One of the translate values (x or y) in the CTM returned by getScreenCTM() appear to always be double the value returned by the other CTM computation.
------- Comment #7 From 2009-09-23 12:16:41 PST -------
I'm surprised this is still sitting as unconfirmed.  This happens in all versions of Safari since 4.0.2 (in fact since nightly build r43284 - May 6, 2009 as I point out above) on PowerPC and Intel Macs running any of Mac OS 10.4, 10.5 or 10.6.

Maybe everybody has been computing their own CTMs all along...
------- Comment #8 From 2009-12-06 10:54:47 PST -------
getCTM and getScreenCTM are specified by SVGLocatable. Both do the same. While getCTM should return the CTM according to the viewport coordinate system, getScreenCTM should return CTM according to the parent user agent. getScreenCTM is wrong in trunk. See http://www.w3.org/TR/SVG/types.html#InterfaceSVGLocatable .
------- Comment #9 From 2010-03-12 04:53:54 PST -------

I also think that this bug should be fixed as soon as possible, as this method is very essential to many interactive SVG applications. Many other javascript functions build upon the SVGLocatable interface.

It is also a regression of something that previously worked.

Related to this bug are:

Here is another good testcase:
If the browser window is very wide then the red circles are way off the mouse cursor, while they should appear directly below the cursor.

BTW: Glenn, thanks for posting the workaround!
------- Comment #10 From 2010-03-23 23:13:33 PST -------
With a reduced test case, someone should be able to run bisect-builds over the nightlies:
and find out what revision range caused this regression.  Then we can better understand what went wrong and who might know best how to fix it. :)
------- Comment #11 From 2010-03-24 04:38:03 PST -------
Running "bisect-builds -r 43163:43284" - Glenn thanks a lot for the detailed investigation!
------- Comment #12 From 2010-03-25 08:49:49 PST -------
All issues with getScreenCTM fixed, going to upload a patch soon, stay tuned...
------- Comment #13 From 2010-03-31 09:11:28 PST -------
Created an attachment (id=52178) [details]
Initial patch

Phew, this was more tricky than I thought. This is only a regression, because of pure luck. The getScreenCTM() implementation was just plain wrong.
When we added support to handle non-SVG parent elements (mixed content documents, ie. a <div> containing a <svg>) the implementation of getScreenCTM() got worse. It has never been correct so far, and finally is after this patch.

Checked all testcases against Opera / Firefox, which both have bugs.
Firefox mixes up getCTM/getScreenCTM: when using absolute positioned <div> object containing a <svg> it lists the left/top CSS translation as translation values in the getCTM matrix, instead of just in the getScreenCTM matrix. Opera gets all of this right. Opera has another bug leading to null matrices in getCTM() when using viewBox on a <svg> fragment in a XHTML document.

I double-checked all testcases, by hand (fun to multiply this by hand and verify ;-) and I'm happy with it now.
------- Comment #14 From 2010-03-31 09:20:21 PST -------
Attachment 52178 [details] did not build on qt:
Build output: http://webkit-commit-queue.appspot.com/results/1610093
------- Comment #15 From 2010-03-31 09:21:12 PST -------
Attachment 52178 [details] did not build on chromium:
Build output: http://webkit-commit-queue.appspot.com/results/1591108
------- Comment #16 From 2010-03-31 09:36:14 PST -------
Created an attachment (id=52180) [details]
Updated patch

s/DocumentScope/ScreenScope/ upon Dirks request.
Remove CString.h include (debugging leftover) - EWS complained because if has recently been moved to wtf/CString.h
Fix typo s/substract/subtract/

No substantial changes.
------- Comment #17 From 2010-03-31 09:39:11 PST -------
(From update of attachment 52180 [details])
------- Comment #18 From 2010-04-01 01:30:51 PST -------
Committed r56905: <http://trac.webkit.org/changeset/56905>