New issue
Advanced search Search tips

Issue 848050 link

Starred by 3 users

Issue metadata

Status: Fixed
Owner:
Closed: Dec 13
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Linux , Android , Windows , Chrome
Pri: 2
Type: Bug

Blocking:
issue 810264



Sign in to add a comment

Preserve input event ack ordering with OOPIFs

Project Member Reported by mcnee@chromium.org, May 30 2018

Issue description

Chrome Version: 69.0.3446.0

With OOPIFs, we target input events to the appropriate renderer to be handled. For some types of input events (notably touch events), when the ack is received from a child renderer, the ack is simply passed to the root RenderWidgetHostView for gesture processing (see CrossProcessFrameConnector::ForwardProcessAckedTouchEvent).

This is an issue when multiple input event sequences are sent to separate renderers within a short time, as the input events may be acked out of order.

For example, say an event sequence is sent to a child, and then a subsequent sequence is sent to the root. If the child is slow to process the events, the acks of the second sequence could be processed before the end of the first. In the case of touch events, this confuses the gesture recognizer causing it to DCHECK/stall.
 

Comment 1 by mcnee@chromium.org, May 30 2018

Blocking: 810264
Cc: dtapu...@chromium.org
So it seems to me there are (at least) a couple of ways to attack this. I suspect Option#1 is simpler/faster to implement.

I'd appreciate any feedback about the relative merits of the approaches, and other ideas that people might have.

Option #1 (least invasive change):

- institute a touch-ack queue in RenderWidgetHostInputEventRouter
  - the queue keeps track of the target view for each unique_touch_event_id
- nothing is acked to the GestureProvider without checking it off, in order, in this queue (so all acks go through RWHIER)
  - in the case where one touch sequence goes to an unresponsive frame, and we start receiving acks from a subsequent touch sequence, we store these acks in the queue but don't immediately forward them to the GestureProvider, as they would confuse it
    + we will wait some maximum amount of time for the unresponsive renderer, but after that we'll send forced acks for everything in the unresponsive sequence (unconsumed? no_consumer?), and then we can process acks that have built up from subsequent sequences from other views
    + if we later receive acks from the unresponsive frame, we will discard them
  - if a renderer goes away without acking touch events, we can detect this and send the necessary acks to the gesture provider (no_consumer in this case)
  - they can be flagged while still in the queue if it's not their turn to be acked yet

This option then mostly involves a queue that keeps track of the various outbound touch events and their targets, and what the most recently seen touch_id is. The 'timeout'mechanism for unresponsive events is perhaps the hardest part of this.

Option#2 (more plumbing, less bookeeping):

- assign a GestureProvider to each cross-process frame
  - then RenderWidgetHostView{Aura|Mac|ChildFrame|etc} always sends acks to its own GestureProvider, and they're always in order for that GestureProvider
  - RenderWidgetHostViewChildFrame, in particular, would no longer blindly forward touch event acks to the root view

