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

add pointer data santizing in flutter web engine #14082

Merged
merged 5 commits into from
Dec 4, 2019

Conversation

chunhtai
Copy link
Contributor

@chunhtai chunhtai commented Dec 2, 2019

Framework moved the pointer data sanitization and metrics-related calculation to engine side.
It is now engine's responsibility to provide those metrics and clean pointer data stream.

This pr implements those calculation and sanitization in flutter web engine.
Fixes flutter/flutter#45510

@auto-assign auto-assign bot requested a review from flar December 2, 2019 23:12
expect(packets[0].data[1].physicalY, equals(10.0));
expect(packets[0].data[1].physicalDeltaX, equals(0.0));
expect(packets[0].data[1].physicalDeltaY, equals(0.0));
// Scroll event will fire twice
Copy link
Contributor Author

@chunhtai chunhtai Dec 3, 2019

Choose a reason for hiding this comment

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

I am seeing this weird behavior which I am not sure it is by design or not. glassPane.dispatchEvent(html.WheelEvent(..)) will actually fire two scroll event back to back to glassPaneElement listener

Copy link
Contributor

Choose a reason for hiding this comment

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

/cc @mdebbar

Hmm, could we possibly have the pointer binding double-subscribed on the glasspane?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems like the some tests do not clear the subscription in between test. It will fire more event if i ran the entire test suite

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes, if it's over-firing in tests, then some tests are probably not cleaning up after themselves.

