@@ -18,6 +18,7 @@ import 'pointer_binding/event_position_helper.dart';
1818import 'pointer_converter.dart' ;
1919import 'safe_browser_api.dart' ;
2020import 'semantics.dart' ;
21+ import 'window.dart' ;
2122
2223/// Set this flag to true to log all the browser events.
2324const bool _debugLogPointerEvents = false ;
@@ -73,21 +74,31 @@ int convertButtonToButtons(int button) {
7374/// Wrapping the Safari iOS workaround that adds a dummy event listener
7475/// More info about the issue and workaround: https://github.com/flutter/flutter/issues/70858
7576class SafariPointerEventWorkaround {
76- static SafariPointerEventWorkaround instance = SafariPointerEventWorkaround () ;
77+ DomEventListener ? _listener ;
7778
7879 void workAroundMissingPointerEvents () {
79- domDocument.addEventListener ('touchstart' , createDomEventListener ((DomEvent event) {}));
80+ _listener = createDomEventListener ((_) {});
81+ domDocument.addEventListener ('touchstart' , _listener);
82+ }
83+
84+ void dispose () {
85+ if (_listener != null ) {
86+ domDocument.removeEventListener ('touchstart' , _listener);
87+ }
8088 }
8189}
8290
8391class PointerBinding {
8492 PointerBinding (
85- this .flutterViewElement,
86- this ._keyboardConverter, [
87- this ._detector = const PointerSupportDetector (),
88- ]) : _pointerDataConverter = PointerDataConverter () {
93+ this .view,
94+ this ._keyboardConverter, {
95+ PointerSupportDetector detector = const PointerSupportDetector (),
96+ SafariPointerEventWorkaround ? safariWorkaround,
97+ }) : _pointerDataConverter = PointerDataConverter (),
98+ _detector = detector {
8999 if (isIosSafari) {
90- SafariPointerEventWorkaround .instance.workAroundMissingPointerEvents ();
100+ _safariWorkaround = safariWorkaround ?? SafariPointerEventWorkaround ();
101+ _safariWorkaround! .workAroundMissingPointerEvents ();
91102 }
92103 _adapter = _createAdapter ();
93104 }
@@ -96,9 +107,9 @@ class PointerBinding {
96107 static PointerBinding ? get instance => _instance;
97108 static PointerBinding ? _instance;
98109
99- static void initInstance (DomElement flutterViewElement , KeyboardConverter keyboardConverter) {
110+ static void initInstance (EngineFlutterView view , KeyboardConverter keyboardConverter) {
100111 if (_instance == null ) {
101- _instance = PointerBinding (flutterViewElement , keyboardConverter);
112+ _instance = PointerBinding (view , keyboardConverter);
102113 assert (() {
103114 registerHotRestartListener (_instance! .dispose);
104115 return true ;
@@ -108,37 +119,27 @@ class PointerBinding {
108119
109120 final ClickDebouncer clickDebouncer = ClickDebouncer ();
110121
122+ SafariPointerEventWorkaround ? _safariWorkaround;
123+
111124 /// Performs necessary clean up for PointerBinding including removing event listeners
112125 /// and clearing the existing pointer state
113126 void dispose () {
114- _adapter.clearListeners ();
115- _pointerDataConverter. clearPointerState ();
127+ _adapter.dispose ();
128+ _safariWorkaround ? . dispose ();
116129 clickDebouncer.reset ();
117130 }
118131
119- final DomElement flutterViewElement;
132+ final EngineFlutterView view;
133+ DomElement get rootElement => view.dom.rootElement;
120134
121135 final PointerSupportDetector _detector;
122136 final PointerDataConverter _pointerDataConverter;
123- KeyboardConverter _keyboardConverter;
137+ final KeyboardConverter _keyboardConverter;
124138 late _BaseAdapter _adapter;
125139
126- @visibleForTesting
127- void debugReset () {
128- _adapter.clearListeners ();
129- _adapter = _createAdapter ();
130- _pointerDataConverter.clearPointerState ();
131- }
132-
133- @visibleForTesting
134- void debugOverrideKeyboardConverter (KeyboardConverter keyboardConverter) {
135- _keyboardConverter = keyboardConverter;
136- debugReset ();
137- }
138-
139140 _BaseAdapter _createAdapter () {
140141 if (_detector.hasPointerEvents) {
141- return _PointerAdapter (clickDebouncer.onPointerData, flutterViewElement , _pointerDataConverter, _keyboardConverter);
142+ return _PointerAdapter (clickDebouncer.onPointerData, view , _pointerDataConverter, _keyboardConverter);
142143 }
143144 throw UnsupportedError (
144145 'This browser does not support pointer events which '
@@ -470,27 +471,29 @@ class _Listener {
470471abstract class _BaseAdapter {
471472 _BaseAdapter (
472473 this ._callback,
473- this .flutterViewElement ,
474+ this ._view ,
474475 this ._pointerDataConverter,
475476 this ._keyboardConverter,
476477 ) {
477478 setup ();
478479 }
479480
480481 final List <_Listener > _listeners = < _Listener > [];
481- final DomElement flutterViewElement ;
482+ final EngineFlutterView _view ;
482483 final _PointerDataCallback _callback;
483484 final PointerDataConverter _pointerDataConverter;
484485 final KeyboardConverter _keyboardConverter;
485486 DomWheelEvent ? _lastWheelEvent;
486487 bool _lastWheelEventWasTrackpad = false ;
487488
489+ DomElement get _rootElement => _view.dom.rootElement;
490+
488491 /// Each subclass is expected to override this method to attach its own event
489492 /// listeners and convert events into pointer events.
490493 void setup ();
491494
492- /// Remove all active event listeners.
493- void clearListeners () {
495+ /// Cleans up all event listeners attached by this adapter .
496+ void dispose () {
494497 for (final _Listener listener in _listeners) {
495498 listener.unregister ();
496499 }
@@ -499,7 +502,7 @@ abstract class _BaseAdapter {
499502
500503 /// Adds a listener for the given [eventName] to [target] .
501504 ///
502- /// Generally speaking, down and leave events should use [flutterViewElement ]
505+ /// Generally speaking, down and leave events should use [_rootElement ]
503506 /// as the [target] , while move and up events should use [domWindow]
504507 /// instead, because the browser doesn't fire the latter two for DOM elements
505508 /// when the pointer is outside the window.
@@ -512,7 +515,7 @@ abstract class _BaseAdapter {
512515 if (_debugLogPointerEvents) {
513516 if (domInstanceOfString (event, 'PointerEvent' )) {
514517 final DomPointerEvent pointerEvent = event as DomPointerEvent ;
515- final ui.Offset offset = computeEventOffsetToTarget (event, flutterViewElement );
518+ final ui.Offset offset = computeEventOffsetToTarget (event, _view );
516519 print ('${pointerEvent .type } '
517520 '${offset .dx .toStringAsFixed (1 )},'
518521 '${offset .dy .toStringAsFixed (1 )}' );
@@ -636,36 +639,37 @@ mixin _WheelEventListenerMixin on _BaseAdapter {
636639 deltaX *= _defaultScrollLineHeight! ;
637640 deltaY *= _defaultScrollLineHeight! ;
638641 case domDeltaPage:
639- deltaX *= ui.window .physicalSize.width;
640- deltaY *= ui.window .physicalSize.height;
642+ deltaX *= _view .physicalSize.width;
643+ deltaY *= _view .physicalSize.height;
641644 case domDeltaPixel:
642645 if (operatingSystem == OperatingSystem .macOs && (isSafari || isFirefox)) {
643646 // Safari and Firefox seem to report delta in logical pixels while
644647 // Chrome uses physical pixels.
645- deltaX *= ui.window .devicePixelRatio;
646- deltaY *= ui.window .devicePixelRatio;
648+ deltaX *= _view .devicePixelRatio;
649+ deltaY *= _view .devicePixelRatio;
647650 }
648651 default :
649652 break ;
650653 }
651654
652655 final List <ui.PointerData > data = < ui.PointerData > [];
653- final ui.Offset offset = computeEventOffsetToTarget (event, flutterViewElement );
656+ final ui.Offset offset = computeEventOffsetToTarget (event, _view );
654657 bool ignoreCtrlKey = false ;
655658 if (operatingSystem == OperatingSystem .macOs) {
656- ignoreCtrlKey = (KeyboardBinding .instance ? .converter. keyIsPressed (kPhysicalControlLeft) ?? false ) ||
657- (KeyboardBinding .instance ? .converter. keyIsPressed (kPhysicalControlRight) ?? false );
659+ ignoreCtrlKey = (_keyboardConverter. keyIsPressed (kPhysicalControlLeft)) ||
660+ (_keyboardConverter. keyIsPressed (kPhysicalControlRight));
658661 }
659662 if (event.ctrlKey && ! ignoreCtrlKey) {
660663 _pointerDataConverter.convert (
661664 data,
665+ viewId: _view.viewId,
662666 change: ui.PointerChange .hover,
663667 timeStamp: _BaseAdapter ._eventTimeStampToDuration (event.timeStamp! ),
664668 kind: kind,
665669 signalKind: ui.PointerSignalKind .scale,
666670 device: deviceId,
667- physicalX: offset.dx * ui.window .devicePixelRatio,
668- physicalY: offset.dy * ui.window .devicePixelRatio,
671+ physicalX: offset.dx * _view .devicePixelRatio,
672+ physicalY: offset.dy * _view .devicePixelRatio,
669673 buttons: event.buttons! .toInt (),
670674 pressure: 1.0 ,
671675 pressureMax: 1.0 ,
@@ -674,13 +678,14 @@ mixin _WheelEventListenerMixin on _BaseAdapter {
674678 } else {
675679 _pointerDataConverter.convert (
676680 data,
681+ viewId: _view.viewId,
677682 change: ui.PointerChange .hover,
678683 timeStamp: _BaseAdapter ._eventTimeStampToDuration (event.timeStamp! ),
679684 kind: kind,
680685 signalKind: ui.PointerSignalKind .scroll,
681686 device: deviceId,
682- physicalX: offset.dx * ui.window .devicePixelRatio,
683- physicalY: offset.dy * ui.window .devicePixelRatio,
687+ physicalX: offset.dx * _view .devicePixelRatio,
688+ physicalY: offset.dy * _view .devicePixelRatio,
684689 buttons: event.buttons! .toInt (),
685690 pressure: 1.0 ,
686691 pressureMax: 1.0 ,
@@ -696,7 +701,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter {
696701 void _addWheelEventListener (DartDomEventListener handler) {
697702 _listeners.add (_Listener .register (
698703 event: 'wheel' ,
699- target: flutterViewElement ,
704+ target: _rootElement ,
700705 handler: handler,
701706 passive: false ,
702707 ));
@@ -886,13 +891,24 @@ typedef _PointerEventListener = dynamic Function(DomPointerEvent event);
886891class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
887892 _PointerAdapter (
888893 super .callback,
889- super .flutterViewElement ,
894+ super .view ,
890895 super .pointerDataConverter,
891896 super .keyboardConverter,
892897 );
893898
894899 final Map <int , _ButtonSanitizer > _sanitizers = < int , _ButtonSanitizer > {};
895900
901+ DomEventTarget get _globalTarget {
902+ // When the Flutter app owns the full page, we want to listen on window for
903+ // some events. Otherwise, we just want to listen on the root element of the
904+ // view.
905+ // TODO(mdebbar): Is there a better way of doing this?
906+ if (_view == EnginePlatformDispatcher .instance.implicitView) {
907+ return domWindow;
908+ }
909+ return _view.dom.rootElement;
910+ }
911+
896912 @visibleForTesting
897913 Iterable <int > debugTrackedDevices () => _sanitizers.keys;
898914
@@ -942,7 +958,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
942958
943959 @override
944960 void setup () {
945- _addPointerEventListener (flutterViewElement , 'pointerdown' , (DomPointerEvent event) {
961+ _addPointerEventListener (_rootElement , 'pointerdown' , (DomPointerEvent event) {
946962 final int device = _getPointerId (event);
947963 final List <ui.PointerData > pointerData = < ui.PointerData > [];
948964 final _ButtonSanitizer sanitizer = _ensureSanitizer (device);
@@ -961,7 +977,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
961977 });
962978
963979 // Why `domWindow` you ask? See this fiddle: https://jsfiddle.net/ditman/7towxaqp
964- _addPointerEventListener (domWindow , 'pointermove' , (DomPointerEvent event) {
980+ _addPointerEventListener (_globalTarget , 'pointermove' , (DomPointerEvent event) {
965981 final int device = _getPointerId (event);
966982 final _ButtonSanitizer sanitizer = _ensureSanitizer (device);
967983 final List <ui.PointerData > pointerData = < ui.PointerData > [];
@@ -977,7 +993,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
977993 _callback (event, pointerData);
978994 });
979995
980- _addPointerEventListener (flutterViewElement , 'pointerleave' , (DomPointerEvent event) {
996+ _addPointerEventListener (_rootElement , 'pointerleave' , (DomPointerEvent event) {
981997 final int device = _getPointerId (event);
982998 final _ButtonSanitizer sanitizer = _ensureSanitizer (device);
983999 final List <ui.PointerData > pointerData = < ui.PointerData > [];
@@ -989,7 +1005,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
9891005 }, checkModifiers: false );
9901006
9911007 // TODO(dit): This must happen in the flutterViewElement, https://github.com/flutter/flutter/issues/116561
992- _addPointerEventListener (domWindow , 'pointerup' , (DomPointerEvent event) {
1008+ _addPointerEventListener (_globalTarget , 'pointerup' , (DomPointerEvent event) {
9931009 final int device = _getPointerId (event);
9941010 if (_hasSanitizer (device)) {
9951011 final List <ui.PointerData > pointerData = < ui.PointerData > [];
@@ -1006,7 +1022,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
10061022
10071023 // A browser fires cancel event if it concludes the pointer will no longer
10081024 // be able to generate events (example: device is deactivated)
1009- _addPointerEventListener (flutterViewElement , 'pointercancel' , (DomPointerEvent event) {
1025+ _addPointerEventListener (_rootElement , 'pointercancel' , (DomPointerEvent event) {
10101026 final int device = _getPointerId (event);
10111027 if (_hasSanitizer (device)) {
10121028 final List <ui.PointerData > pointerData = < ui.PointerData > [];
@@ -1033,16 +1049,17 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
10331049 final double tilt = _computeHighestTilt (event);
10341050 final Duration timeStamp = _BaseAdapter ._eventTimeStampToDuration (event.timeStamp! );
10351051 final num ? pressure = event.pressure;
1036- final ui.Offset offset = computeEventOffsetToTarget (event, flutterViewElement );
1052+ final ui.Offset offset = computeEventOffsetToTarget (event, _view );
10371053 _pointerDataConverter.convert (
10381054 data,
1055+ viewId: _view.viewId,
10391056 change: details.change,
10401057 timeStamp: timeStamp,
10411058 kind: kind,
10421059 signalKind: ui.PointerSignalKind .none,
10431060 device: _getPointerId (event),
1044- physicalX: offset.dx * ui.window .devicePixelRatio,
1045- physicalY: offset.dy * ui.window .devicePixelRatio,
1061+ physicalX: offset.dx * _view .devicePixelRatio,
1062+ physicalY: offset.dy * _view .devicePixelRatio,
10461063 buttons: details.buttons,
10471064 pressure: pressure == null ? 0.0 : pressure.toDouble (),
10481065 pressureMax: 1.0 ,
0 commit comments