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] PointerBinding per view #48248

Merged
merged 8 commits into from
Dec 11, 2023
Merged

Conversation

mdebbar
Copy link
Contributor

@mdebbar mdebbar commented Nov 20, 2023

  • Move initialization of PointerBinding/KeyboardBinding out of FlutterViewEmbedder.
  • computeEventOffsetToTarget properly handles events within a given view.
  • PointerBinding operates on the given Flutter view (it still listens to some domWindow events for the implicit view).
    • Stop using globals e.g. ui.window, KeyboardBinding.instance, SafariPointerEventWorkaround.instance, etc.
  • pointer_binding_test.dart doesn't use globals either.
  • clickDebouncer is now a static property on PointerBinding.

Fixes flutter/flutter#137289

@github-actions github-actions bot added the platform-web Code specifically for the web engine label Nov 20, 2023
@mdebbar mdebbar marked this pull request as ready for review November 29, 2023 22:25
DomWheelEvent? _lastWheelEvent;
bool _lastWheelEventWasTrackpad = false;

DomElement get _viewTarget => _view.dom.rootElement;
DomEventTarget get _globalTarget {
// When the Flutter app owns the full page, we want to listen on window for
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't returning thew view target implictily covering this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope. The root element is always a <flutter-view> element that we insert somewhere. But in the implicitView case, we want this to return window.

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.

Looks good! I had a bunch of comments here and there, but I don't think they're blocking! (I hope! :P)

if (isIosSafari) {
SafariPointerEventWorkaround.instance.workAroundMissingPointerEvents();
_safariWorkaround = safariWorkaround ?? SafariPointerEventWorkaround();
Copy link
Member

Choose a reason for hiding this comment

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

Looks like this Safari workaround could be a singleton? It only needs to be applied once into the document, and not on every view, right?

(The only thing it's doing is adding an empty listener to document.ontouchstart it seems?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made the "default" workaround instance a static member on PointerBinding (so we only ever create one). And the instance only attaches the event listener once (even it gets called multiple times).

Comment on lines -72 to -73
KeyboardBinding.initInstance();
PointerBinding.initInstance(
Copy link
Member

Choose a reason for hiding this comment

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

So little stuff remaining in this reset function!!

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 can't wait to get rid of it!

Comment on lines 111 to 113
/// Resets global pointer state that's not tied to any single [PointerBinding]
/// instance.
static void resetGlobalState() {
Copy link
Member

Choose a reason for hiding this comment

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

I cannot find any prod code calling this, so I'm assuming it's only for tests?

Can this be called debugResetGlobalState (or indicate that this is for tests in any other way, with @visibleForTesting or documentation?)

Comment on lines 500 to 502
if (_view == EnginePlatformDispatcher.instance.implicitView) {
return domWindow;
}
Copy link
Member

Choose a reason for hiding this comment

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

Maybe have an eventsTarget getter on the view that returns the proper object? The embedding strategy for "full page" already has a bunch of domDocument hardcoded in certain places, it could return a domWindow very easily.

Also: do you think we could migrate the "embedded" mode to use the rootElement as its target in this PR as well? That way we'd only have two cases:

  • embedded element -> rootElement
  • whole window -> domWindow

Rather than distinguishing them by being an "implicitView" or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made the change but I'm not sure if I captured exactly what you wanted. Please see 1de6ab9

Comment on lines 978 to +979
// Why `domWindow` you ask? See this fiddle: https://jsfiddle.net/ditman/7towxaqp
_addPointerEventListener(domWindow, 'pointermove', (DomPointerEvent event) {
_addPointerEventListener(_globalTarget, 'pointermove', (DomPointerEvent event) {
Copy link
Member

Choose a reason for hiding this comment

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

(Rant) I wonder if the solution to all this "listening to global events craze" is to have:

  • A per-view listener that is targeted to the view itself for normal mouse moves
  • A global listener on window that we can tap from as needed in special cases (when the mouse leaves the window, etc...)

That way we might simplify the general case of pointer handling :/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do need to figure this out at some point, yes. For example, do we care about hover events happening outside flutter views? What if the pointer is down and moves outside of the view? etc.

Comment on lines +30 to +33
assert(() {
registerHotRestartListener(reset);
return true;
}());
Copy link
Member

Choose a reason for hiding this comment

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

Are we landing in the following pattern:

  • reset for hot restart + tests
  • dispose for actual code paths that could happen at runtime?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The way I think about these is reset means the instance is back to its original state and can be used further. OTOH, dispose means the object cleaned after itself and can't be used anymore.

assert(_pointers.containsKey(device));
final _PointerState state = _pointers[device]!;
assert(globalPointerState.pointers.containsKey(device));
final _PointerDeviceState state = globalPointerState.pointers[device]!;
Copy link
Member

Choose a reason for hiding this comment

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

I don't mind globalPointerState being a global, but it could be modeled as a property of a PointerDevice object and do:

_PointerDeviceState state = pointerDevices[deviceId].state!;

But since we don't care about anything else of the pointerDevice -> ship it like this!

Comment on lines +32 to +33
// Remove <body> margins to avoid messing up with all the test coordinates.
domDocument.body!.style.margin = '0';
Copy link
Member

Choose a reason for hiding this comment

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

I guess we didn't need this before because the (full-screen) implicitView has this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Exactly! The full page embedding strategy was doing this for us.

@mdebbar mdebbar added the autosubmit Merge PR when tree becomes green via auto submit App label Dec 11, 2023
@auto-submit auto-submit bot merged commit 4c30919 into flutter:main Dec 11, 2023
26 checks passed
@mdebbar mdebbar deleted the pointer_binding_view branch December 11, 2023 20:54
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 11, 2023
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Dec 11, 2023
…139936)

flutter/engine@5587d26...4c30919

2023-12-11 mdebbar@google.com [web] PointerBinding per view (flutter/engine#48248)

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 chinmaygarde@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
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 will affect goldens
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[web] Pointer events support multi-view
3 participants