mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[web] - Move text editing nodes outside of shadowDOM, reland (flutter/engine#40968)
[web] - Move text editing nodes outside of shadowDOM, reland
This commit is contained in:
parent
8179d070b3
commit
a5fdffa8c8
@ -24,7 +24,8 @@ import 'view_embedder/embedding_strategy/embedding_strategy.dart';
|
||||
/// Manages several top-level elements that host Flutter-generated content,
|
||||
/// including:
|
||||
///
|
||||
/// - [glassPaneElement], the root element of a Flutter view.
|
||||
/// - [flutterViewElement], the root element of a Flutter view.
|
||||
/// - [glassPaneElement], the glass pane element that hosts the shadowDOM.
|
||||
/// - [glassPaneShadow], the shadow root used to isolate Flutter-rendered
|
||||
/// content from the surrounding page content, including from the platform
|
||||
/// views.
|
||||
@ -62,7 +63,10 @@ class FlutterViewEmbedder {
|
||||
/// Abstracts all the DOM manipulations required to embed a Flutter app in an user-supplied `hostElement`.
|
||||
final EmbeddingStrategy _embeddingStrategy;
|
||||
|
||||
// The tag name for the root view of the flutter app (glass-pane)
|
||||
// The tag name for the Flutter View, which hosts the app.
|
||||
static const String flutterViewTagName = 'flutter-view';
|
||||
|
||||
// The tag name for the glass-pane.
|
||||
static const String glassPaneTagName = 'flt-glass-pane';
|
||||
|
||||
/// The element that contains the [sceneElement].
|
||||
@ -117,6 +121,9 @@ class FlutterViewEmbedder {
|
||||
/// which captures semantics input events. The semantics DOM tree must be a
|
||||
/// child of the glass pane element so that events bubble up to the glass pane
|
||||
/// if they are not handled by semantics.
|
||||
DomElement get flutterViewElement => _flutterViewElement;
|
||||
late DomElement _flutterViewElement;
|
||||
|
||||
DomElement get glassPaneElement => _glassPaneElement;
|
||||
late DomElement _glassPaneElement;
|
||||
|
||||
@ -124,6 +131,9 @@ class FlutterViewEmbedder {
|
||||
HostNode get glassPaneShadow => _glassPaneShadow;
|
||||
late HostNode _glassPaneShadow;
|
||||
|
||||
DomElement get textEditingHostNode => _textEditingHostNode;
|
||||
late DomElement _textEditingHostNode;
|
||||
|
||||
static const String defaultFontStyle = 'normal';
|
||||
static const String defaultFontWeight = 'normal';
|
||||
static const double defaultFontSize = 14;
|
||||
@ -149,14 +159,17 @@ class FlutterViewEmbedder {
|
||||
);
|
||||
|
||||
// Create and inject the [_glassPaneElement].
|
||||
_flutterViewElement = domDocument.createElement(flutterViewTagName);
|
||||
_glassPaneElement = domDocument.createElement(glassPaneTagName);
|
||||
|
||||
|
||||
// This must be attached to the DOM now, so the engine can create a host
|
||||
// node (ShadowDOM or a fallback) next.
|
||||
//
|
||||
// The embeddingStrategy will take care of cleaning up the glassPane on hot
|
||||
// restart.
|
||||
_embeddingStrategy.attachGlassPane(glassPaneElement);
|
||||
_embeddingStrategy.attachGlassPane(flutterViewElement);
|
||||
flutterViewElement.appendChild(glassPaneElement);
|
||||
|
||||
// Create a [HostNode] under the glass pane element, and attach everything
|
||||
// there, instead of directly underneath the glass panel.
|
||||
@ -168,6 +181,9 @@ class FlutterViewEmbedder {
|
||||
);
|
||||
_glassPaneShadow = glassPaneElementHostNode;
|
||||
|
||||
_textEditingHostNode =
|
||||
createTextEditingHostNode(flutterViewElement, defaultCssFont);
|
||||
|
||||
// Don't allow the scene to receive pointer events.
|
||||
_sceneHostElement = domDocument.createElement('flt-scene-host')
|
||||
..style.pointerEvents = 'none';
|
||||
@ -189,20 +205,20 @@ class FlutterViewEmbedder {
|
||||
glassPaneElementHostNode.appendAll(<DomNode>[
|
||||
accessibilityPlaceholder,
|
||||
_sceneHostElement!,
|
||||
|
||||
// The semantic host goes last because hit-test order-wise it must be
|
||||
// first. If semantics goes under the scene host, platform views will
|
||||
// obscure semantic elements.
|
||||
//
|
||||
// You may be wondering: wouldn't semantics obscure platform views and
|
||||
// make then not accessible? At least with some careful planning, that
|
||||
// should not be the case. The semantics tree makes all of its non-leaf
|
||||
// elements transparent. This way, if a platform view appears among other
|
||||
// interactive Flutter widgets, as long as those widgets do not intersect
|
||||
// with the platform view, the platform view will be reachable.
|
||||
semanticsHostElement,
|
||||
]);
|
||||
|
||||
// The semantic host goes last because hit-test order-wise it must be
|
||||
// first. If semantics goes under the scene host, platform views will
|
||||
// obscure semantic elements.
|
||||
//
|
||||
// You may be wondering: wouldn't semantics obscure platform views and
|
||||
// make then not accessible? At least with some careful planning, that
|
||||
// should not be the case. The semantics tree makes all of its non-leaf
|
||||
// elements transparent. This way, if a platform view appears among other
|
||||
// interactive Flutter widgets, as long as those widgets do not intersect
|
||||
// with the platform view, the platform view will be reachable.
|
||||
flutterViewElement.appendChild(semanticsHostElement);
|
||||
|
||||
// When debugging semantics, make the scene semi-transparent so that the
|
||||
// semantics tree is more prominent.
|
||||
if (configuration.debugShowSemanticsNodes) {
|
||||
@ -211,7 +227,7 @@ class FlutterViewEmbedder {
|
||||
|
||||
KeyboardBinding.initInstance();
|
||||
PointerBinding.initInstance(
|
||||
glassPaneElement,
|
||||
flutterViewElement,
|
||||
KeyboardBinding.instance!.converter,
|
||||
);
|
||||
|
||||
@ -336,7 +352,7 @@ class FlutterViewEmbedder {
|
||||
if (isWebKit) {
|
||||
// The resourcesHost *must* be a sibling of the glassPaneElement.
|
||||
_embeddingStrategy.attachResourcesHost(resourcesHost,
|
||||
nextTo: glassPaneElement);
|
||||
nextTo: flutterViewElement);
|
||||
} else {
|
||||
glassPaneShadow.node
|
||||
.insertBefore(resourcesHost, glassPaneShadow.node.firstChild);
|
||||
@ -393,3 +409,24 @@ FlutterViewEmbedder? _flutterViewEmbedder;
|
||||
FlutterViewEmbedder ensureFlutterViewEmbedderInitialized() =>
|
||||
_flutterViewEmbedder ??=
|
||||
FlutterViewEmbedder(hostElement: configuration.hostElement);
|
||||
|
||||
/// Creates a node to host text editing elements and applies a stylesheet
|
||||
/// to Flutter nodes that exist outside of the shadowDOM.
|
||||
DomElement createTextEditingHostNode(DomElement root, String defaultFont) {
|
||||
final DomElement domElement =
|
||||
domDocument.createElement('flt-text-editing-host');
|
||||
final DomHTMLStyleElement styleElement = createDomHTMLStyleElement();
|
||||
|
||||
styleElement.id = 'flt-text-editing-stylesheet';
|
||||
root.appendChild(styleElement);
|
||||
applyGlobalCssRulesToSheet(
|
||||
styleElement.sheet! as DomCSSStyleSheet,
|
||||
hasAutofillOverlay: browserHasAutofillOverlay(),
|
||||
cssSelectorPrefix: FlutterViewEmbedder.flutterViewTagName,
|
||||
defaultCssFont: defaultFont,
|
||||
);
|
||||
|
||||
root.appendChild(domElement);
|
||||
|
||||
return domElement;
|
||||
}
|
||||
|
||||
@ -110,10 +110,8 @@ class ShadowDomHostNode implements HostNode {
|
||||
/// This also calls [applyGlobalCssRulesToSheet], with the [defaultFont]
|
||||
/// to be used as the default font definition.
|
||||
ShadowDomHostNode(DomElement root, String defaultFont)
|
||||
: assert(
|
||||
root.isConnected ?? true,
|
||||
'The `root` of a ShadowDomHostNode must be connected to the Document object or a ShadowRoot.'
|
||||
) {
|
||||
: assert(root.isConnected ?? true,
|
||||
'The `root` of a ShadowDomHostNode must be connected to the Document object or a ShadowRoot.') {
|
||||
_shadow = root.attachShadow(<String, dynamic>{
|
||||
'mode': 'open',
|
||||
// This needs to stay false to prevent issues like this:
|
||||
@ -181,7 +179,7 @@ class ElementHostNode implements HostNode {
|
||||
applyGlobalCssRulesToSheet(
|
||||
styleElement.sheet! as DomCSSStyleSheet,
|
||||
hasAutofillOverlay: browserHasAutofillOverlay(),
|
||||
cssSelectorPrefix: FlutterViewEmbedder.glassPaneTagName,
|
||||
cssSelectorPrefix: FlutterViewEmbedder.flutterViewTagName,
|
||||
defaultCssFont: defaultFont,
|
||||
);
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ class MouseCursor {
|
||||
|
||||
void activateSystemCursor(String? kind) {
|
||||
setElementStyle(
|
||||
flutterViewEmbedder.glassPaneElement,
|
||||
flutterViewEmbedder.flutterViewElement,
|
||||
'cursor',
|
||||
_mapKindToCssValue(kind),
|
||||
);
|
||||
|
||||
@ -128,8 +128,9 @@ class PlatformViewManager {
|
||||
}
|
||||
|
||||
_ensureContentCorrectlySized(content, viewType);
|
||||
wrapper.append(content);
|
||||
|
||||
return wrapper..append(content);
|
||||
return wrapper;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ class SafariPointerEventWorkaround {
|
||||
}
|
||||
|
||||
class PointerBinding {
|
||||
PointerBinding(this.glassPaneElement, this._keyboardConverter)
|
||||
PointerBinding(this.flutterViewElement, this._keyboardConverter)
|
||||
: _pointerDataConverter = PointerDataConverter(),
|
||||
_detector = const PointerSupportDetector() {
|
||||
if (isIosSafari) {
|
||||
@ -93,9 +93,9 @@ class PointerBinding {
|
||||
static PointerBinding? get instance => _instance;
|
||||
static PointerBinding? _instance;
|
||||
|
||||
static void initInstance(DomElement glassPaneElement, KeyboardConverter keyboardConverter) {
|
||||
static void initInstance(DomElement flutterViewElement, KeyboardConverter keyboardConverter) {
|
||||
if (_instance == null) {
|
||||
_instance = PointerBinding(glassPaneElement, keyboardConverter);
|
||||
_instance = PointerBinding(flutterViewElement, keyboardConverter);
|
||||
assert(() {
|
||||
registerHotRestartListener(_instance!.dispose);
|
||||
return true;
|
||||
@ -110,7 +110,7 @@ class PointerBinding {
|
||||
_pointerDataConverter.clearPointerState();
|
||||
}
|
||||
|
||||
final DomElement glassPaneElement;
|
||||
final DomElement flutterViewElement;
|
||||
|
||||
PointerSupportDetector _detector;
|
||||
final PointerDataConverter _pointerDataConverter;
|
||||
@ -156,15 +156,15 @@ class PointerBinding {
|
||||
// TODO(dit): remove old API fallbacks, https://github.com/flutter/flutter/issues/116141
|
||||
_BaseAdapter _createAdapter() {
|
||||
if (_detector.hasPointerEvents) {
|
||||
return _PointerAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
|
||||
return _PointerAdapter(_onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter);
|
||||
}
|
||||
// Fallback for Safari Mobile < 13. To be removed.
|
||||
if (_detector.hasTouchEvents) {
|
||||
return _TouchAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
|
||||
return _TouchAdapter(_onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter);
|
||||
}
|
||||
// Fallback for Safari Desktop < 13. To be removed.
|
||||
if (_detector.hasMouseEvents) {
|
||||
return _MouseAdapter(_onPointerData, glassPaneElement, _pointerDataConverter, _keyboardConverter);
|
||||
return _MouseAdapter(_onPointerData, flutterViewElement, _pointerDataConverter, _keyboardConverter);
|
||||
}
|
||||
throw UnsupportedError('This browser does not support pointer, touch, or mouse events.');
|
||||
}
|
||||
@ -264,7 +264,7 @@ class _Listener {
|
||||
abstract class _BaseAdapter {
|
||||
_BaseAdapter(
|
||||
this._callback,
|
||||
this.glassPaneElement,
|
||||
this.flutterViewElement,
|
||||
this._pointerDataConverter,
|
||||
this._keyboardConverter,
|
||||
) {
|
||||
@ -272,7 +272,7 @@ abstract class _BaseAdapter {
|
||||
}
|
||||
|
||||
final List<_Listener> _listeners = <_Listener>[];
|
||||
final DomElement glassPaneElement;
|
||||
final DomElement flutterViewElement;
|
||||
final _PointerDataCallback _callback;
|
||||
final PointerDataConverter _pointerDataConverter;
|
||||
final KeyboardConverter _keyboardConverter;
|
||||
@ -293,7 +293,7 @@ abstract class _BaseAdapter {
|
||||
|
||||
/// Adds a listener for the given [eventName] to [target].
|
||||
///
|
||||
/// Generally speaking, down and leave events should use [glassPaneElement]
|
||||
/// Generally speaking, down and leave events should use [flutterViewElement]
|
||||
/// as the [target], while move and up events should use [domWindow]
|
||||
/// instead, because the browser doesn't fire the latter two for DOM elements
|
||||
/// when the pointer is outside the window.
|
||||
@ -313,7 +313,7 @@ abstract class _BaseAdapter {
|
||||
if (_debugLogPointerEvents) {
|
||||
if (domInstanceOfString(event, 'PointerEvent')) {
|
||||
final DomPointerEvent pointerEvent = event as DomPointerEvent;
|
||||
final ui.Offset offset = computeEventOffsetToTarget(event, glassPaneElement);
|
||||
final ui.Offset offset = computeEventOffsetToTarget(event, flutterViewElement);
|
||||
print('${pointerEvent.type} '
|
||||
'${offset.dx.toStringAsFixed(1)},'
|
||||
'${offset.dy.toStringAsFixed(1)}');
|
||||
@ -452,7 +452,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter {
|
||||
}
|
||||
|
||||
final List<ui.PointerData> data = <ui.PointerData>[];
|
||||
final ui.Offset offset = computeEventOffsetToTarget(event, glassPaneElement);
|
||||
final ui.Offset offset = computeEventOffsetToTarget(event, flutterViewElement);
|
||||
bool ignoreCtrlKey = false;
|
||||
if (operatingSystem == OperatingSystem.macOs) {
|
||||
ignoreCtrlKey = (KeyboardBinding.instance?.converter.keyIsPressed(kPhysicalControlLeft) ?? false) ||
|
||||
@ -498,7 +498,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter {
|
||||
void _addWheelEventListener(DartDomEventListener handler) {
|
||||
_listeners.add(_Listener.registerNative(
|
||||
event: 'wheel',
|
||||
target: glassPaneElement,
|
||||
target: flutterViewElement,
|
||||
jsHandler: createDomEventListener(handler),
|
||||
));
|
||||
}
|
||||
@ -687,7 +687,7 @@ typedef _PointerEventListener = dynamic Function(DomPointerEvent event);
|
||||
class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
_PointerAdapter(
|
||||
super.callback,
|
||||
super.glassPaneElement,
|
||||
super.flutterViewElement,
|
||||
super.pointerDataConverter,
|
||||
super.keyboardConverter,
|
||||
);
|
||||
@ -744,7 +744,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
|
||||
@override
|
||||
void setup() {
|
||||
_addPointerEventListener(glassPaneElement, 'pointerdown', (DomPointerEvent event) {
|
||||
_addPointerEventListener(flutterViewElement, 'pointerdown', (DomPointerEvent event) {
|
||||
final int device = _getPointerId(event);
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
final _ButtonSanitizer sanitizer = _ensureSanitizer(device);
|
||||
@ -779,7 +779,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
_callback(pointerData);
|
||||
});
|
||||
|
||||
_addPointerEventListener(glassPaneElement, 'pointerleave', (DomPointerEvent event) {
|
||||
_addPointerEventListener(flutterViewElement, 'pointerleave', (DomPointerEvent event) {
|
||||
final int device = _getPointerId(event);
|
||||
final _ButtonSanitizer sanitizer = _ensureSanitizer(device);
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
@ -790,7 +790,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
}
|
||||
}, useCapture: false, checkModifiers: false);
|
||||
|
||||
// TODO(dit): This must happen in the glassPane, https://github.com/flutter/flutter/issues/116561
|
||||
// TODO(dit): This must happen in the flutterViewElement, https://github.com/flutter/flutter/issues/116561
|
||||
_addPointerEventListener(domWindow, 'pointerup', (DomPointerEvent event) {
|
||||
final int device = _getPointerId(event);
|
||||
if (_hasSanitizer(device)) {
|
||||
@ -804,11 +804,11 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(dit): Synthesize a "cancel" event when 'pointerup' happens outside of the glassPane, https://github.com/flutter/flutter/issues/116561
|
||||
// TODO(dit): Synthesize a "cancel" event when 'pointerup' happens outside of the flutterViewElement, https://github.com/flutter/flutter/issues/116561
|
||||
|
||||
// A browser fires cancel event if it concludes the pointer will no longer
|
||||
// be able to generate events (example: device is deactivated)
|
||||
_addPointerEventListener(glassPaneElement, 'pointercancel', (DomPointerEvent event) {
|
||||
_addPointerEventListener(flutterViewElement, 'pointercancel', (DomPointerEvent event) {
|
||||
final int device = _getPointerId(event);
|
||||
if (_hasSanitizer(device)) {
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
@ -835,7 +835,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
final double tilt = _computeHighestTilt(event);
|
||||
final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!);
|
||||
final num? pressure = event.pressure;
|
||||
final ui.Offset offset = computeEventOffsetToTarget(event, glassPaneElement);
|
||||
final ui.Offset offset = computeEventOffsetToTarget(event, flutterViewElement);
|
||||
_pointerDataConverter.convert(
|
||||
data,
|
||||
change: details.change,
|
||||
@ -906,7 +906,7 @@ typedef _TouchEventListener = dynamic Function(DomTouchEvent event);
|
||||
class _TouchAdapter extends _BaseAdapter {
|
||||
_TouchAdapter(
|
||||
super.callback,
|
||||
super.glassPaneElement,
|
||||
super.flutterViewElement,
|
||||
super.pointerDataConverter,
|
||||
super.keyboardConverter,
|
||||
);
|
||||
@ -938,7 +938,7 @@ class _TouchAdapter extends _BaseAdapter {
|
||||
|
||||
@override
|
||||
void setup() {
|
||||
_addTouchEventListener(glassPaneElement, 'touchstart', (DomTouchEvent event) {
|
||||
_addTouchEventListener(flutterViewElement, 'touchstart', (DomTouchEvent event) {
|
||||
final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!);
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
for (final DomTouch touch in event.changedTouches.cast<DomTouch>()) {
|
||||
@ -957,7 +957,7 @@ class _TouchAdapter extends _BaseAdapter {
|
||||
_callback(pointerData);
|
||||
});
|
||||
|
||||
_addTouchEventListener(glassPaneElement, 'touchmove', (DomTouchEvent event) {
|
||||
_addTouchEventListener(flutterViewElement, 'touchmove', (DomTouchEvent event) {
|
||||
event.preventDefault(); // Prevents standard overscroll on iOS/Webkit.
|
||||
final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!);
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
@ -976,7 +976,7 @@ class _TouchAdapter extends _BaseAdapter {
|
||||
_callback(pointerData);
|
||||
});
|
||||
|
||||
_addTouchEventListener(glassPaneElement, 'touchend', (DomTouchEvent event) {
|
||||
_addTouchEventListener(flutterViewElement, 'touchend', (DomTouchEvent event) {
|
||||
// On Safari Mobile, the keyboard does not show unless this line is
|
||||
// added.
|
||||
event.preventDefault();
|
||||
@ -998,7 +998,7 @@ class _TouchAdapter extends _BaseAdapter {
|
||||
_callback(pointerData);
|
||||
});
|
||||
|
||||
_addTouchEventListener(glassPaneElement, 'touchcancel', (DomTouchEvent event) {
|
||||
_addTouchEventListener(flutterViewElement, 'touchcancel', (DomTouchEvent event) {
|
||||
final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!);
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
for (final DomTouch touch in event.changedTouches.cast<DomTouch>()) {
|
||||
@ -1064,7 +1064,7 @@ typedef _MouseEventListener = dynamic Function(DomMouseEvent event);
|
||||
class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
_MouseAdapter(
|
||||
super.callback,
|
||||
super.glassPaneElement,
|
||||
super.flutterViewElement,
|
||||
super.pointerDataConverter,
|
||||
super.keyboardConverter,
|
||||
);
|
||||
@ -1099,7 +1099,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
|
||||
@override
|
||||
void setup() {
|
||||
_addMouseEventListener(glassPaneElement, 'mousedown', (DomMouseEvent event) {
|
||||
_addMouseEventListener(flutterViewElement, 'mousedown', (DomMouseEvent event) {
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
final _SanitizedDetails? up =
|
||||
_sanitizer.sanitizeMissingRightClickUp(buttons: event.buttons!.toInt());
|
||||
@ -1127,7 +1127,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
_callback(pointerData);
|
||||
});
|
||||
|
||||
_addMouseEventListener(glassPaneElement, 'mouseleave', (DomMouseEvent event) {
|
||||
_addMouseEventListener(flutterViewElement, 'mouseleave', (DomMouseEvent event) {
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
final _SanitizedDetails? details = _sanitizer.sanitizeLeaveEvent(buttons: event.buttons!.toInt());
|
||||
if (details != null) {
|
||||
@ -1136,7 +1136,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
}
|
||||
}, useCapture: false);
|
||||
|
||||
// TODO(dit): This must happen in the glassPane, https://github.com/flutter/flutter/issues/116561
|
||||
// TODO(dit): This must happen in the flutterViewElement, https://github.com/flutter/flutter/issues/116561
|
||||
_addMouseEventListener(domWindow, 'mouseup', (DomMouseEvent event) {
|
||||
final List<ui.PointerData> pointerData = <ui.PointerData>[];
|
||||
final _SanitizedDetails? sanitizedDetails = _sanitizer.sanitizeUpEvent(buttons: event.buttons?.toInt());
|
||||
@ -1158,7 +1158,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
|
||||
required DomMouseEvent event,
|
||||
required _SanitizedDetails details,
|
||||
}) {
|
||||
final ui.Offset offset = computeEventOffsetToTarget(event, glassPaneElement);
|
||||
final ui.Offset offset = computeEventOffsetToTarget(event, flutterViewElement);
|
||||
_pointerDataConverter.convert(
|
||||
data,
|
||||
change: details.change,
|
||||
|
||||
@ -19,23 +19,25 @@ import '../semantics.dart' show EngineSemanticsOwner;
|
||||
/// It also takes into account semantics being enabled to fix the case where
|
||||
/// offsetX, offsetY == 0 (TalkBack events).
|
||||
ui.Offset computeEventOffsetToTarget(DomMouseEvent event, DomElement actualTarget) {
|
||||
// On top of a platform view
|
||||
if (event.target != actualTarget) {
|
||||
return _computeOffsetOnPlatformView(event, actualTarget);
|
||||
}
|
||||
// On a TalkBack event
|
||||
if (EngineSemanticsOwner.instance.semanticsEnabled && event.offsetX == 0 && event.offsetY == 0) {
|
||||
return _computeOffsetForTalkbackEvent(event, actualTarget);
|
||||
}
|
||||
|
||||
final bool isTargetOutsideOfShadowDOM = event.target != actualTarget;
|
||||
if (isTargetOutsideOfShadowDOM) {
|
||||
return _computeOffsetRelativeToActualTarget(event, actualTarget);
|
||||
}
|
||||
// Return the offsetX/Y in the normal case.
|
||||
// (This works with 3D translations of the parent element.)
|
||||
return ui.Offset(event.offsetX, event.offsetY);
|
||||
}
|
||||
|
||||
/// Computes the event offset when hovering over a platformView.
|
||||
/// Computes the event offset when hovering over any nodes that don't exist in
|
||||
/// the shadowDOM such as platform views or text editing nodes.
|
||||
///
|
||||
/// This still uses offsetX/Y, but adds the offset from the top/left corner of the
|
||||
/// platform view to the glass pane (`actualTarget`).
|
||||
/// platform view to the Flutter View (`actualTarget`).
|
||||
///
|
||||
/// ×--FlutterView(actualTarget)--------------+
|
||||
/// |\ |
|
||||
@ -57,7 +59,7 @@ ui.Offset computeEventOffsetToTarget(DomMouseEvent event, DomElement actualTarge
|
||||
///
|
||||
/// Event offset relative to FlutterView = (offsetX + xP, offsetY + yP)
|
||||
// TODO(dit): Make this understand 3D transforms, https://github.com/flutter/flutter/issues/117091
|
||||
ui.Offset _computeOffsetOnPlatformView(DomMouseEvent event, DomElement actualTarget) {
|
||||
ui.Offset _computeOffsetRelativeToActualTarget(DomMouseEvent event, DomElement actualTarget) {
|
||||
final DomElement target = event.target! as DomElement;
|
||||
final DomRect targetRect = target.getBoundingClientRect();
|
||||
final DomRect actualTargetRect = actualTarget.getBoundingClientRect();
|
||||
|
||||
@ -7,7 +7,6 @@ import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../browser_detection.dart';
|
||||
import '../dom.dart';
|
||||
import '../embedder.dart';
|
||||
import '../platform_dispatcher.dart';
|
||||
import '../text_editing/text_editing.dart';
|
||||
import 'semantics.dart';
|
||||
@ -421,14 +420,14 @@ class TextField extends RoleManager {
|
||||
..height = '${semanticsObject.rect!.height}px';
|
||||
|
||||
if (semanticsObject.hasFocus) {
|
||||
if (flutterViewEmbedder.glassPaneShadow.activeElement !=
|
||||
if (domDocument.activeElement !=
|
||||
activeEditableElement) {
|
||||
semanticsObject.owner.addOneTimePostUpdateCallback(() {
|
||||
activeEditableElement.focus();
|
||||
});
|
||||
}
|
||||
SemanticsTextEditingStrategy._instance?.activate(this);
|
||||
} else if (flutterViewEmbedder.glassPaneShadow.activeElement ==
|
||||
} else if (domDocument.activeElement ==
|
||||
activeEditableElement) {
|
||||
if (!isIosSafari) {
|
||||
SemanticsTextEditingStrategy._instance?.deactivate(this);
|
||||
|
||||
@ -51,7 +51,8 @@ void _emptyCallback(dynamic _) {}
|
||||
|
||||
/// The default [HostNode] that hosts all DOM required for text editing when a11y is not enabled.
|
||||
@visibleForTesting
|
||||
HostNode get defaultTextEditingRoot => flutterViewEmbedder.glassPaneShadow;
|
||||
DomElement get defaultTextEditingRoot =>
|
||||
flutterViewEmbedder.textEditingHostNode;
|
||||
|
||||
/// These style attributes are constant throughout the life time of an input
|
||||
/// element.
|
||||
|
||||
@ -42,7 +42,7 @@ void main() {
|
||||
|
||||
void testMain() {
|
||||
ensureFlutterViewEmbedderInitialized();
|
||||
final DomElement glassPane = flutterViewEmbedder.glassPaneElement;
|
||||
final DomElement flutterViewElement = flutterViewEmbedder.flutterViewElement;
|
||||
late double dpi;
|
||||
|
||||
setUp(() {
|
||||
@ -483,7 +483,7 @@ void testMain() {
|
||||
_MouseEventContext(),
|
||||
_TouchEventContext(),
|
||||
],
|
||||
'can receive pointer events on the glass pane',
|
||||
'can receive pointer events on the app root',
|
||||
(_BasicEventContext context) {
|
||||
PointerBinding.instance!.debugOverrideDetector(context);
|
||||
ui.PointerDataPacket? receivedPacket;
|
||||
@ -491,7 +491,7 @@ void testMain() {
|
||||
receivedPacket = packet;
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
|
||||
expect(receivedPacket, isNotNull);
|
||||
expect(receivedPacket!.data[0].buttons, equals(1));
|
||||
@ -512,7 +512,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets.single.data, hasLength(2));
|
||||
@ -546,7 +546,7 @@ void testMain() {
|
||||
|
||||
expect(keyboardConverter.keyIsPressed(physicalLeft), false);
|
||||
expect(keyboardConverter.keyIsPressed(physicalRight), false);
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 1);
|
||||
expectKeyData(keyDataList.last,
|
||||
type: ui.KeyEventType.down,
|
||||
@ -598,7 +598,7 @@ void testMain() {
|
||||
expect(keyboardConverter.keyIsPressed(physicalRight), false);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 0);
|
||||
}
|
||||
|
||||
@ -618,7 +618,7 @@ void testMain() {
|
||||
expect(keyboardConverter.keyIsPressed(physicalRight), true);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 0);
|
||||
}
|
||||
|
||||
@ -668,7 +668,7 @@ void testMain() {
|
||||
expect(keyboardConverter.keyIsPressed(physicalRight), false);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 1);
|
||||
expectKeyData(keyDataList.last,
|
||||
type: ui.KeyEventType.up,
|
||||
@ -697,7 +697,7 @@ void testMain() {
|
||||
expect(keyboardConverter.keyIsPressed(physicalRight), true);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 1);
|
||||
expectKeyData(keyDataList.last,
|
||||
type: ui.KeyEventType.up,
|
||||
@ -749,7 +749,7 @@ void testMain() {
|
||||
expect(keyboardConverter.keyIsPressed(physicalRight), false);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 0);
|
||||
}
|
||||
|
||||
@ -786,7 +786,7 @@ void testMain() {
|
||||
expect(keyboardConverter.keyIsPressed(physicalAltRight), true);
|
||||
keyDataList.clear(); // Remove key data generated by handleEvent.
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown());
|
||||
flutterViewElement.dispatchEvent(context.primaryDown());
|
||||
expect(keyDataList.length, 1);
|
||||
expectKeyData(keyDataList.last,
|
||||
type: ui.KeyEventType.up,
|
||||
@ -814,7 +814,7 @@ void testMain() {
|
||||
|
||||
final DomElement semanticsPlaceholder =
|
||||
createDomElement('flt-semantics-placeholder');
|
||||
glassPane.append(semanticsPlaceholder);
|
||||
flutterViewElement.append(semanticsPlaceholder);
|
||||
|
||||
// Press on the semantics placeholder.
|
||||
semanticsPlaceholder.dispatchEvent(context.primaryDown(
|
||||
@ -853,7 +853,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release the pointer on the semantics placeholder.
|
||||
glassPane.dispatchEvent(context.primaryUp(
|
||||
flutterViewElement.dispatchEvent(context.primaryUp(
|
||||
clientX: 100.0,
|
||||
clientY: 200.0,
|
||||
));
|
||||
@ -887,7 +887,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.hover());
|
||||
flutterViewElement.dispatchEvent(context.hover());
|
||||
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets.single.data, hasLength(2));
|
||||
@ -911,7 +911,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown(
|
||||
flutterViewElement.dispatchEvent(context.primaryDown(
|
||||
clientX: 10.0,
|
||||
clientY: 10.0,
|
||||
));
|
||||
@ -923,7 +923,7 @@ void testMain() {
|
||||
expect(packets[0].data[1].change, equals(ui.PointerChange.down));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown(
|
||||
flutterViewElement.dispatchEvent(context.primaryDown(
|
||||
clientX: 20.0,
|
||||
clientY: 20.0,
|
||||
));
|
||||
@ -948,7 +948,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -956,7 +956,7 @@ void testMain() {
|
||||
deltaY: 10,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 20,
|
||||
clientY: 50,
|
||||
@ -964,14 +964,14 @@ void testMain() {
|
||||
deltaY: 10,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 0,
|
||||
buttons: 1,
|
||||
clientX: 20.0,
|
||||
clientY: 50.0,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 1,
|
||||
clientX: 30,
|
||||
clientY: 60,
|
||||
@ -1073,7 +1073,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1122,7 +1122,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1170,7 +1170,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1181,7 +1181,7 @@ void testMain() {
|
||||
timeStamp: 0,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1192,7 +1192,7 @@ void testMain() {
|
||||
timeStamp: 10,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1203,7 +1203,7 @@ void testMain() {
|
||||
timeStamp: 20,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1214,7 +1214,7 @@ void testMain() {
|
||||
timeStamp: 1000,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1225,7 +1225,7 @@ void testMain() {
|
||||
timeStamp: 1010,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1236,7 +1236,7 @@ void testMain() {
|
||||
timeStamp: 2000,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1411,7 +1411,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1419,7 +1419,7 @@ void testMain() {
|
||||
deltaY: 120,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1431,7 +1431,7 @@ void testMain() {
|
||||
debugOperatingSystemOverride = OperatingSystem.macOs;
|
||||
KeyboardBinding.instance?.converter.handleEvent(keyDownEvent('ControlLeft', 'Control', kCtrl));
|
||||
|
||||
glassPane.dispatchEvent(context.wheel(
|
||||
flutterViewElement.dispatchEvent(context.wheel(
|
||||
buttons: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
@ -1516,7 +1516,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.hover(
|
||||
flutterViewElement.dispatchEvent(context.hover(
|
||||
clientX: 10.0,
|
||||
clientY: 10.0,
|
||||
));
|
||||
@ -1539,7 +1539,7 @@ void testMain() {
|
||||
expect(packets[0].data[1].physicalDeltaY, equals(0.0));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.hover(
|
||||
flutterViewElement.dispatchEvent(context.hover(
|
||||
clientX: 20.0,
|
||||
clientY: 20.0,
|
||||
));
|
||||
@ -1554,7 +1554,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown(
|
||||
flutterViewElement.dispatchEvent(context.primaryDown(
|
||||
clientX: 20.0,
|
||||
clientY: 20.0,
|
||||
));
|
||||
@ -1569,7 +1569,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].physicalDeltaY, equals(0.0));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.primaryMove(
|
||||
flutterViewElement.dispatchEvent(context.primaryMove(
|
||||
clientX: 40.0,
|
||||
clientY: 30.0,
|
||||
));
|
||||
@ -1584,7 +1584,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.primaryUp(
|
||||
flutterViewElement.dispatchEvent(context.primaryUp(
|
||||
clientX: 40.0,
|
||||
clientY: 30.0,
|
||||
));
|
||||
@ -1599,7 +1599,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].physicalDeltaY, equals(0.0));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.hover(
|
||||
flutterViewElement.dispatchEvent(context.hover(
|
||||
clientX: 20.0,
|
||||
clientY: 10.0,
|
||||
));
|
||||
@ -1614,7 +1614,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].physicalDeltaY, equals(-20.0 * dpi));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.primaryDown(
|
||||
flutterViewElement.dispatchEvent(context.primaryDown(
|
||||
clientX: 20.0,
|
||||
clientY: 10.0,
|
||||
));
|
||||
@ -1646,7 +1646,7 @@ void testMain() {
|
||||
|
||||
// Add and hover
|
||||
|
||||
glassPane.dispatchEvent(context.hover(
|
||||
flutterViewElement.dispatchEvent(context.hover(
|
||||
clientX: 10,
|
||||
clientY: 11,
|
||||
));
|
||||
@ -1665,7 +1665,7 @@ void testMain() {
|
||||
expect(packets[0].data[1].buttons, equals(0));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 0,
|
||||
buttons: 1,
|
||||
clientX: 10.0,
|
||||
@ -1680,7 +1680,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].buttons, equals(1));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: _kNoButtonChange,
|
||||
buttons: 1,
|
||||
clientX: 20.0,
|
||||
@ -1695,7 +1695,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].buttons, equals(1));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 0,
|
||||
clientX: 20.0,
|
||||
clientY: 21.0,
|
||||
@ -1710,7 +1710,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Drag with secondary button
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
clientX: 20.0,
|
||||
@ -1725,7 +1725,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].buttons, equals(2));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: _kNoButtonChange,
|
||||
buttons: 2,
|
||||
clientX: 30.0,
|
||||
@ -1740,7 +1740,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].buttons, equals(2));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 2,
|
||||
clientX: 30.0,
|
||||
clientY: 31.0,
|
||||
@ -1755,7 +1755,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Drag with middle button
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 1,
|
||||
buttons: 4,
|
||||
clientX: 30.0,
|
||||
@ -1770,7 +1770,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].buttons, equals(4));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: _kNoButtonChange,
|
||||
buttons: 4,
|
||||
clientX: 40.0,
|
||||
@ -1785,7 +1785,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].buttons, equals(4));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 1,
|
||||
clientX: 40.0,
|
||||
clientY: 41.0,
|
||||
@ -1801,7 +1801,7 @@ void testMain() {
|
||||
|
||||
// Leave
|
||||
|
||||
glassPane.dispatchEvent(context.mouseLeave(
|
||||
flutterViewElement.dispatchEvent(context.mouseLeave(
|
||||
buttons: 1,
|
||||
clientX: 1000.0,
|
||||
clientY: 2000.0,
|
||||
@ -1809,7 +1809,7 @@ void testMain() {
|
||||
expect(packets, isEmpty);
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseLeave(
|
||||
flutterViewElement.dispatchEvent(context.mouseLeave(
|
||||
buttons: 0,
|
||||
clientX: 1000.0,
|
||||
clientY: 2000.0,
|
||||
@ -1839,7 +1839,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press LMB.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 0,
|
||||
buttons: 1,
|
||||
));
|
||||
@ -1854,7 +1854,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Press MMB.
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: 1,
|
||||
buttons: 5,
|
||||
));
|
||||
@ -1866,7 +1866,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release LMB.
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: 0,
|
||||
buttons: 4,
|
||||
));
|
||||
@ -1878,7 +1878,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release MMB.
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 1,
|
||||
));
|
||||
expect(packets, hasLength(1));
|
||||
@ -1906,7 +1906,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
clientX: 10,
|
||||
@ -1927,7 +1927,7 @@ void testMain() {
|
||||
expect(packets[0].data[1].buttons, equals(2));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: _kNoButtonChange,
|
||||
buttons: 2,
|
||||
clientX: 20.0,
|
||||
@ -1942,7 +1942,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].buttons, equals(2));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: _kNoButtonChange,
|
||||
buttons: 2,
|
||||
clientX: 20.0,
|
||||
@ -1957,7 +1957,7 @@ void testMain() {
|
||||
expect(packets[0].data[0].buttons, equals(2));
|
||||
packets.clear();
|
||||
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 2,
|
||||
clientX: 20.0,
|
||||
clientY: 21.0,
|
||||
@ -1993,7 +1993,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press RMB and hold, popping up the context menu.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
));
|
||||
@ -2010,7 +2010,7 @@ void testMain() {
|
||||
// Press LMB. The event will have "button: -1" here, despite the change
|
||||
// in "buttons", probably because the "press" gesture was absorbed by
|
||||
// dismissing the context menu.
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: _kNoButtonChange,
|
||||
buttons: 3,
|
||||
));
|
||||
@ -2022,7 +2022,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release LMB.
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: 0,
|
||||
buttons: 2,
|
||||
));
|
||||
@ -2034,7 +2034,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release RMB.
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 2,
|
||||
));
|
||||
expect(packets, hasLength(1));
|
||||
@ -2066,7 +2066,7 @@ void testMain() {
|
||||
|
||||
// Press RMB popping up the context menu, then release by LMB down and up.
|
||||
// Browser won't send up event in that case.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
));
|
||||
@ -2081,7 +2081,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// User now hovers.
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: _kNoButtonChange,
|
||||
buttons: 0,
|
||||
));
|
||||
@ -2115,7 +2115,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press RMB and hold, popping up the context menu.
|
||||
glassPane.dispatchEvent(context.mouseMove(
|
||||
flutterViewElement.dispatchEvent(context.mouseMove(
|
||||
button: -1,
|
||||
buttons: 2,
|
||||
clientX: 10.0,
|
||||
@ -2152,7 +2152,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press RMB and hold, popping up the context menu.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
clientX: 10.0,
|
||||
@ -2171,7 +2171,7 @@ void testMain() {
|
||||
// Move the mouse. The event will have "buttons: 0" because RMB was
|
||||
// released but the browser didn't send a pointerup/mouseup event.
|
||||
// The hover is also triggered at a different position.
|
||||
glassPane.dispatchEvent(context.hover(
|
||||
flutterViewElement.dispatchEvent(context.hover(
|
||||
clientX: 20.0,
|
||||
clientY: 20.0,
|
||||
));
|
||||
@ -2215,7 +2215,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press RMB and hold, popping up the context menu.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
clientX: 10.0,
|
||||
@ -2232,7 +2232,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Press LMB.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 0,
|
||||
buttons: 3,
|
||||
clientX: 20.0,
|
||||
@ -2246,7 +2246,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release LMB.
|
||||
glassPane.dispatchEvent(context.primaryUp(
|
||||
flutterViewElement.dispatchEvent(context.primaryUp(
|
||||
clientX: 20.0,
|
||||
clientY: 20.0,
|
||||
));
|
||||
@ -2278,7 +2278,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press RMB and hold, popping up the context menu.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
clientX: 10.0,
|
||||
@ -2296,7 +2296,7 @@ void testMain() {
|
||||
// Press RMB again. In Chrome, when RMB is clicked again while the
|
||||
// context menu is still active, it sends a pointerdown/mousedown event
|
||||
// with "buttons:0". We convert this to pointer up, pointer down.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 0,
|
||||
clientX: 20.0,
|
||||
@ -2318,7 +2318,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release RMB.
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 2,
|
||||
clientX: 20.0,
|
||||
clientY: 20.0,
|
||||
@ -2353,7 +2353,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press RMB, popping up the context menu.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
clientX: 10.0,
|
||||
@ -2369,7 +2369,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// RMB up.
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 2,
|
||||
clientX: 10.0,
|
||||
clientY: 10.0,
|
||||
@ -2384,7 +2384,7 @@ void testMain() {
|
||||
// Press RMB again. In Chrome, when RMB is clicked again while the
|
||||
// context menu is still active, it sends a pointerdown/mousedown event
|
||||
// with "buttons:0".
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 0,
|
||||
clientX: 20.0,
|
||||
@ -2401,7 +2401,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release RMB.
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 2,
|
||||
clientX: 20.0,
|
||||
clientY: 20.0,
|
||||
@ -2439,7 +2439,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press RMB and hold, popping up the context menu.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
clientX: 10.0,
|
||||
@ -2455,7 +2455,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release RMB.
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 2,
|
||||
clientX: 10.0,
|
||||
clientY: 10.0,
|
||||
@ -2467,7 +2467,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Press RMB again, in a different location.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 2,
|
||||
clientX: 20.0,
|
||||
@ -2505,7 +2505,7 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press and hold LMB.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 0,
|
||||
buttons: 1,
|
||||
clientX: 5.0,
|
||||
@ -2524,7 +2524,7 @@ void testMain() {
|
||||
|
||||
// Press and hold RMB. The pointer is already down, so we only send a move
|
||||
// to update the position of the pointer.
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
button: 2,
|
||||
buttons: 3,
|
||||
clientX: 20.0,
|
||||
@ -2540,7 +2540,7 @@ void testMain() {
|
||||
|
||||
// Release LMB. The pointer is still down (RMB), so we only send a move to
|
||||
// update the position of the pointer.
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 0,
|
||||
buttons: 2,
|
||||
clientX: 30.0,
|
||||
@ -2555,7 +2555,7 @@ void testMain() {
|
||||
packets.clear();
|
||||
|
||||
// Release RMB. There's no more buttons down, so we send an up event.
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
button: 2,
|
||||
buttons: 0,
|
||||
clientX: 30.0,
|
||||
@ -2574,7 +2574,7 @@ void testMain() {
|
||||
if (!isIosSafari) _PointerEventContext(),
|
||||
if (!isIosSafari) _MouseEventContext(),
|
||||
],
|
||||
'correctly detects up event outside of glasspane',
|
||||
'correctly detects up event outside of flutterViewElement',
|
||||
(_ButtonedEventMixin context) {
|
||||
PointerBinding.instance!.debugOverrideDetector(context);
|
||||
// This can happen when the up event occurs while the mouse is outside the
|
||||
@ -2586,26 +2586,26 @@ void testMain() {
|
||||
};
|
||||
|
||||
// Press and drag around.
|
||||
glassPane.dispatchEvent(context.primaryDown(
|
||||
flutterViewElement.dispatchEvent(context.primaryDown(
|
||||
clientX: 10.0,
|
||||
clientY: 10.0,
|
||||
));
|
||||
glassPane.dispatchEvent(context.primaryMove(
|
||||
flutterViewElement.dispatchEvent(context.primaryMove(
|
||||
clientX: 12.0,
|
||||
clientY: 10.0,
|
||||
));
|
||||
glassPane.dispatchEvent(context.primaryMove(
|
||||
flutterViewElement.dispatchEvent(context.primaryMove(
|
||||
clientX: 15.0,
|
||||
clientY: 10.0,
|
||||
));
|
||||
glassPane.dispatchEvent(context.primaryMove(
|
||||
flutterViewElement.dispatchEvent(context.primaryMove(
|
||||
clientX: 20.0,
|
||||
clientY: 10.0,
|
||||
));
|
||||
packets.clear();
|
||||
|
||||
// Move outside the glasspane.
|
||||
glassPane.dispatchEvent(context.primaryMove(
|
||||
// Move outside the flutterViewElement.
|
||||
flutterViewElement.dispatchEvent(context.primaryMove(
|
||||
clientX: 900.0,
|
||||
clientY: 1900.0,
|
||||
));
|
||||
@ -2616,8 +2616,8 @@ void testMain() {
|
||||
expect(packets[0].data[0].physicalY, equals(1900.0 * dpi));
|
||||
packets.clear();
|
||||
|
||||
// Release outside the glasspane.
|
||||
glassPane.dispatchEvent(context.primaryUp(
|
||||
// Release outside the flutterViewElement.
|
||||
flutterViewElement.dispatchEvent(context.primaryUp(
|
||||
clientX: 1000.0,
|
||||
clientY: 2000.0,
|
||||
));
|
||||
@ -2653,7 +2653,7 @@ void testMain() {
|
||||
context.multiTouchDown(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 2, clientX: 100, clientY: 101),
|
||||
_TouchDetails(pointer: 3, clientX: 200, clientY: 201),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
if (context.runtimeType == _PointerEventContext) {
|
||||
expect(packets.length, 2);
|
||||
expect(packets[0].data.length, 2);
|
||||
@ -2700,7 +2700,7 @@ void testMain() {
|
||||
context.multiTouchMove(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 3, clientX: 300, clientY: 302),
|
||||
_TouchDetails(pointer: 2, clientX: 400, clientY: 402),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
if (context.runtimeType == _PointerEventContext) {
|
||||
expect(packets.length, 2);
|
||||
expect(packets[0].data.length, 1);
|
||||
@ -2734,7 +2734,7 @@ void testMain() {
|
||||
// One pointer up
|
||||
context.multiTouchUp(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 3, clientX: 300, clientY: 302),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.up));
|
||||
@ -2757,7 +2757,7 @@ void testMain() {
|
||||
// Another pointer up
|
||||
context.multiTouchUp(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 2, clientX: 400, clientY: 402),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.up));
|
||||
@ -2781,7 +2781,7 @@ void testMain() {
|
||||
context.multiTouchDown(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 3, clientX: 500, clientY: 501),
|
||||
_TouchDetails(pointer: 2, clientX: 600, clientY: 601),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
if (context.runtimeType == _PointerEventContext) {
|
||||
expect(packets.length, 2);
|
||||
expect(packets[0].data.length, 2);
|
||||
@ -2843,13 +2843,13 @@ void testMain() {
|
||||
context.multiTouchDown(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 2, clientX: 100, clientY: 101),
|
||||
_TouchDetails(pointer: 3, clientX: 200, clientY: 201),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
packets.clear(); // Down event is tested in other tests.
|
||||
|
||||
// One pointer cancel
|
||||
context.multiTouchCancel(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 3, clientX: 300, clientY: 302),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets.length, 1);
|
||||
expect(packets[0].data.length, 2);
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.cancel));
|
||||
@ -2887,7 +2887,7 @@ void testMain() {
|
||||
|
||||
context.multiTouchDown(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 1, clientX: 100, clientY: 101),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(1));
|
||||
// An add will be synthesized.
|
||||
expect(packets[0].data, hasLength(2));
|
||||
@ -2900,7 +2900,7 @@ void testMain() {
|
||||
|
||||
context.multiTouchDown(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 2, clientX: 200, clientY: 202),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
// An add will be synthesized.
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets[0].data, hasLength(2));
|
||||
@ -2927,12 +2927,12 @@ void testMain() {
|
||||
|
||||
context.multiTouchUp(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 23, clientX: 200, clientY: 202),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(0));
|
||||
|
||||
context.multiTouchCancel(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 24, clientX: 200, clientY: 202),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(0));
|
||||
},
|
||||
);
|
||||
@ -2955,7 +2955,7 @@ void testMain() {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(context.mouseDown(
|
||||
flutterViewElement.dispatchEvent(context.mouseDown(
|
||||
pointerId: 12,
|
||||
button: 0,
|
||||
buttons: 1,
|
||||
@ -2973,7 +2973,7 @@ void testMain() {
|
||||
|
||||
expect(
|
||||
() {
|
||||
glassPane.dispatchEvent(context.mouseUp(
|
||||
flutterViewElement.dispatchEvent(context.mouseUp(
|
||||
pointerId: 41,
|
||||
button: 0,
|
||||
buttons: 0,
|
||||
@ -3008,7 +3008,7 @@ void testMain() {
|
||||
|
||||
context.multiTouchDown(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 1, clientX: 20, clientY: 20),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.add));
|
||||
@ -3030,7 +3030,7 @@ void testMain() {
|
||||
|
||||
context.multiTouchMove(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 1, clientX: 40, clientY: 30),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets[0].data, hasLength(1));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.move));
|
||||
@ -3044,7 +3044,7 @@ void testMain() {
|
||||
|
||||
context.multiTouchUp(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 1, clientX: 40, clientY: 30),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.up));
|
||||
@ -3066,7 +3066,7 @@ void testMain() {
|
||||
|
||||
context.multiTouchDown(const <_TouchDetails>[
|
||||
_TouchDetails(pointer: 2, clientX: 20, clientY: 10),
|
||||
]).forEach(glassPane.dispatchEvent);
|
||||
]).forEach(flutterViewElement.dispatchEvent);
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.add));
|
||||
|
||||
@ -157,18 +157,16 @@ void _testEngineSemanticsOwner() {
|
||||
expect(semantics().semanticsEnabled, isFalse);
|
||||
|
||||
// Synthesize a click on the placeholder.
|
||||
final DomElement placeholder =
|
||||
appHostNode.querySelector('flt-semantics-placeholder')!;
|
||||
final DomElement placeholder = flutterViewEmbedder.glassPaneShadow
|
||||
.querySelector('flt-semantics-placeholder')!;
|
||||
|
||||
expect(placeholder.isConnected, isTrue);
|
||||
|
||||
final DomRect rect = placeholder.getBoundingClientRect();
|
||||
placeholder.dispatchEvent(createDomMouseEvent(
|
||||
'click', <Object?, Object?>{
|
||||
'clientX': (rect.left + (rect.right - rect.left) / 2).floor(),
|
||||
'clientY': (rect.top + (rect.bottom - rect.top) / 2).floor(),
|
||||
}
|
||||
));
|
||||
placeholder.dispatchEvent(createDomMouseEvent('click', <Object?, Object?>{
|
||||
'clientX': (rect.left + (rect.right - rect.left) / 2).floor(),
|
||||
'clientY': (rect.top + (rect.bottom - rect.top) / 2).floor(),
|
||||
}));
|
||||
|
||||
// On mobile semantics is enabled asynchronously.
|
||||
if (isMobile) {
|
||||
@ -182,7 +180,8 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
test('accessibilityFeatures copyWith function works', () {
|
||||
const EngineAccessibilityFeatures original = EngineAccessibilityFeatures(0);
|
||||
EngineAccessibilityFeatures copy = original.copyWith(accessibleNavigation: true);
|
||||
EngineAccessibilityFeatures copy =
|
||||
original.copyWith(accessibleNavigation: true);
|
||||
expect(copy.accessibleNavigation, true);
|
||||
expect(copy.boldText, false);
|
||||
expect(copy.disableAnimations, false);
|
||||
@ -254,8 +253,8 @@ void _testEngineSemanticsOwner() {
|
||||
.instance.accessibilityFeatures.accessibleNavigation,
|
||||
isFalse);
|
||||
|
||||
final DomElement placeholder =
|
||||
appHostNode.querySelector('flt-semantics-placeholder')!;
|
||||
final DomElement placeholder = flutterViewEmbedder.glassPaneShadow
|
||||
.querySelector('flt-semantics-placeholder')!;
|
||||
|
||||
expect(placeholder.isConnected, isTrue);
|
||||
|
||||
@ -428,7 +427,8 @@ void _testEngineSemanticsOwner() {
|
||||
);
|
||||
});
|
||||
|
||||
test('forwards events to framework if shouldEnableSemantics returns true', () {
|
||||
test('forwards events to framework if shouldEnableSemantics returns true',
|
||||
() {
|
||||
final MockSemanticsEnabler mockSemanticsEnabler = MockSemanticsEnabler();
|
||||
semantics().semanticsHelper.semanticsEnabler = mockSemanticsEnabler;
|
||||
final DomEvent pointerEvent = createDomEvent('Event', 'pointermove');
|
||||
@ -439,8 +439,7 @@ void _testEngineSemanticsOwner() {
|
||||
|
||||
class MockSemanticsEnabler implements SemanticsEnabler {
|
||||
@override
|
||||
void dispose() {
|
||||
}
|
||||
void dispose() {}
|
||||
|
||||
@override
|
||||
bool get isWaitingToEnableSemantics => throw UnimplementedError();
|
||||
@ -716,7 +715,8 @@ void _testContainer() {
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('renders in traversal order, hit-tests in reverse z-index order', () async {
|
||||
test('renders in traversal order, hit-tests in reverse z-index order',
|
||||
() async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
@ -809,7 +809,9 @@ void _testContainer() {
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
|
||||
test('container nodes are transparent and leaf children are opaque hit-test wise', () async {
|
||||
test(
|
||||
'container nodes are transparent and leaf children are opaque hit-test wise',
|
||||
() async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
@ -835,10 +837,12 @@ void _testContainer() {
|
||||
final DomElement root = appHostNode.querySelector('#flt-semantic-node-0')!;
|
||||
expect(root.style.pointerEvents, 'none');
|
||||
|
||||
final DomElement child1 = appHostNode.querySelector('#flt-semantic-node-1')!;
|
||||
final DomElement child1 =
|
||||
appHostNode.querySelector('#flt-semantic-node-1')!;
|
||||
expect(child1.style.pointerEvents, 'all');
|
||||
|
||||
final DomElement child2 = appHostNode.querySelector('#flt-semantic-node-2')!;
|
||||
final DomElement child2 =
|
||||
appHostNode.querySelector('#flt-semantic-node-2')!;
|
||||
expect(child2.style.pointerEvents, 'all');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
@ -1179,8 +1183,8 @@ void _testIncrementables() {
|
||||
<input aria-valuenow="1" aria-valuetext="d" aria-valuemax="2" aria-valuemin="1">
|
||||
</sem>''');
|
||||
|
||||
final DomHTMLInputElement input = appHostNode.querySelector('input')! as
|
||||
DomHTMLInputElement;
|
||||
final DomHTMLInputElement input =
|
||||
appHostNode.querySelector('input')! as DomHTMLInputElement;
|
||||
input.value = '2';
|
||||
input.dispatchEvent(createDomEvent('Event', 'change'));
|
||||
|
||||
@ -1212,8 +1216,8 @@ void _testIncrementables() {
|
||||
<input aria-valuenow="1" aria-valuetext="d" aria-valuemax="1" aria-valuemin="0">
|
||||
</sem>''');
|
||||
|
||||
final DomHTMLInputElement input = appHostNode.querySelector('input')! as
|
||||
DomHTMLInputElement;
|
||||
final DomHTMLInputElement input =
|
||||
appHostNode.querySelector('input')! as DomHTMLInputElement;
|
||||
input.value = '0';
|
||||
input.dispatchEvent(createDomEvent('Event', 'change'));
|
||||
|
||||
@ -1299,11 +1303,11 @@ void _testTextField() {
|
||||
final DomElement textField =
|
||||
appHostNode.querySelector('input[data-semantics-role="text-field"]')!;
|
||||
|
||||
expect(appHostNode.activeElement, isNot(textField));
|
||||
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
|
||||
|
||||
textField.focus();
|
||||
|
||||
expect(appHostNode.activeElement, textField);
|
||||
expect(appHostNode.ownerDocument?.activeElement, textField);
|
||||
expect(await logger.idLog.first, 0);
|
||||
expect(await logger.actionLog.first, ui.SemanticsAction.tap);
|
||||
|
||||
@ -1616,13 +1620,15 @@ void _testTappable() {
|
||||
}
|
||||
|
||||
updateTappable(enabled: false);
|
||||
expectSemanticsTree('<sem role="button" aria-disabled="true" style="$rootSemanticStyle"></sem>');
|
||||
expectSemanticsTree(
|
||||
'<sem role="button" aria-disabled="true" style="$rootSemanticStyle"></sem>');
|
||||
|
||||
updateTappable(enabled: true);
|
||||
expectSemanticsTree('<sem role="button" style="$rootSemanticStyle"></sem>');
|
||||
|
||||
updateTappable(enabled: false);
|
||||
expectSemanticsTree('<sem role="button" aria-disabled="true" style="$rootSemanticStyle"></sem>');
|
||||
expectSemanticsTree(
|
||||
'<sem role="button" aria-disabled="true" style="$rootSemanticStyle"></sem>');
|
||||
|
||||
updateTappable(enabled: true);
|
||||
expectSemanticsTree('<sem role="button" style="$rootSemanticStyle"></sem>');
|
||||
@ -1647,7 +1653,7 @@ void _testTappable() {
|
||||
);
|
||||
tester.apply();
|
||||
|
||||
expect(flutterViewEmbedder.glassPaneShadow.activeElement, tester.getSemanticsObject(0).element);
|
||||
expect(domDocument.activeElement, tester.getSemanticsObject(0).element);
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
}
|
||||
@ -1942,13 +1948,13 @@ void _testPlatformView() {
|
||||
ui.window.render(sceneBuilder.build());
|
||||
|
||||
final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
|
||||
updateNode(
|
||||
builder,
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 20, 60),
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1, 2, 3]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1, 2, 3]),
|
||||
transform: Float64List.fromList(Matrix4.diagonal3Values(ui.window.devicePixelRatio, ui.window.devicePixelRatio, 1).storage)
|
||||
);
|
||||
updateNode(builder,
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 20, 60),
|
||||
childrenInTraversalOrder: Int32List.fromList(<int>[1, 2, 3]),
|
||||
childrenInHitTestOrder: Int32List.fromList(<int>[1, 2, 3]),
|
||||
transform: Float64List.fromList(Matrix4.diagonal3Values(
|
||||
ui.window.devicePixelRatio, ui.window.devicePixelRatio, 1)
|
||||
.storage));
|
||||
updateNode(
|
||||
builder,
|
||||
id: 1,
|
||||
@ -2009,22 +2015,19 @@ void _testPlatformView() {
|
||||
|
||||
final DomElement platformViewElement =
|
||||
flutterViewEmbedder.glassPaneElement.querySelector('#view-0')!;
|
||||
final DomRect platformViewRect = platformViewElement.getBoundingClientRect();
|
||||
final DomRect platformViewRect =
|
||||
platformViewElement.getBoundingClientRect();
|
||||
expect(platformViewRect.left, 0);
|
||||
expect(platformViewRect.top, 15);
|
||||
expect(platformViewRect.right, 20);
|
||||
expect(platformViewRect.bottom, 45);
|
||||
|
||||
// This test is only relevant for shadow DOM because we only really support
|
||||
// proper platform view embedding in browsers that support shadow DOM.
|
||||
final DomShadowRoot shadowRoot = appHostNode.node as DomShadowRoot;
|
||||
|
||||
// Hit test child 1
|
||||
expect(shadowRoot.elementFromPoint(10, 10), child1);
|
||||
expect(domDocument.elementFromPoint(10, 10), child1);
|
||||
|
||||
// Hit test overlap between child 1 and 2
|
||||
// TODO(yjbanov): this is a known limitation, see https://github.com/flutter/flutter/issues/101439
|
||||
expect(shadowRoot.elementFromPoint(10, 20), child1);
|
||||
expect(domDocument.elementFromPoint(10, 20), child1);
|
||||
|
||||
// Hit test child 2
|
||||
// Clicking at the location of the middle semantics node should allow the
|
||||
@ -2043,10 +2046,10 @@ void _testPlatformView() {
|
||||
expect(domDocument.elementFromPoint(10, 30), platformViewElement);
|
||||
|
||||
// Hit test overlap between child 2 and 3
|
||||
expect(shadowRoot.elementFromPoint(10, 40), child3);
|
||||
expect(domDocument.elementFromPoint(10, 40), child3);
|
||||
|
||||
// Hit test child 3
|
||||
expect(shadowRoot.elementFromPoint(10, 50), child3);
|
||||
expect(domDocument.elementFromPoint(10, 50), child3);
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
@ -2111,9 +2114,11 @@ void updateNode(
|
||||
String value = '',
|
||||
List<ui.StringAttribute> valueAttributes = const <ui.StringAttribute>[],
|
||||
String increasedValue = '',
|
||||
List<ui.StringAttribute> increasedValueAttributes = const <ui.StringAttribute>[],
|
||||
List<ui.StringAttribute> increasedValueAttributes =
|
||||
const <ui.StringAttribute>[],
|
||||
String decreasedValue = '',
|
||||
List<ui.StringAttribute> decreasedValueAttributes = const <ui.StringAttribute>[],
|
||||
List<ui.StringAttribute> decreasedValueAttributes =
|
||||
const <ui.StringAttribute>[],
|
||||
String tooltip = '',
|
||||
ui.TextDirection textDirection = ui.TextDirection.ltr,
|
||||
Float64List? transform,
|
||||
|
||||
@ -8,7 +8,6 @@ import 'dart:typed_data';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine/dom.dart';
|
||||
import 'package:ui/src/engine/embedder.dart';
|
||||
import 'package:ui/src/engine/host_node.dart';
|
||||
import 'package:ui/src/engine/semantics.dart';
|
||||
import 'package:ui/src/engine/util.dart';
|
||||
import 'package:ui/src/engine/vector_math.dart';
|
||||
@ -19,10 +18,11 @@ import '../../common/matchers.dart';
|
||||
/// Gets the DOM host where the Flutter app is being rendered.
|
||||
///
|
||||
/// This function returns the correct host for the flutter app under testing,
|
||||
/// so we don't have to hardcode domDocument across the test. (The host of a
|
||||
/// normal flutter app used to be domDocument, but now that the app is wrapped
|
||||
/// in a Shadow DOM, that's not the case anymore.)
|
||||
HostNode get appHostNode => flutterViewEmbedder.glassPaneShadow;
|
||||
/// so we don't have to hardcode domDocument across the test. The semantics
|
||||
/// tree has moved outside of the shadowDOM as a workaround for a password
|
||||
/// autofill bug on Chrome.
|
||||
/// Ref: https://github.com/flutter/flutter/issues/87735
|
||||
DomElement get appHostNode => flutterViewEmbedder.flutterViewElement;
|
||||
|
||||
/// CSS style applied to the root of the semantics tree.
|
||||
// TODO(yjbanov): this should be handled internally by [expectSemanticsTree].
|
||||
|
||||
@ -102,11 +102,11 @@ void testMain() {
|
||||
final DomElement textField = appHostNode
|
||||
.querySelector('input[data-semantics-role="text-field"]')!;
|
||||
|
||||
expect(appHostNode.activeElement, isNot(textField));
|
||||
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
|
||||
|
||||
textField.focus();
|
||||
|
||||
expect(appHostNode.activeElement, textField);
|
||||
expect(appHostNode.ownerDocument?.activeElement, textField);
|
||||
expect(await logger.idLog.first, 0);
|
||||
expect(await logger.actionLog.first, ui.SemanticsAction.tap);
|
||||
}, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638
|
||||
@ -115,8 +115,7 @@ void testMain() {
|
||||
skip: browserEngine != BrowserEngine.blink);
|
||||
|
||||
test('Syncs semantic state from framework', () {
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
|
||||
int changeCount = 0;
|
||||
int actionCount = 0;
|
||||
@ -140,8 +139,7 @@ void testMain() {
|
||||
|
||||
final TextField textField =
|
||||
textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField;
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
expect(textField.editableElement, strategy.domElement);
|
||||
expect(textField.activeEditableElement.getAttribute('aria-label'), 'greeting');
|
||||
expect(textField.activeEditableElement.style.width, '10px');
|
||||
@ -154,8 +152,7 @@ void testMain() {
|
||||
rect: const ui.Rect.fromLTWH(0, 0, 12, 17),
|
||||
);
|
||||
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
expect(strategy.domElement, null);
|
||||
expect(textField.activeEditableElement.getAttribute('aria-label'), 'farewell');
|
||||
expect(textField.activeEditableElement.style.width, '12px');
|
||||
@ -201,8 +198,7 @@ void testMain() {
|
||||
test(
|
||||
'Updates editing state when receiving framework messages from the text input channel',
|
||||
() {
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
|
||||
strategy.enable(
|
||||
singlelineConfig,
|
||||
@ -246,8 +242,7 @@ void testMain() {
|
||||
});
|
||||
|
||||
test('Gives up focus after DOM blur', () {
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
|
||||
strategy.enable(
|
||||
singlelineConfig,
|
||||
@ -262,13 +257,11 @@ void testMain() {
|
||||
final TextField textField =
|
||||
textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField;
|
||||
expect(textField.editableElement, strategy.domElement);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
// The input should not refocus after blur.
|
||||
textField.activeEditableElement.blur();
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
strategy.disable();
|
||||
});
|
||||
|
||||
@ -288,8 +281,7 @@ void testMain() {
|
||||
isFocused: true,
|
||||
);
|
||||
expect(strategy.domElement, isNotNull);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
strategy.disable();
|
||||
expect(strategy.domElement, isNull);
|
||||
@ -300,8 +292,7 @@ void testMain() {
|
||||
expect(appHostNode.contains(textField.editableElement), isTrue);
|
||||
// Editing element is not enabled.
|
||||
expect(strategy.isEnabled, isFalse);
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
});
|
||||
|
||||
test('Refocuses when setting editing state', () {
|
||||
@ -316,13 +307,11 @@ void testMain() {
|
||||
isFocused: true,
|
||||
);
|
||||
expect(strategy.domElement, isNotNull);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
// Blur the element without telling the framework.
|
||||
strategy.activeDomElement.blur();
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
|
||||
// The input will have focus after editing state is set and semantics updated.
|
||||
strategy.setEditingState(EditingState(text: 'foo'));
|
||||
@ -340,8 +329,7 @@ void testMain() {
|
||||
value: 'hello',
|
||||
isFocused: true,
|
||||
);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
strategy.disable();
|
||||
});
|
||||
@ -361,8 +349,7 @@ void testMain() {
|
||||
final DomHTMLTextAreaElement textArea =
|
||||
strategy.domElement! as DomHTMLTextAreaElement;
|
||||
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
strategy.enable(
|
||||
singlelineConfig,
|
||||
@ -371,8 +358,7 @@ void testMain() {
|
||||
);
|
||||
|
||||
textArea.blur();
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
|
||||
strategy.disable();
|
||||
// It doesn't remove the textarea from the DOM.
|
||||
@ -456,13 +442,14 @@ void testMain() {
|
||||
createTwoFieldSemantics(tester, focusFieldId: 1);
|
||||
expect(tester.apply().length, 3);
|
||||
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, tester.getTextField(1).editableElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement,
|
||||
tester.getTextField(1).editableElement);
|
||||
expect(strategy.domElement, tester.getTextField(1).editableElement);
|
||||
|
||||
createTwoFieldSemantics(tester, focusFieldId: 2);
|
||||
expect(tester.apply().length, 3);
|
||||
expect(appHostNode.activeElement, tester.getTextField(2).editableElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement,
|
||||
tester.getTextField(2).editableElement);
|
||||
expect(strategy.domElement, tester.getTextField(2).editableElement);
|
||||
}
|
||||
});
|
||||
@ -510,8 +497,7 @@ void testMain() {
|
||||
});
|
||||
|
||||
test('Syncs semantic state from framework', () {
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
|
||||
int changeCount = 0;
|
||||
int actionCount = 0;
|
||||
@ -535,8 +521,7 @@ void testMain() {
|
||||
final TextField textField =
|
||||
textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField;
|
||||
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
expect(textField.editableElement, strategy.domElement);
|
||||
expect(textField.activeEditableElement.getAttribute('aria-label'), 'greeting');
|
||||
expect(textField.activeEditableElement.style.width, '10px');
|
||||
@ -552,8 +537,7 @@ void testMain() {
|
||||
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
|
||||
|
||||
expect(strategy.domElement, null);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, textBox);
|
||||
expect(appHostNode.ownerDocument?.activeElement, textBox);
|
||||
expect(textBox.getAttribute('aria-label'), 'farewell');
|
||||
|
||||
strategy.disable();
|
||||
@ -596,8 +580,7 @@ void testMain() {
|
||||
test(
|
||||
'Updates editing state when receiving framework messages from the text input channel',
|
||||
() {
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
|
||||
strategy.enable(
|
||||
singlelineConfig,
|
||||
@ -641,8 +624,7 @@ void testMain() {
|
||||
});
|
||||
|
||||
test('Gives up focus after DOM blur', () {
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(appHostNode.activeElement, null);
|
||||
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
|
||||
|
||||
strategy.enable(
|
||||
singlelineConfig,
|
||||
@ -657,15 +639,13 @@ void testMain() {
|
||||
textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField;
|
||||
|
||||
expect(textField.editableElement, strategy.domElement);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
// The input should not refocus after blur.
|
||||
textField.activeEditableElement.blur();
|
||||
final DomElement textBox =
|
||||
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, textBox);
|
||||
expect(appHostNode.ownerDocument?.activeElement, textBox);
|
||||
|
||||
strategy.disable();
|
||||
});
|
||||
@ -686,8 +666,7 @@ void testMain() {
|
||||
isFocused: true,
|
||||
);
|
||||
expect(strategy.domElement, isNotNull);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
strategy.disable();
|
||||
expect(strategy.domElement, isNull);
|
||||
@ -700,8 +679,7 @@ void testMain() {
|
||||
// Focus is on the semantic object
|
||||
final DomElement textBox =
|
||||
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, textBox);
|
||||
expect(appHostNode.ownerDocument?.activeElement, textBox);
|
||||
});
|
||||
|
||||
test('Refocuses when setting editing state', () {
|
||||
@ -716,15 +694,13 @@ void testMain() {
|
||||
isFocused: true,
|
||||
);
|
||||
expect(strategy.domElement, isNotNull);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
// Blur the element without telling the framework.
|
||||
strategy.activeDomElement.blur();
|
||||
final DomElement textBox =
|
||||
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, textBox);
|
||||
expect(appHostNode.ownerDocument?.activeElement, textBox);
|
||||
|
||||
// The input will have focus after editing state is set and semantics updated.
|
||||
strategy.setEditingState(EditingState(text: 'foo'));
|
||||
@ -742,8 +718,7 @@ void testMain() {
|
||||
value: 'hello',
|
||||
isFocused: true,
|
||||
);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
strategy.disable();
|
||||
});
|
||||
@ -760,9 +735,9 @@ void testMain() {
|
||||
isMultiline: true,
|
||||
);
|
||||
|
||||
final DomHTMLTextAreaElement textArea = strategy.domElement! as DomHTMLTextAreaElement;
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, strategy.domElement);
|
||||
final DomHTMLTextAreaElement textArea =
|
||||
strategy.domElement! as DomHTMLTextAreaElement;
|
||||
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
|
||||
|
||||
strategy.enable(
|
||||
singlelineConfig,
|
||||
@ -776,8 +751,7 @@ void testMain() {
|
||||
final DomElement textBox =
|
||||
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
|
||||
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, textBox);
|
||||
expect(appHostNode.ownerDocument?.activeElement, textBox);
|
||||
|
||||
strategy.disable();
|
||||
// It removes the textarea from the DOM.
|
||||
@ -840,13 +814,14 @@ void testMain() {
|
||||
createTwoFieldSemanticsForIos(tester, focusFieldId: 1);
|
||||
|
||||
expect(tester.apply().length, 3);
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(appHostNode.activeElement, tester.getTextField(1).editableElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement,
|
||||
tester.getTextField(1).editableElement);
|
||||
expect(strategy.domElement, tester.getTextField(1).editableElement);
|
||||
|
||||
createTwoFieldSemanticsForIos(tester, focusFieldId: 2);
|
||||
expect(tester.apply().length, 3);
|
||||
expect(appHostNode.activeElement, tester.getTextField(2).editableElement);
|
||||
expect(appHostNode.ownerDocument?.activeElement,
|
||||
tester.getTextField(2).editableElement);
|
||||
expect(strategy.domElement, tester.getTextField(2).editableElement);
|
||||
}
|
||||
});
|
||||
|
||||
@ -90,7 +90,8 @@ Future<void> testMain() async {
|
||||
);
|
||||
// The focus initially is on the body.
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(defaultTextEditingRoot.activeElement, null);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
domDocument.body);
|
||||
|
||||
editingStrategy!.enable(
|
||||
singlelineConfig,
|
||||
@ -105,8 +106,8 @@ Future<void> testMain() async {
|
||||
final DomElement input = defaultTextEditingRoot.querySelector('input')!;
|
||||
// Now the editing element should have focus.
|
||||
|
||||
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
|
||||
expect(defaultTextEditingRoot.activeElement, input);
|
||||
expect(domDocument.activeElement, input);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement, input);
|
||||
|
||||
expect(editingStrategy!.domElement, input);
|
||||
expect(input.getAttribute('type'), null);
|
||||
@ -121,7 +122,8 @@ Future<void> testMain() async {
|
||||
);
|
||||
// The focus is back to the body.
|
||||
expect(domDocument.activeElement, domDocument.body);
|
||||
expect(defaultTextEditingRoot.activeElement, null);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
domDocument.body);
|
||||
});
|
||||
|
||||
test('Respects read-only config', () {
|
||||
@ -280,7 +282,7 @@ Future<void> testMain() async {
|
||||
final DomHTMLTextAreaElement textarea =
|
||||
defaultTextEditingRoot.querySelector('textarea')! as DomHTMLTextAreaElement;
|
||||
// Now the textarea should have focus.
|
||||
expect(defaultTextEditingRoot.activeElement, textarea);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement, textarea);
|
||||
expect(editingStrategy!.domElement, textarea);
|
||||
|
||||
textarea.value = 'foo\nbar';
|
||||
@ -302,7 +304,8 @@ Future<void> testMain() async {
|
||||
// The textarea should be cleaned up.
|
||||
expect(defaultTextEditingRoot.querySelectorAll('textarea'), hasLength(0));
|
||||
// The focus is back to the body.
|
||||
expect(defaultTextEditingRoot.activeElement, null);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
domDocument.body);
|
||||
|
||||
// There should be no input action.
|
||||
expect(lastInputAction, isNull);
|
||||
@ -619,7 +622,7 @@ Future<void> testMain() async {
|
||||
const MethodCall show = MethodCall('TextInput.show');
|
||||
sendFrameworkMessage(codec.encodeMethodCall(show));
|
||||
|
||||
expect(defaultTextEditingRoot.activeElement,
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
textEditing!.strategy.domElement);
|
||||
});
|
||||
|
||||
@ -679,7 +682,8 @@ Future<void> testMain() async {
|
||||
sendFrameworkMessage(codec.encodeMethodCall(setEditingState));
|
||||
|
||||
// Editing shouldn't have started yet.
|
||||
expect(defaultTextEditingRoot.activeElement, null);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
domDocument.body);
|
||||
|
||||
const MethodCall show = MethodCall('TextInput.show');
|
||||
sendFrameworkMessage(codec.encodeMethodCall(show));
|
||||
@ -704,7 +708,7 @@ Future<void> testMain() async {
|
||||
expect(spy.messages, hasLength(0));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
// DOM element still keeps the focus.
|
||||
expect(defaultTextEditingRoot.activeElement,
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
textEditing!.strategy.domElement);
|
||||
});
|
||||
|
||||
@ -722,7 +726,8 @@ Future<void> testMain() async {
|
||||
sendFrameworkMessage(codec.encodeMethodCall(setEditingState));
|
||||
|
||||
// Editing shouldn't have started yet.
|
||||
expect(defaultTextEditingRoot.activeElement, null);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
domDocument.body);
|
||||
|
||||
const MethodCall show = MethodCall('TextInput.show');
|
||||
sendFrameworkMessage(codec.encodeMethodCall(show));
|
||||
@ -751,7 +756,8 @@ Future<void> testMain() async {
|
||||
spy.messages[0].methodName, 'TextInputClient.onConnectionClosed');
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
// DOM element loses the focus.
|
||||
expect(defaultTextEditingRoot.activeElement, null);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
domDocument.body);
|
||||
},
|
||||
// Test on ios-safari only.
|
||||
skip: browserEngine != BrowserEngine.webkit ||
|
||||
@ -772,7 +778,8 @@ Future<void> testMain() async {
|
||||
sendFrameworkMessage(codec.encodeMethodCall(setEditingState));
|
||||
|
||||
// Editing shouldn't have started yet.
|
||||
expect(defaultTextEditingRoot.activeElement, null);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
domDocument.body);
|
||||
|
||||
const MethodCall show = MethodCall('TextInput.show');
|
||||
sendFrameworkMessage(codec.encodeMethodCall(show));
|
||||
@ -1151,7 +1158,8 @@ Future<void> testMain() async {
|
||||
// In Safari Desktop Autofill menu appears as soon as an element is
|
||||
// focused, therefore the input element is only focused after the
|
||||
// location is received.
|
||||
expect(defaultTextEditingRoot.activeElement, inputElement);
|
||||
expect(
|
||||
defaultTextEditingRoot.ownerDocument?.activeElement, inputElement);
|
||||
expect(inputElement.selectionStart, 2);
|
||||
expect(inputElement.selectionEnd, 3);
|
||||
}
|
||||
@ -1164,7 +1172,7 @@ Future<void> testMain() async {
|
||||
sendFrameworkMessage(codec.encodeMethodCall(updateSizeAndTransform));
|
||||
|
||||
// Check the element still has focus. User can keep editing.
|
||||
expect(defaultTextEditingRoot.activeElement,
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
textEditing!.strategy.domElement);
|
||||
|
||||
// Check the cursor location is the same.
|
||||
@ -1764,7 +1772,8 @@ Future<void> testMain() async {
|
||||
sendFrameworkMessage(codec.encodeMethodCall(setClient));
|
||||
|
||||
// Editing shouldn't have started yet.
|
||||
expect(defaultTextEditingRoot.activeElement, null);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement,
|
||||
domDocument.body);
|
||||
|
||||
const MethodCall show = MethodCall('TextInput.show');
|
||||
sendFrameworkMessage(codec.encodeMethodCall(show));
|
||||
@ -2646,7 +2655,7 @@ void checkInputEditingState(
|
||||
expect(element, isNotNull);
|
||||
expect(domInstanceOfString(element, 'HTMLInputElement'), true);
|
||||
final DomHTMLInputElement input = element! as DomHTMLInputElement;
|
||||
expect(defaultTextEditingRoot.activeElement, input);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement, input);
|
||||
expect(input.value, text);
|
||||
expect(input.selectionStart, start);
|
||||
expect(input.selectionEnd, end);
|
||||
@ -2672,7 +2681,7 @@ void checkTextAreaEditingState(
|
||||
int start,
|
||||
int end,
|
||||
) {
|
||||
expect(defaultTextEditingRoot.activeElement, textarea);
|
||||
expect(defaultTextEditingRoot.ownerDocument?.activeElement, textarea);
|
||||
expect(textarea.value, text);
|
||||
expect(textarea.selectionStart, start);
|
||||
expect(textarea.selectionEnd, end);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user