Closed Bug 1025815 Opened 10 years ago Closed 6 years ago

Incorrect result of document.caretPositionFromPoint after CSS transform

Categories

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

30 Branch
defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla62
Tracking Status
firefox62 --- fixed

People

(Reporter: inferrna, Assigned: ke5trel)

References

Details

(Keywords: testcase)

Attachments

(5 files)

User Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0 (Beta/Release)
Build ID: 20140608211622

Steps to reproduce:

Trying to get caret position offset inside element after css transform.scale() was applied.
example is in attachment. In chromium it works fine.


Actual results:

Returning value is incorrect and depends on scaling factor.


Expected results:

Returning value must be the same as without transform.
Status: UNCONFIRMED → NEW
Component: Untriaged → DOM
Ever confirmed: true
Keywords: testcase
OS: Linux → All
Product: Firefox → Core
Hardware: x86_64 → All
Probably related to bug 863618.
Depends on: 863618
This bug needs its own fix in nsIDocument::CaretPositionFromPoint.
No longer depends on: 863618
Assignee: nobody → ke5trel
Comment on attachment 8969602 [details]
Bug 1025815 - caretPositionFromPoint needs to account for CSS transforms.

Boris is a better reviewer here.
Attachment #8969602 - Flags: review?(overholt) → review?(bzbarsky)
Comment on attachment 8969602 [details]
Bug 1025815 - caretPositionFromPoint needs to account for CSS transforms.

https://reviewboard.mozilla.org/r/238370/#review245186

r=me with the minor test nit below.

Please let me know if you won't be able to update the patch to that nit and I can do it.

::: dom/base/test/test_caretPositionFromPoint.html:113
(Diff revision 1)
>  
> +    // Check the first and last characters of the transformed div.
> +    var test7Element = document.getElementById('test7');
> +    var test7Rect = test7Element.getBoundingClientRect();
> +    checkOffsetsFromPoint(Math.round(test7Rect.left + 1), Math.round(test7Rect.top + 1), 0, 'test7');
> +    checkOffsetsFromPoint(Math.round(test7Rect.left + test7Rect.width - 1), Math.round(test7Rect.top + 1), 13, 'test7');

How about "test7Rect.right - 1" instead of manually adding up left+width?
Attachment #8969602 - Flags: review?(bzbarsky) → review+
Attachment #8969602 - Flags: review?(overholt)
Can you please trigger a try build for me?
Flags: needinfo?(bzbarsky)
Yep, triggered https://treeherder.mozilla.org/#/jobs?repo=try&revision=8d8bfd7ebe67cb49c00a913facc1aed6cfef5130

If that succeeds (and I expect it will), I'll make sure this lands.
Flags: needinfo?(bzbarsky)
OK, there are definitely some test failures.  Mostly in test_caretPositionFromPoint.html but also in test_bug864595.html.
Flags: needinfo?(ke5trel)
Thanks for running the try for me.

TransformRootPointToFrame uses _global_ coords so it does not work correctly in iframes (I was running the tests directly unframed), should be using GetEventCoordinatesRelativeTo. I also had to move test7 so it wasn't clipped by the frame. The failing tests now pass locally when run in iframes using --verify.
Flags: needinfo?(ke5trel)
Kicked off another try run at https://treeherder.mozilla.org/#/jobs?repo=try&revision=1ca71aaff219e39e27e2d3f4b073a87cef63322e&selectedJob=176508048

I need to think a bit about the actual patch (and whether I'm even the right reviewer for it).  Having to go through widgets here is a bit odd to me.
Thanks! I couldn't find any layout helper functions that do this transformation without using widgets. The approach here is the same as nsDOMWindowUtils::SelectAtPoint which already works with CSS transform.
Comment on attachment 8969602 [details]
Bug 1025815 - caretPositionFromPoint needs to account for CSS transforms.

https://reviewboard.mozilla.org/r/238370/#review247668

Sorry for the lag here.... This looks good, modulo the nits below.

::: dom/base/nsDocument.cpp:9998
(Diff revision 3)
>        nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
>    if (!ptFrame) {
>      return nullptr;
>    }
>  
> -  // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
> +  // Require frame-relative coordinates for GetContentOffsetsFromPoint.

How about "We need frame-relative...."

::: dom/base/nsDocument.cpp:10001
(Diff revision 3)
>    }
>  
> -  // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
> -  // to adjust to frame-relative coordinates before we can perform this call.
> -  // It should also not take into account the padding of the frame.
> -  nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
> +  // Require frame-relative coordinates for GetContentOffsetsFromPoint.
> +  nsPoint aOffset;
> +  nsCOMPtr<nsIWidget> widget = nsContentUtils::GetWidget(ps, &aOffset);
> +  LayoutDeviceIntPoint aPoint =

This should probably have a better name than aPoint.  Maybe pointRelativeToWidget?

::: dom/base/test/test_caretPositionFromPoint.html:125
(Diff revision 3)
>  <body onload="doTesting();">
>  <div id="a" contenteditable><span id="test1">abc, abc, abc</span><br>
>  <span id="test2" style="color: blue;">abc, abc, abc</span><br>
>  <textarea id="test3">abc</textarea><input id="test4" value="abcdef"><br><br>
>  <marquee>marquee</marquee>
> +<div id="test7" style="transform: translate(140px, -20px); display: inline-block;">abc, abc, abc</div>

Please document why those specific translate() values are used.
Attachment #8969602 - Flags: review+
Comment on attachment 8969602 [details]
Bug 1025815 - caretPositionFromPoint needs to account for CSS transforms.

https://reviewboard.mozilla.org/r/238370/#review249056
Hmm.  I somehow missed the mail for comment 16....   I don't know how that happened.

Triggered another try run: https://treeherder.mozilla.org/#/jobs?repo=try&revision=e9bdf625075fef728146bfc441ad7b49a6b7c11b
Pushed by bzbarsky@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/f365bf91369c
caretPositionFromPoint needs to account for CSS transforms. r=bz
Thank you for fixing this!
https://hg.mozilla.org/mozilla-central/rev/f365bf91369c
Status: NEW → RESOLVED
Closed: 6 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla62
Thank you very much for fixing this.

Interestingly I notice this affects the result even when transforms are not in use. For example, in the attached test case, if we call caretPositionFromPoint at a point exactly half way across a character, it will now return the *next* character whereas previously it returned the character under the cursor.

That is, before the patch it would report 0 but now it will report 1.

I don't think that's a problem but I'm adding this test case in case it breaks anyone's content (it broke some automated tests I have for this).
There is a similar change in result for SVG text. In this case taking the point exactly between two characters (according to Range.getBoundingClientRect) returns the *previous* character after this change, where it used to return the *next* character.
Thanks for sharing details about your test breakage Brian. Non-transform cases are expected to be affected by this change and it now behaves consistently with caret placement using the mouse cursor.

Here's what I get with your tests:

> Ubuntu 18.04
> FF60  HTML: 0  SVG: 1
> FF62  HTML: 1  SVG: 1

> Windows 10 VirtualBox
> FF60  HTML: 0  SVG: 1
> FF62  HTML: 0  SVG: 1

I cannot reproduce your results with the SVG case or with Windows 10 VirtualBox. In the Linux HTML case it varies with the character, eg 'A' gives offset 0 while 'Z' gives offset 1. The 'A' middle position is 14px while 'Z' is 13.5px, so it appears subpixel positions are being rounded up causing the offset to go to the next character. Rounding is expected with this change due to the conversion from app units to device pixels which is necessary to utilize functions that account for CSS transform.
Component: DOM → DOM: Core & HTML
Regressions: 1546612
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: