[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:
htoor3 2023-04-11 14:41:14 -05:00 committed by GitHub
parent 8179d070b3
commit a5fdffa8c8
13 changed files with 335 additions and 308 deletions

View File

@ -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;
}

View File

@ -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,
);

View File

@ -67,7 +67,7 @@ class MouseCursor {
void activateSystemCursor(String? kind) {
setElementStyle(
flutterViewEmbedder.glassPaneElement,
flutterViewEmbedder.flutterViewElement,
'cursor',
_mapKindToCssValue(kind),
);

View File

@ -128,8 +128,9 @@ class PlatformViewManager {
}
_ensureContentCorrectlySized(content, viewType);
wrapper.append(content);
return wrapper..append(content);
return wrapper;
});
}

View File

@ -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,

View File

@ -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();

View File

@ -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);

View File

@ -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.

View File

@ -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));

View File

@ -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,

View File

@ -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].

View File

@ -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);
}
});

View File

@ -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);