Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web] fix clicks on merged semantic nodes (attempt #2) #47360

Merged
merged 4 commits into from
Nov 8, 2023

Conversation

yjbanov
Copy link
Contributor

@yjbanov yjbanov commented Oct 26, 2023

This relands #43620 with a fix for nested tappable nodes. The first PR introduced this regression: flutter/flutter#134842.

This PR includes the original PR and a fix for the regression. The fix is to call stopPropagation on the "click" event so that it is not handled by the ancestor if the child has already decided to send a SemanticsAction.tap to the framework. This ensures that there cannot be more than one SemanticsAction.tap sent to the framework.

Fixes flutter/flutter#134842

@yjbanov yjbanov requested review from ditman and mdebbar October 26, 2023 22:11
@github-actions github-actions bot added the platform-web Code specifically for the web engine label Oct 26, 2023
Copy link
Member

@ditman ditman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Added a small note in case the EventTarget generating the click/tap events may have multiple listeners (not just bubble up)

Comment on lines +303 to +304
// https://github.com/flutter/flutter/issues/134842
click.stopPropagation();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way I've seen this handled (in jQuery, admittedly) is with stopImmediatePropagation which was a "flag for the framework" so other listeners attached to the same EventTarget wouldn't run (even without bubbling). (IIRC this was just a boolean flag written into the event object itself that other bits of the framework knew how to check).

We may need something like this in Flutter web, now that we're juggling potentially multiple sets of listeners into the same dom element? maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, TIL! Looks like it's a standard API, not just jQuery. In this case we don't have multiple listeners on the same element. All the elements in question are created by the engine and we are attaching just one click listener (unless there's a bug, but that's a different story). So we only need to stop propagation to ancestor elements. Do you think we should still do something like this in this case?

@yjbanov yjbanov force-pushed the a11y-click-debounce-2 branch from dc326f0 to 56c1246 Compare November 6, 2023 21:54
@yjbanov yjbanov added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 6, 2023
@auto-submit auto-submit bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 6, 2023
Copy link
Contributor

auto-submit bot commented Nov 6, 2023

auto label is removed for flutter/engine/47360, due to - The status or check suite Linux linux_web_engine has failed. Please fix the issues identified (or deflake) before re-applying this label.

@yjbanov yjbanov added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 7, 2023
@auto-submit auto-submit bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 7, 2023
Copy link
Contributor

auto-submit bot commented Nov 7, 2023

auto label is removed for flutter/engine/47360, due to - The status or check suite Linux linux_android_debug_engine has failed. Please fix the issues identified (or deflake) before re-applying this label.

@yjbanov yjbanov added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 8, 2023
@auto-submit auto-submit bot merged commit eb3b2bb into flutter:main Nov 8, 2023
24 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 8, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 8, 2023
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Nov 8, 2023
…138091)

flutter/engine@3e3be5e...117d47a