- We would need to be careful to correctly wire the call to GestureProvider::OnTouchEvent() to happen *after* the event has been routed so it goes to the correct GestureProvider
(https://cs.chromium.org/chromium/src/ui/events/gesture_detection/filtered_gesture_provider.cc?rcl=e75e328a026878f90373d4055f8a582da0f9e7e0&l=28)
  - at present, this happens under the assumption of a single gesture provider attached to the root view
 - this approach may also involve other event types that the GestureProvider is interested in

Comment 4 by mcnee@chromium.org, Jun 1 2018

Note that this ordering issue will also apply to touchpad pinch. The concept behind option 1 can be easily extended to touchpad pinch, but not option 2.

Also, option 2 seems like it's deferring the reordering issue to the generated gesture events. If a GestureProvider in the root produces a pinch gesture from touches in the root and a GestureProvider in the child produces a pinch gesture from touches in the child, we'll have to combine these into a valid event sequence.

For option 1, is the timeout for acks from a child necessary? Unlike the case of the renderer going away, the timeout seems like an optimization. If the child renderer is slow to ack, it would have presumably been slow to ack in the single renderer case as well.

Also, what if the slow child does eventually preventDefault to implement some custom behaviour? Perhaps having a timeout where the slow acks are considered consumed would allow us to unblock the queue while still respecting preventDefault.

Comment 5 by d...@tapuska.com, Jun 1 2018

Pass through touch event queue already preserves touch events ack order. I guess the problem is that it per render widget. We could also be using callbacks to the gesture controller. Not sure if that would help. But old chrome ipc is gone so now we can bind what ever data we want with a call back.  
Thanks for the feedback.

I guess in option 2 I was assuming that things like touch-sequences would generate gestures that only go back to the same renderer, so overlap isn't an issue, *except* in the case of pinch, though I'm sure that can be managed. But as mentioned earlier, this is part of of what I suspect makes the Option #2 more complicated.

Regarding the timeout for acks, I partly included that because I know kenrb@ already has something like that in the targetting logic, and there are obvious advantages to not allowing un unresponsive child frame to impede performance on the main frame (or other frames for that matter). So yes, it may be an optimization, but I suspect it's a worthwhile one.

In your last example, I'm not entirely sure how that would play out, but at some point it's perhaps fair to 'give up' on an unresponsive page. Definitely though we want to unblock the queue without undue delay.

So far option #1 seems preferable, even if there are some details to figure out.
Re C#5, yes, the issue is that current ordering guarantees are per render widget.

Can you elaborate on the callbacks? Presumably they would have the same issues around asynchrony, so the Gesture controller would have to manage that.

Which leads me to an option #3 of sorts, in which the GestureProvider/Recognizer internally keeps separate state for each sequence. Then as touches are acked, the gesture provider can handle them so long as they're in-order per sequence. But I know less about the architecture of the GestureProvider than I do about RWHIER, so my ideas have been more focused on the latter.
Cc: -wjmaclean@chromium.org
Owner: wjmaclean@chromium.org
Status: Started (was: Available)
Project Member

Comment 9 by bugdroid1@chromium.org, Aug 22

The following revision refers to this bug:
  https://chromium.googlesource.com/chromium/src.git/+/926835bcfa1e7a584475f3e2f4bff8cb089140cc

commit 926835bcfa1e7a584475f3e2f4bff8cb089140cc
Author: W. James MacLean <wjmaclean@chromium.org>
Date: Wed Aug 22 18:33:14 2018

Refactor touch event acks to go through RenderWidgetHostInputEventRouter.

This is a refactoring CL to pave the way for a TouchEventAckQueue
implementation that will allow us to ensure the ordering of acks
when multiple renderers are present, i.e. OOPIF subframes exist.

Bug:  848050 
Change-Id: Ib446486ac6521c66cbfe3f552355e597107070db
Reviewed-on: https://chromium-review.googlesource.com/1181765
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Commit-Queue: James MacLean <wjmaclean@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585178}
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/frame_host/cross_process_frame_connector.cc
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/frame_host/cross_process_frame_connector.h
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/frame_host/render_widget_host_view_guest.cc
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/frame_host/render_widget_host_view_guest.h
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/renderer_host/frame_connector_delegate.h
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/renderer_host/render_widget_host_impl.cc
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/renderer_host/render_widget_host_input_event_router.cc
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/renderer_host/render_widget_host_input_event_router.h
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/renderer_host/render_widget_host_view_base.cc
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/renderer_host/render_widget_host_view_base.h
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/renderer_host/render_widget_host_view_child_frame.cc
[modify] https://crrev.com/926835bcfa1e7a584475f3e2f4bff8cb089140cc/content/browser/renderer_host/render_widget_host_view_child_frame.h

Project Member

Comment 10 by bugdroid1@chromium.org, Sep 12

The following revision refers to this bug:
  https://chromium.googlesource.com/chromium/src.git/+/626dce8e9568cf276416d0fcd1216abb1ca5d5eb

commit 626dce8e9568cf276416d0fcd1216abb1ca5d5eb
Author: W. James MacLean <wjmaclean@chromium.org>
Date: Wed Sep 12 16:01:22 2018