@@ -15,23 +15,17 @@ class PointerBinding {
static PointerBinding get instance => _instance;
static PointerBinding _instance;

// Set of pointerIds that are added before routing hover and mouse wheel
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 no longer need to guess whether the framework will synthesize an add or not, because it won't.

@@ -490,34 +470,6 @@ Duration _eventTimeStampToDuration(num milliseconds) {
return Duration(milliseconds: ms, microseconds: micro);
}

void _ensureMouseDeviceAdded(List<ui.PointerData> data, double clientX,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

synthesizing logic is moved to pointer_converter.dart

@yjbanov
Copy link
Contributor

yjbanov commented Dec 3, 2019

@chunhtai

It is not engine's responsibility

Did you mean "It is now"?

Copy link
Contributor

@yjbanov yjbanov left a comment

Choose a reason for hiding this comment

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

@mdebbar should definitely review this. I'll have a second look at it tomorrow.

lib/web_ui/lib/src/engine/pointer_binding.dart Outdated Show resolved Hide resolved
expect(packets[0].data[1].physicalY, equals(10.0));
expect(packets[0].data[1].physicalDeltaX, equals(0.0));
expect(packets[0].data[1].physicalDeltaY, equals(0.0));
// Scroll event will fire twice
Copy link
Contributor

Choose a reason for hiding this comment

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

/cc @mdebbar

Hmm, could we possibly have the pointer binding double-subscribed on the glasspane?

@yjbanov yjbanov requested a review from mdebbar December 3, 2019 04:48
@mdebbar mdebbar added the platform-web Code specifically for the web engine label Dec 3, 2019
class _PointerState {
_PointerState(this.x, this.y);

int get pointer => _pointer; // The identifier used in framework hit test.
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be a dartdoc comment.

_PointerState _ensureStateForPointer(ui.PointerData datum, double x, double y) {
return _pointers.putIfAbsent(
datum.device,
() => _PointerState(x, y),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: indentation is off.


/// Convert the given list pointer data into a sequence of framework-compatible
/// pointer data.
Iterable<ui.PointerData> convert(List<ui.PointerData> data) sync* {
Copy link
Contributor

Choose a reason for hiding this comment

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

I understand that this is how the Flutter framework used to implement this. But on the web, sync* is expensive and we try to avoid it. Are there any downsides if we change this method to mutate the data list in-place instead of generating a new iterable? (we might also rename the method to make it clear that it mutates the list in-place).

Copy link
Member

Choose a reason for hiding this comment

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

sync* is expensive everywhere, if we're doing it on dart:ui it should be removed

yield _synthesizeFrom(datum, ui.PointerChange.add);
yield _synthesizeFrom(datum, ui.PointerChange.down);
}
assert(state.down);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm probably missing something here, but it seems like this assertion always fails if alreadyAdded is false. We could do one of two things:

  1. Do state.down = true; when we synthesize PointerChange.down above.
  2. Disallow PointerChange.move if the pointer hasn't already been added.

I'm in favor of option 2, but I'm open for discussing it further.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh I missing a state.down=true. I added this part when i was confused at #14082 (comment)
I should remove this synthesizing all together

@tvolkert
Copy link
Contributor

tvolkert commented Dec 3, 2019

Is this a fix for flutter/flutter#45510?

@chunhtai
Copy link
Contributor Author

chunhtai commented Dec 3, 2019

@tvolkert yes

}

List<ui.PointerData> _convertWheelEventToPointerData(
html.WheelEvent event,
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 moved all these helper function to baseadapter abstract class

void _addWheelEventListener(void listener(html.WheelEvent e)) {
final dynamic eventOptions = js_util.newObject();
js_util.setProperty(eventOptions, 'passive', false);
js_util.callMethod(PointerBinding.instance.domRenderer.glassPaneElement,
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 a bit confusing to me. If we will call preventDefault, why don't we just use capture = true, and then we can just use the more generic function BaseAdapter. _addEventListener?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand.

The browser by default sets passive=true on scroll and wheel event listeners. So we need to explicitly set it to passive=false to be able to call event.preventDefault(). This is independent from capture.

cc @ferhatb who implemented wheel events.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we use usecapture= true, we don't have to call event.preventDefault()?

Copy link
Contributor

Choose a reason for hiding this comment

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

Calling event.preventDefault() is necessary regardless of the value of useCapture.

useCapture controls when your listener gets called. If it's true, your listener is called before your children. If it's false, your children are called before your listener.

event.preventDefault tells the browser to not perform the default behavior for that event (which in this case is scrolling). We want Flutter to handle the scrolling, so we prevent the browser from doing any kind of scrolling.

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 just check we can still call preventdefault if we have usecapture = true, and from the api documentation of usecapture I can't see why we dont want to use it.

The reason why I want to move away from js_util.callMethod is that i am having trouble remove the listener afterward.

I updated it so that add wheel listener goes through BaseAdapter._addEventListener as well. Let me know if I missed something.

Copy link
Contributor

Choose a reason for hiding this comment

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

offline discussion: we have to use js interop to remove the wheel event listener (js_util.callMethod(glassPane, 'removeEventListener', ...)).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

lib/web_ui/lib/src/engine/pointer_binding.dart Outdated Show resolved Hide resolved
@chunhtai chunhtai removed the Work in progress (WIP) Not ready (yet) for review! label Dec 3, 2019
@chunhtai
Copy link
Contributor Author

chunhtai commented Dec 3, 2019

@mdebbar this is ready for re-review

Copy link
Contributor

@yjbanov yjbanov left a comment

Choose a reason for hiding this comment

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

LGTM if LGT @mdebbar

Copy link
Contributor

@mdebbar mdebbar left a comment

Choose a reason for hiding this comment

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

Great tests! Thanks for fixing this!

image

@chunhtai chunhtai merged commit 3e6d6bc into flutter:master Dec 4, 2019
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 4, 2019
engine-flutter-autoroll added a commit to flutter/flutter that referenced this pull request Dec 4, 2019
…ine#14082) (#46049)

git@github.com:flutter/engine.git/compare/90e28c027c0b...3e6d6bc

git log 90e28c0..3e6d6bc --first-parent --oneline
2019-12-04 47866232+chunhtai@users.noreply.github.com add pointer data santizing in flutter web engine (#14082)


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 on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
@Ahmadre
Copy link

Ahmadre commented Dec 4, 2019

Great! Thank you soo much @chunhtai ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes platform-web Code specifically for the web engine
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[web]: Gestures (Pan/Drag, etc.) not working anymore
7 participants