Bug 172019 - elementFromPoint() should consider x and y to be in client (layout viewport) coordinates
Summary: elementFromPoint() should consider x and y to be in client (layout viewport) ...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: DOM (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Ali Juma
URL:
Keywords: InRadar
Depends on:
Blocks: 170981
  Show dependency treegraph
 
Reported: 2017-05-11 22:14 PDT by Simon Fraser (smfr)
Modified: 2017-07-11 11:01 PDT (History)
7 users (show)

See Also:


Attachments
WIP (7.40 KB, patch)
2017-06-26 11:59 PDT, Ali Juma
no flags Details | Formatted Diff | Diff
Patch (12.53 KB, patch)
2017-07-04 16:28 PDT, Ali Juma
no flags Details | Formatted Diff | Diff
Patch (12.59 KB, patch)
2017-07-07 08:55 PDT, Ali Juma
no flags Details | Formatted Diff | Diff
Patch (13.56 KB, patch)
2017-07-10 08:50 PDT, Ali Juma
no flags Details | Formatted Diff | Diff
Patch for landing (13.48 KB, patch)
2017-07-10 13:54 PDT, Ali Juma
no flags Details | Formatted Diff | Diff
Patch for landing (13.52 KB, patch)
2017-07-10 14:07 PDT, Ali Juma
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Simon Fraser (smfr) 2017-05-11 22:14:59 PDT
We should fix document.elementFromPoint() to consider x and y to be in the same coordinates as getBoundingClientRect() (bug 171113), and event.clientX/clientY.
Comment 1 Simon Fraser (smfr) 2017-05-11 22:17:22 PDT
document.elementFromPoint() ignores elements outside the "viewport", but should we climb based on the layout viewport, or the visual viewport?

I always thought this clipping behavior was arbitrary and interfere with genuine use cases; not sure if it's a security policy or not.
Comment 2 Simon Fraser (smfr) 2017-05-12 13:58:07 PDT
Need to fix this to fix the imported/w3c/web-platform-tests/cssom-view/elementFromPoint.html test.
Comment 3 Radar WebKit Bug Importer 2017-05-13 11:27:26 PDT
<rdar://problem/32178867>
Comment 4 Ali Juma 2017-06-26 11:59:25 PDT
Created attachment 313860 [details]
WIP

Still need to add tests and sort out failures.
Comment 5 Ali Juma 2017-07-04 16:28:00 PDT
Created attachment 314585 [details]
Patch
Comment 6 Simon Fraser (smfr) 2017-07-05 11:31:07 PDT
Comment on attachment 314585 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=314585&action=review

> Source/WebCore/dom/TreeScope.cpp:307
> +        documentScope().updateLayout();

Why is this documentScope().updateLayout(); only in the visual viewport code path?

> Source/WebCore/dom/TreeScope.cpp:308
> +        FloatPoint layoutViewportPoint = view->clientToLayoutViewportPoint(clientPoint);

clientPoint should already be in layout viewport coordinates from the caller.

> Source/WebCore/page/FrameView.h:480
> +    FloatRect layoutViewportToAbsoluteRect(FloatRect) const;
> +    FloatPoint layoutViewportToAbsolutePoint(FloatPoint) const;
> +
> +    FloatPoint clientToLayoutViewportPoint(FloatPoint) const;

I don't think we should introduce these new conversion functions. "layoutViewport" coordinates should just be client coordinates when visual viewports are enabled, and callers can already convert to them via documentToClientRect etc.

> Source/WebCore/rendering/RenderLayer.cpp:4934
> +            hitTestArea.intersect(absoluteLayoutViewportRect);
> +        } else
> +            hitTestArea.intersect(renderer().view().frameView().visibleContentRect(LegacyIOSDocumentVisibleRect));

Neither of these make much sense for the case when visualOverflowRect() was called; that's not in absolute coordinates.
Comment 7 Ali Juma 2017-07-05 12:41:12 PDT
Comment on attachment 314585 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=314585&action=review

>> Source/WebCore/dom/TreeScope.cpp:307
>> +        documentScope().updateLayout();
> 
> Why is this documentScope().updateLayout(); only in the visual viewport code path?