Implement TouchEventAckQueue in RenderWidgetHostInputEventRouter.

This CL introduces a queue to track outgoing TouchEvents, and
track when they are acked. At present that's all it does, but in a
subsequent CL this queue will be used by RWHIER for invoking
ProcessAckedTouchEvent() on the root RenderWidgetHostView, and
making sure the TouchEvents are acked in-order with respect to their
unique_touch_event_id values.

This CL also introduces UMA metrics to track renderer performance
by noting when the queue length grows beyond a preset size. In future
metrics will be added to detect (i) acking touch events due to a
renderer going away, and (ii) when acks are forced due to
non-responsiveness of a nominally 'alive' renderer.

Bug:  848050 
Change-Id: I976e41082f3859d77184ce7371ff7c7dba265782
Reviewed-on: https://chromium-review.googlesource.com/1188436
Commit-Queue: James MacLean <wjmaclean@chromium.org>
Reviewed-by: Ilya Sherman <isherman@chromium.org>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590705}
[modify] https://crrev.com/626dce8e9568cf276416d0fcd1216abb1ca5d5eb/content/browser/renderer_host/input/compositor_event_ack_browsertest.cc
[modify] https://crrev.com/626dce8e9568cf276416d0fcd1216abb1ca5d5eb/content/browser/renderer_host/input/main_thread_event_queue_browsertest.cc
[modify] https://crrev.com/626dce8e9568cf276416d0fcd1216abb1ca5d5eb/content/browser/renderer_host/input/touch_input_browsertest.cc
[modify] https://crrev.com/626dce8e9568cf276416d0fcd1216abb1ca5d5eb/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc
[modify] https://crrev.com/626dce8e9568cf276416d0fcd1216abb1ca5d5eb/content/browser/renderer_host/render_widget_host_input_event_router.cc
[modify] https://crrev.com/626dce8e9568cf276416d0fcd1216abb1ca5d5eb/content/browser/renderer_host/render_widget_host_input_event_router.h
[modify] https://crrev.com/626dce8e9568cf276416d0fcd1216abb1ca5d5eb/tools/metrics/histograms/histograms.xml

Project Member

Comment 11 by bugdroid1@chromium.org, Dec 12

The following revision refers to this bug:
  https://chromium.googlesource.com/chromium/src.git/+/ad0bd35f93a41cc30cd7741ea842cf84c7e39821

commit ad0bd35f93a41cc30cd7741ea842cf84c7e39821
Author: W. James MacLean <wjmaclean@chromium.org>
Date: Wed Dec 12 22:10:05 2018

Shift TouchEventAcking to TEAQ

This CL makes TouchEventAckQueue responsible for calling
ProcessAckedTouchEvents() on the root view. Several main changes:

1) The TEAQ now stores the full TouchEvent, not just the event's
touch ID. This is required by ProcessAckedTouchEvents().

2) With the assumed invariant that now all touch events are
acked, and in-order, we don't need to explicitly mark emulated
touch events as such, since we can use the TouchEmulator to keep
track for us.

3) The TEAQ now holds a pointer to its owning RWHIER, since it
needs access to both (i) the TouchEmulator, and (ii) the function
IsViewInMap(). The latter means having to make IsViewInMap() a
public function on RWHIER.

Bug:  848050 
Change-Id: I822424ff1d04b8ba0c67a88956b1517c38a877ec
Reviewed-on: https://chromium-review.googlesource.com/c/1355243
Commit-Queue: James MacLean <wjmaclean@chromium.org>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#616071}
[modify] https://crrev.com/ad0bd35f93a41cc30cd7741ea842cf84c7e39821/content/browser/renderer_host/render_widget_host_input_event_router.cc
[modify] https://crrev.com/ad0bd35f93a41cc30cd7741ea842cf84c7e39821/content/browser/renderer_host/render_widget_host_input_event_router.h
[modify] https://crrev.com/ad0bd35f93a41cc30cd7741ea842cf84c7e39821/content/browser/site_per_process_browsertest.cc

Status: Fixed (was: Started)

Sign in to add a comment