2023-11-08 skia-flutter-autoroll@skia.org Roll Skia from fce71a80b0a2 to a4cce5236dcf (1 revision) (flutter/engine#47807)
2023-11-08 chris@bracken.jp [macOS] Clean up resources in ViewController tests (flutter/engine#47792)
2023-11-08 skia-flutter-autoroll@skia.org Roll Skia from f3d250126ba9 to fce71a80b0a2 (1 revision) (flutter/engine#47796)
2023-11-08 skia-flutter-autoroll@skia.org Roll Skia from b4fa927468e6 to f3d250126ba9 (1 revision) (flutter/engine#47793)
2023-11-08 flar@google.com [Impeller] Add Rect::GetNormalizingTransform to handle UV coordinate conversion (flutter/engine#47775)
2023-11-08 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Remove Fuchsia Mac SDK from DEPS" (flutter/engine#47791)
2023-11-08 yjbanov@google.com [web] fix clicks on merged semantic nodes (attempt #2) (flutter/engine#47360)
2023-11-08 skia-flutter-autoroll@skia.org Roll Skia from 0f78e5f765d3 to b4fa927468e6 (1 revision) (flutter/engine#47788)
2023-11-08 skia-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from VcFEJiUUTYwkhEAlJ... to sD8HRA4JmXczujkqO... (flutter/engine#47785)
2023-11-08 jiahaog@users.noreply.github.com Fix narrowing conversion lint (flutter/engine#47740)
2023-11-08 chris@bracken.jp [macOS] Bail out of tests if engine not running (flutter/engine#47771)
2023-11-08 skia-flutter-autoroll@skia.org Roll Skia from f91d39395e85 to 0f78e5f765d3 (1 revision) (flutter/engine#47776)
2023-11-08 chris@bracken.jp [testing] Extract StreamCapture test utility (flutter/engine#47774)
2023-11-08 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Promote fuchsia build v2 to prod." (flutter/engine#47779)
2023-11-08 aam@google.com Include updated locations for dart third_party components into license ignore-list. (flutter/engine#47770)
2023-11-07 chillers@google.com Remove Fuchsia Mac SDK from DEPS (flutter/engine#47700)
2023-11-07 skia-flutter-autoroll@skia.org Roll Skia from 030e21befbc9 to f91d39395e85 (6 revisions) (flutter/engine#47769)
2023-11-07 chinmaygarde@google.com [Impeller] Support static thread safety analysis with condition variables. (flutter/engine#47763)
2023-11-07 godofredoc@google.com Promote fuchsia build v2 to prod. (flutter/engine#47729)
2023-11-07 737941+loic-sharma@users.noreply.github.com [Windows] Reduce warnings produced by unit tests (flutter/engine#47724)

Also rolling transitive DEPS:
  fuchsia/sdk/core/linux-amd64 from VcFEJiUUTYwk to sD8HRA4JmXcz

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC bdero@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Copy link
Contributor

@p-mazhnik p-mazhnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I revisited this PR after encountering issues with semantics on the web. Although it’s 8 months old, the code is still relevant, so I want to document here what I’ve found

Comment on lines +342 to +344
// The event landed on an non-tappable target. Assume this won't lead to
// double clicks and forward the event to the framework.
_sendToFramework(event, data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually leads to double clicks :)
Consider the following scenario:

<flt-semantics flt-tappable id="flt-semantic-node-1">
  <flt-semantics id="flt-semantic-node-2"> </flt-semantics>
</flt-semantics>

If pointer events are triggered on flt-semantic-node-2, we send them to framework in this condition (flt-tappable is not present), and isDebouncing will be false.
On the other hand, click event on the flt-semantic-node-2 propagates to flt-semantic-node-1 that has flt-tappable and is also sent to framework (isDebouncing is false).

(flutter/flutter#147050 (comment) and most likely issue flutter/flutter#150020)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I fixed one variant of this in #53694. Just using the event.target is probably naive. It should start with event.target and then walk up to the nearest tappable ancestor.

Comment on lines +266 to +269
void onClick(DomEvent click, int semanticsNodeId, bool isListening) {
assert(click.type == 'click');

if (!isDebouncing) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it is possible that isDebouncing is true but _state.target is not equal to click.target

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the problem you pointed out above (the sketchy target detection), it seems quite possible.

Comment on lines +324 to +334
// The 200ms duration was chosen empirically by testing tapping, mouse
// clicking, trackpad tapping and clicking, as well as the following
// screen readers: TalkBack on Android, VoiceOver on macOS, Narrator/
// NVDA/JAWS on Windows. 200ms seemed to hit the sweet spot by
// satisfying the following:
// * It was short enough that delaying the `pointerdown` still allowed
// drag gestures to begin reasonably soon (e.g. scrolling).
// * It was long enough to register taps and clicks.
// * It was successful at detecting taps generated by all tested
// screen readers.
timer: Timer(const Duration(milliseconds: 200), _onTimerExpired),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not working for long taps (issue flutter/flutter#147050).

In long tap case, it is possible that pointerup event is fired after 200 milliseconds since pointerdown, and as a result isDebounce will be false when click event is received, leading to duplication.

In the previous solution (that is still exists in parallel), timer was added after pointerup (and others) as well, so it is probably should fix the issue

const List<String> pointerEventTypes = <String>[
'pointerdown',
'pointermove',
'pointerleave',
'pointerup',
'pointercancel',
'touchstart',
'touchend',
'touchmove',
'touchcancel',
'mousedown',
'mousemove',
'mouseleave',
'mouseup',
];
if (pointerEventTypes.contains(event.type)) {
_temporarilyDisableBrowserGestureMode();
}

/// Disables browser gestures temporarily because pointer events were detected.
///
/// This is used to deduplicate gestures detected by Flutter and gestures
/// detected by the browser. Flutter-detected gestures have higher precedence.
void _temporarilyDisableBrowserGestureMode() {
const Duration kDebounceThreshold = Duration(milliseconds: 500);
_getGestureModeClock()!.datetime = _now().add(kDebounceThreshold);
if (_gestureMode != GestureMode.pointerEvents) {
_gestureMode = GestureMode.pointerEvents;
_notifyGestureModeListeners();
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct, and it's on my radar to fix. This method was enough for the screen reader case. However, we're trying to make it so semantics can be always enabled, and therefore it must also work for all interactions without the screen reader. This code needs a revision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App platform-web Code specifically for the web engine
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[a11y][web] Selecting menu items triggers a back navigation
3 participants