This is because of the call to view->layoutViewportRect() below. We need the layout viewport updated before that. In the existing code path, we don't update layout until RenderView::hitTest.

>> Source/WebCore/dom/TreeScope.cpp:308
>> +        FloatPoint layoutViewportPoint = view->clientToLayoutViewportPoint(clientPoint);
> 
> clientPoint should already be in layout viewport coordinates from the caller.

The layout viewport incorporates page zoom, so this conversion is needed in order to treat the input point as being in the same space as the rect output by getBoundingClientRect(). For example, with an 800x600 window and a page zoom of 2, the layout viewport will still be 800x600, but an element positioned at the bottom right of the window (and at the bottom right of the layout viewport) will have a bounding client rect whose bottom right corner is (400, 300).

>> Source/WebCore/rendering/RenderLayer.cpp:4934
>> +            hitTestArea.intersect(renderer().view().frameView().visibleContentRect(LegacyIOSDocumentVisibleRect));
> 
> Neither of these make much sense for the case when visualOverflowRect() was called; that's not in absolute coordinates.

I'll look into this some more to figure out how hitTestArea is being used in the RenderFlowThread case.
Comment 8 Simon Fraser (smfr) 2017-07-05 12:48:03 PDT
(In reply to Ali Juma from comment #7)
> Comment on attachment 314585 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=314585&action=review
> 
> >> Source/WebCore/dom/TreeScope.cpp:307
> >> +        documentScope().updateLayout();
> > 
> > Why is this documentScope().updateLayout(); only in the visual viewport code path?
> 
> This is because of the call to view->layoutViewportRect() below. We need the
> layout viewport updated before that. In the existing code path, we don't
> update layout until RenderView::hitTest.

OK.


> >> Source/WebCore/dom/TreeScope.cpp:308
> >> +        FloatPoint layoutViewportPoint = view->clientToLayoutViewportPoint(clientPoint);
> > 
> > clientPoint should already be in layout viewport coordinates from the caller.
> 
> The layout viewport incorporates page zoom, so this conversion is needed in
> order to treat the input point as being in the same space as the rect output
> by getBoundingClientRect().

Ah right. Long term, I'd like all "document" coordinates to be independent of page zoom (see recently-added FIXME in FrameView.h), so ideally visual/layoutViewportRects would not change with page zoom, but that's too invasive to do now. I was going to ask you if you'd tested with page zoom!

> >> Source/WebCore/rendering/RenderLayer.cpp:4934
> >> +            hitTestArea.intersect(renderer().view().frameView().visibleContentRect(LegacyIOSDocumentVisibleRect));
> > 
> > Neither of these make much sense for the case when visualOverflowRect() was called; that's not in absolute coordinates.
> 
> I'll look into this some more to figure out how hitTestArea is being used in
> the RenderFlowThread case.
Comment 9 Ali Juma 2017-07-07 08:55:47 PDT
Created attachment 314850 [details]
Patch
Comment 10 Ali Juma 2017-07-07 10:26:59 PDT
Comment on attachment 314585 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=314585&action=review

>>>> Source/WebCore/rendering/RenderLayer.cpp:4934
>>>> +            hitTestArea.intersect(renderer().view().frameView().visibleContentRect(LegacyIOSDocumentVisibleRect));
>>> 
>>> Neither of these make much sense for the case when visualOverflowRect() was called; that's not in absolute coordinates.
>> 
>> I'll look into this some more to figure out how hitTestArea is being used in the RenderFlowThread case.
> 
> 

It turns out that the visualOverflowRect() case never happens, since RenderLayer::hitTest is never called on a RenderFlowThread. RenderLayer::hitTest is the top-level entry point to hit-testing (RenderLayer::hitTestLayer is what gets called on each layer). It's mostly called on RenderViews, though from accessibility code it's possible to call it on the currently focused Element's RenderObject's layer. But RenderFlowThreads are never directly owned by Elements.
Comment 11 Simon Fraser (smfr) 2017-07-07 18:07:02 PDT
Comment on attachment 314850 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=314850&action=review

See the "Coordinate systems:" comment in FrameView.h, and enhance it to add "layoutViewport" coordinates.

> Source/WebCore/page/FrameView.cpp:4930
> +    ASSERT(frame().settings().visualViewportEnabled());
> +    rect.scale(frame().frameScaleFactor());
> +    return rect;

This seems wrong. layoutViewport -> absolute should be accounting for both the layout viewport origin, and the frame scale.

> Source/WebCore/page/FrameView.cpp:4936
> +    p.scale(frame().frameScaleFactor());

Ditto.

> Source/WebCore/page/FrameView.cpp:4944
> +    ASSERT(frame().settings().visualViewportEnabled());
> +    p.scale(frame().pageZoomFactor());
> +    p.moveBy(layoutViewportRect().location());

client to layout viewport should just be about pageZoom.

> Source/WebCore/page/FrameView.h:480
> +    FloatRect layoutViewportToAbsoluteRect(FloatRect) const;
> +    FloatPoint layoutViewportToAbsolutePoint(FloatPoint) const;
> +
> +    FloatPoint clientToLayoutViewportPoint(FloatPoint) const;

Please add a comment saying that "layoutViewport" coordinate differ from client coordinates because of page zoom.
Comment 12 Ali Juma 2017-07-10 08:50:25 PDT
Created attachment 314984 [details]
Patch

Addressed comments.
Comment 13 Simon Fraser (smfr) 2017-07-10 11:50:13 PDT
Comment on attachment 314984 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=314984&action=review

> Source/WebCore/dom/TreeScope.cpp:309
> +        FloatRect layoutViewportBounds(FloatPoint(), view->layoutViewportRect().size());

FloatPoint() can be { }

> Source/WebCore/page/FrameView.cpp:4939
> +    p.scale(frame().frameScaleFactor());
> +    return p;

This can use p.scaled()

> Source/WebCore/page/FrameView.cpp:4946
> +    p.scale(frame().pageZoomFactor());
> +    return p;

This can use p.scaled()

> Source/WebCore/page/FrameView.h:455
> +    //    Relative to the layout viewport rect, affected by page zoom but not by page scale. Affected by scroll origin.

I would say "Similar to client coordinates, but affected by page zoom (but not page scale)." I think the "Affected by scroll origin" adds confusion, so remove it.

> Source/WebCore/rendering/RenderLayer.cpp:4931
> +            const FrameView& frameView = renderer().view().frameView();

auto& frameView =

> Source/WebCore/rendering/RenderLayer.cpp:4932
> +            LayoutRect layoutViewportBounds(LayoutPoint(), frameView.layoutViewportRect().size());

LayoutPoint() -> { }
Comment 14 Ali Juma 2017-07-10 13:54:10 PDT
Created attachment 315019 [details]
Patch for landing
Comment 15 WebKit Commit Bot 2017-07-10 14:04:04 PDT
Comment on attachment 315019 [details]
Patch for landing

Rejecting attachment 315019 [details] from commit-queue.

Failed to run "['/Volumes/Data/EWS/WebKit/Tools/Scripts/webkit-patch', '--status-host=webkit-queues.webkit.org', '--bot-id=webkit-cq-02', 'validate-changelog', '--check-oops', '--non-interactive', 315019, '--port=mac']" exit_code: 1 cwd: /Volumes/Data/EWS/WebKit

ChangeLog entry in LayoutTests/ChangeLog contains OOPS!.

Full output: http://webkit-queues.webkit.org/results/4094583
Comment 16 Ali Juma 2017-07-10 14:07:06 PDT
Created attachment 315022 [details]
Patch for landing
Comment 17 Ali Juma 2017-07-10 14:10:24 PDT
Sorry about the previous patch, I was expecting the commit queue to fix up the OOPS lines. The newly-uploaded patch should be good to go.
Comment 18 WebKit Commit Bot 2017-07-11 11:01:19 PDT
Comment on attachment 315022 [details]
Patch for landing

Clearing flags on attachment: 315022

Committed r219342: <http://trac.webkit.org/changeset/219342>
Comment 19 WebKit Commit Bot 2017-07-11 11:01:21 PDT
All reviewed patches have been landed.  Closing bug.