[web] DomManager for each FlutterView (flutter/engine#47388)

The PR may seem large, but the main changes are simple:

- Introduce a `DomManager` that aims to take over all DOM responsibilities from `FlutterViewEmbedder`.
- Update all references to `flutterViewEmbedder.*domElement*` to `domManager.*domElement*`.
- Describe the general DOM structure of a Flutter View in a doc comment.

Next steps (in future PRs):
- Move all DOM manipulation methods out of `FlutterViewEmbedder` into `DomManager`.
- Move DOM creation logic out of `FlutterViewEmbedder` into `DomManager`.
This commit is contained in:
Mouad Debbar
2023-10-27 19:53:27 -04:00
committed by GitHub
parent a20662c91d
commit 57cc7e9136
19 changed files with 387 additions and 278 deletions

View File

@@ -3765,6 +3765,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/vector_math.dart + ../../../f
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/dimensions_provider.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/dom_manager.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/embedding_strategy.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart + ../../../flutter/LICENSE
@@ -6552,6 +6553,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/vector_math.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/dimensions_provider.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/dom_manager.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/embedding_strategy.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart

View File

@@ -187,6 +187,7 @@ export 'engine/vector_math.dart';
export 'engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart';
export 'engine/view_embedder/dimensions_provider/dimensions_provider.dart';
export 'engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart';
export 'engine/view_embedder/dom_manager.dart';
export 'engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart';
export 'engine/view_embedder/embedding_strategy/embedding_strategy.dart';
export 'engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart';

View File

@@ -75,8 +75,8 @@ class FlutterViewEmbedder {
///
/// This element is inserted after the [semanticsHostElement] so that
/// platform views take precedence in DOM event handling.
DomElement? get sceneHostElement => _sceneHostElement;
DomElement? _sceneHostElement;
DomElement get sceneHostElement => _sceneHostElement;
late DomElement _sceneHostElement;
/// A child element of body outside the shadowroot that hosts
/// global resources such svg filters and clip paths when using webkit.
@@ -95,8 +95,8 @@ class FlutterViewEmbedder {
///
/// This element is inserted before the [semanticsHostElement] so that
/// platform views take precedence in DOM event handling.
DomElement? get semanticsHostElement => _semanticsHostElement;
DomElement? _semanticsHostElement;
DomElement get semanticsHostElementDEPRECATED => _semanticsHostElement;
late DomElement _semanticsHostElement;
/// The last scene element rendered by the [render] method.
DomElement? get sceneElement => _sceneElement;
@@ -110,7 +110,7 @@ class FlutterViewEmbedder {
if (sceneElement != _sceneElement) {
_sceneElement?.remove();
_sceneElement = sceneElement;
_sceneHostElement!.append(sceneElement!);
_sceneHostElement.append(sceneElement!);
}
}
@@ -120,17 +120,17 @@ 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;
DomElement get flutterViewElementDEPRECATED => _flutterViewElement;
late DomElement _flutterViewElement;
DomElement get glassPaneElement => _glassPaneElement;
DomElement get glassPaneElementDEPRECATED => _glassPaneElement;
late DomElement _glassPaneElement;
/// The shadow root of the [glassPaneElement], which contains the whole Flutter app.
DomShadowRoot get glassPaneShadow => _glassPaneShadow;
DomShadowRoot get glassPaneShadowDEPRECATED => _glassPaneShadow;
late DomShadowRoot _glassPaneShadow;
DomElement get textEditingHostNode => _textEditingHostNode;
DomElement get textEditingHostNodeDEPRECATED => _textEditingHostNode;
late DomElement _textEditingHostNode;
AccessibilityAnnouncements get accessibilityAnnouncements => _accessibilityAnnouncements;
@@ -169,16 +169,16 @@ class FlutterViewEmbedder {
//
// The embeddingStrategy will take care of cleaning up the glassPane on hot
// restart.
_embeddingStrategy.attachGlassPane(flutterViewElement);
flutterViewElement.appendChild(glassPaneElement);
_embeddingStrategy.attachGlassPane(_flutterViewElement);
_flutterViewElement.appendChild(_glassPaneElement);
if (getJsProperty<Object?>(glassPaneElement, 'attachShadow') == null) {
if (getJsProperty<Object?>(_glassPaneElement, 'attachShadow') == null) {
throw UnsupportedError('ShadowDOM is not supported in this browser.');
}
// Create a [HostNode] under the glass pane element, and attach everything
// there, instead of directly underneath the glass panel.
final DomShadowRoot shadowRoot = glassPaneElement.attachShadow(<String, dynamic>{
final DomShadowRoot shadowRoot = _glassPaneElement.attachShadow(<String, dynamic>{
'mode': 'open',
// This needs to stay false to prevent issues like this:
// - https://github.com/flutter/flutter/issues/85759
@@ -196,7 +196,7 @@ class FlutterViewEmbedder {
);
_textEditingHostNode =
createTextEditingHostNode(flutterViewElement, defaultCssFont, configuration.nonce);
createTextEditingHostNode(_flutterViewElement, defaultCssFont, configuration.nonce);
// Don't allow the scene to receive pointer events.
_sceneHostElement = domDocument.createElement('flt-scene-host')
@@ -204,12 +204,10 @@ class FlutterViewEmbedder {
renderer.reset(this);
final DomElement semanticsHostElement =
domDocument.createElement('flt-semantics-host');
semanticsHostElement.style
_semanticsHostElement = domDocument.createElement('flt-semantics-host');
_semanticsHostElement.style
..position = 'absolute'
..transformOrigin = '0 0 0';
_semanticsHostElement = semanticsHostElement;
updateSemanticsScreenProperties();
final DomElement accessibilityPlaceholder = EngineSemanticsOwner
@@ -220,7 +218,7 @@ class FlutterViewEmbedder {
_accessibilityAnnouncements = AccessibilityAnnouncements(hostElement: announcementsElement);
shadowRoot.append(accessibilityPlaceholder);
shadowRoot.append(_sceneHostElement!);
shadowRoot.append(_sceneHostElement);
shadowRoot.append(announcementsElement);
// The semantic host goes last because hit-test order-wise it must be
@@ -233,17 +231,17 @@ class FlutterViewEmbedder {
// 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);
_flutterViewElement.appendChild(_semanticsHostElement);
// When debugging semantics, make the scene semi-transparent so that the
// semantics tree is more prominent.
if (configuration.debugShowSemanticsNodes) {
_sceneHostElement!.style.opacity = '0.3';
_sceneHostElement.style.opacity = '0.3';
}
KeyboardBinding.initInstance();
PointerBinding.initInstance(
flutterViewElement,
_flutterViewElement,
KeyboardBinding.instance!.converter,
);
@@ -259,7 +257,7 @@ class FlutterViewEmbedder {
/// logical pixels. To compensate, an inverse scale is injected at the root
/// level.
void updateSemanticsScreenProperties() {
_semanticsHostElement!.style
_semanticsHostElement.style
.setProperty('transform', 'scale(${1 / window.devicePixelRatio})');
}
@@ -301,9 +299,9 @@ class FlutterViewEmbedder {
if (isWebKit) {
// The resourcesHost *must* be a sibling of the glassPaneElement.
_embeddingStrategy.attachResourcesHost(resourcesHost,
nextTo: flutterViewElement);
nextTo: _flutterViewElement);
} else {
glassPaneShadow.insertBefore(resourcesHost, glassPaneShadow.firstChild);
_glassPaneShadow.insertBefore(resourcesHost, _glassPaneShadow.firstChild);
}
_resourcesHost = resourcesHost;
}

View File

@@ -4,13 +4,13 @@
import 'dart:typed_data';
import 'package:ui/src/engine/embedder.dart';
import 'package:ui/src/engine/text_editing/text_editing.dart';
import 'package:ui/src/engine/vector_math.dart';
import 'package:ui/ui.dart' as ui show Offset;
import '../dom.dart';
import '../platform_dispatcher.dart';
import '../semantics.dart' show EngineSemanticsOwner;
import '../text_editing/text_editing.dart';
import '../vector_math.dart';
/// Returns an [ui.Offset] of the position of [event], relative to the position of [actualTarget].
///
@@ -30,7 +30,10 @@ ui.Offset computeEventOffsetToTarget(DomMouseEvent event, DomElement actualTarge
}
// On one of our text-editing nodes
final bool isInput = flutterViewEmbedder.textEditingHostNode.contains(event.target! as DomNode);
// TODO(mdebbar): There could be multiple views with multiple text editing hosts.
// https://github.com/flutter/flutter/issues/137344
final DomElement textEditingHost = EnginePlatformDispatcher.instance.implicitView!.dom.textEditingHost;
final bool isInput = textEditingHost.contains(event.target! as DomNode);
if (isInput) {
final EditableTextGeometry? inputGeometry = textEditing.strategy.geometry;
if (inputGeometry != null) {

View File

@@ -14,7 +14,6 @@ import '../alarm_clock.dart';
import '../browser_detection.dart';
import '../configuration.dart';
import '../dom.dart';
import '../embedder.dart';
import '../platform_dispatcher.dart';
import '../util.dart';
import '../vector_math.dart';
@@ -2198,7 +2197,10 @@ class EngineSemanticsOwner {
if (_rootSemanticsElement == null) {
final SemanticsObject root = _semanticsTree[0]!;
_rootSemanticsElement = root.element;
flutterViewEmbedder.semanticsHostElement!.append(root.element);
// TODO(mdebbar): There could be multiple views with multiple semantics hosts.
// https://github.com/flutter/flutter/issues/137344
final DomElement semanticsHost = EnginePlatformDispatcher.instance.implicitView!.dom.semanticsHost;
semanticsHost.append(root.element);
}
_finalizeTree();

View File

@@ -4,7 +4,8 @@
import '../../engine.dart' show registerHotRestartListener;
import '../dom.dart';
import '../embedder.dart';
import '../platform_dispatcher.dart';
import '../view_embedder/dom_manager.dart';
// TODO(yjbanov): this is a hack we use to compute ideographic baseline; this
// number is the ratio ideographic/alphabetic for font Ahem,
@@ -14,11 +15,9 @@ import '../embedder.dart';
// anything as of this writing.
const double baselineRatioHack = 1.1662499904632568;
/// Hosts ruler DOM elements in a hidden container under a `root` [DomNode].
///
/// The `root` [DomNode] is optional. Defaults to [flutterViewEmbedder.glassPaneShadow].
/// Hosts ruler DOM elements in a hidden container under [DomManager.renderingHost].
class RulerHost {
RulerHost({DomNode? root}) {
RulerHost() {
_rulerHost.style
..position = 'fixed'
..visibility = 'hidden'
@@ -28,11 +27,10 @@ class RulerHost {
..width = '0'
..height = '0';
if (root == null) {
flutterViewEmbedder.glassPaneShadow.appendChild(_rulerHost);
} else {
root.appendChild(_rulerHost);
}
// TODO(mdebbar): There could be multiple views with multiple rendering hosts.
// https://github.com/flutter/flutter/issues/137344
final DomNode renderingHost = EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost;
renderingHost.appendChild(_rulerHost);
registerHotRestartListener(dispose);
}

View File

@@ -11,7 +11,6 @@ import 'package:ui/ui.dart' as ui;
import '../browser_detection.dart';
import '../dom.dart';
import '../embedder.dart';
import '../mouse/prevent_default.dart';
import '../platform_dispatcher.dart';
import '../safe_browser_api.dart';
@@ -51,8 +50,9 @@ void _emptyCallback(dynamic _) {}
/// The default [HostNode] that hosts all DOM required for text editing when a11y is not enabled.
@visibleForTesting
DomElement get defaultTextEditingRoot =>
flutterViewEmbedder.textEditingHostNode;
// TODO(mdebbar): There could be multiple views with multiple text editing hosts.
// https://github.com/flutter/flutter/issues/137344
DomElement get defaultTextEditingRoot => EnginePlatformDispatcher.instance.implicitView!.dom.textEditingHost;
/// These style attributes are constant throughout the life time of an input
/// element.

View File

@@ -0,0 +1,66 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:ui/ui.dart' as ui;
import '../dom.dart';
import '../embedder.dart';
/// Manages DOM elements and the DOM structure for a [ui.FlutterView].
///
/// Here's the general DOM structure of a Flutter View:
///
/// [rootElement] <flutter-view>
/// |
/// +- [platformViewsHost] <flt-glass-pane>
/// | |
/// | +- [renderingHost] #shadow-root
/// | | |
/// | | +- <flt-semantics-placeholder>
/// | | |
/// | | +- <flt-scene-host>
/// | | | |
/// | | | +- <flt-scene>
/// | | |
/// | | +- <flt-announcement-host>
/// | |
/// | +- ...platform views
/// |
/// +- [textEditingHost] <text-editing-host>
/// | |
/// | +- ...text fields
/// |
/// +- [semanticsHost] <semantics-host>
/// |
/// +- ...semantics nodes
///
class DomManager {
DomManager.fromFlutterViewEmbedderDEPRECATED(this._embedder);
final FlutterViewEmbedder _embedder;
/// The root DOM element for the entire Flutter View.
///
/// This is where input events are captured, such as pointer events.
///
/// If semantics is enabled, this element also contains the semantics DOM tree,
/// which captures semantics input events.
DomElement get rootElement => _embedder.flutterViewElementDEPRECATED;
/// Hosts all platform view elements.
DomElement get platformViewsHost => _embedder.glassPaneElementDEPRECATED;
/// Hosts all rendering elements and canvases.
DomShadowRoot get renderingHost => _embedder.glassPaneShadowDEPRECATED;
/// Hosts all text editing elements.
DomElement get textEditingHost => _embedder.textEditingHostNodeDEPRECATED;
/// Hosts the semantics tree.
///
/// This element is in front of the [renderingHost] and [platformViewsHost].
/// Otherwise, the phone will disable focusing by touch, only by tabbing
/// around the UI.
DomElement get semanticsHost => _embedder.semanticsHostElementDEPRECATED;
}

View File

@@ -24,6 +24,7 @@ import 'platform_dispatcher.dart';
import 'platform_views/message_handler.dart';
import 'services.dart';
import 'util.dart';
import 'view_embedder/dom_manager.dart';
typedef _HandleMessageCallBack = Future<bool> Function();
@@ -39,9 +40,9 @@ const int kImplicitViewId = 0;
/// a few web-specific properties.
abstract interface class EngineFlutterView extends ui.FlutterView {
ContextMenu get contextMenu;
DomManager get dom;
MouseCursor get mouseCursor;
PlatformViewMessageHandler get platformViewMessageHandler;
DomElement get rootElement;
}
/// The Web implementation of [ui.SingletonFlutterWindow].
@@ -69,17 +70,18 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow implements EngineFlu
final EnginePlatformDispatcher platformDispatcher;
@override
late final MouseCursor mouseCursor = MouseCursor(rootElement);
late final MouseCursor mouseCursor = MouseCursor(dom.rootElement);
@override
late final ContextMenu contextMenu = ContextMenu(rootElement);
late final ContextMenu contextMenu = ContextMenu(dom.rootElement);
@override
DomElement get rootElement => flutterViewEmbedder.flutterViewElement;
late final DomManager dom =
DomManager.fromFlutterViewEmbedderDEPRECATED(flutterViewEmbedder);
@override
late final PlatformViewMessageHandler platformViewMessageHandler =
PlatformViewMessageHandler(platformViewsContainer: flutterViewEmbedder.glassPaneElement);
PlatformViewMessageHandler(platformViewsContainer: dom.platformViewsHost);
/// Handles the browser history integration to allow users to use the back
/// button, etc.

View File

@@ -13,6 +13,10 @@ import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
import 'common.dart';
import 'test_data.dart';
DomElement get platformViewsHost {
return EnginePlatformDispatcher.instance.implicitView!.dom.platformViewsHost;
}
void main() {
internalBootstrapBrowserTest(() => testMain);
}
@@ -41,8 +45,7 @@ void testMain() {
// The platform view is now split in two parts. The contents live
// as a child of the glassPane, and the slot lives in the glassPane
// shadow root. The slot is the one that has pointer events auto.
final DomElement contents =
flutterViewEmbedder.glassPaneElement.querySelector('#view-0')!;
final DomElement contents = platformViewsHost.querySelector('#view-0')!;
final DomElement slot =
flutterViewEmbedder.sceneElement!.querySelector('slot')!;
final DomElement contentsHost = contents.parent!;
@@ -594,10 +597,7 @@ void testMain() {
_overlay,
]);
expect(
flutterViewEmbedder.glassPaneElement.querySelector('flt-platform-view'),
isNotNull,
);
expect(platformViewsHost.querySelector('flt-platform-view'), isNotNull);
await disposePlatformView(0);
@@ -609,10 +609,7 @@ void testMain() {
_overlay,
]);
expect(
flutterViewEmbedder.glassPaneElement.querySelector('flt-platform-view'),
isNull,
);
expect(platformViewsHost.querySelector('flt-platform-view'), isNull);
});
test(
@@ -681,10 +678,7 @@ void testMain() {
_overlay,
]);
expect(
flutterViewEmbedder.glassPaneElement.querySelector('flt-platform-view'),
isNotNull,
);
expect(platformViewsHost.querySelector('flt-platform-view'), isNotNull);
// Render a frame with a different platform view.
await createPlatformView(1, 'test-platform-view');
@@ -699,9 +693,9 @@ void testMain() {
]);
expect(
flutterViewEmbedder.glassPaneElement
.querySelectorAll('flt-platform-view'),
hasLength(2));
platformViewsHost.querySelectorAll('flt-platform-view'),
hasLength(2),
);
// Render a frame without a platform view, but also without disposing of
// the platform view.
@@ -715,9 +709,9 @@ void testMain() {
// The actual contents of the platform view are kept in the dom, until
// it's actually disposed of!
expect(
flutterViewEmbedder.glassPaneElement
.querySelectorAll('flt-platform-view'),
hasLength(2));
platformViewsHost.querySelectorAll('flt-platform-view'),
hasLength(2),
);
});
test(

View File

@@ -47,7 +47,7 @@ void testMain() {
final FlutterViewEmbedder embedder = FlutterViewEmbedder();
expect(
embedder.glassPaneShadow.querySelectorAll('flt-semantics-placeholder'),
embedder.glassPaneShadowDEPRECATED.querySelectorAll('flt-semantics-placeholder'),
isNotEmpty,
);
});
@@ -71,7 +71,7 @@ void testMain() {
regularTextField.focus();
DomCSSStyleDeclaration? style = domWindow.getComputedStyle(
embedder.glassPaneShadow.querySelector('input')!,
embedder.glassPaneShadowDEPRECATED.querySelector('input')!,
'::placeholder');
expect(style, isNotNull);
expect(style.opacity, isNot('0'));
@@ -83,7 +83,7 @@ void testMain() {
textField.focus();
style = domWindow.getComputedStyle(
embedder.glassPaneShadow.querySelector('input.flt-text-editing')!,
embedder.glassPaneShadowDEPRECATED.querySelector('input.flt-text-editing')!,
'::placeholder');
expect(style, isNotNull);
expect(style.opacity, '0');
@@ -97,7 +97,7 @@ void testMain() {
});
tearDown(() {
embedder.glassPaneElement.remove();
embedder.glassPaneElementDEPRECATED.remove();
});
test('throws when shadowDom is not available', () {
@@ -111,27 +111,27 @@ void testMain() {
});
test('Initializes and attaches a shadow root', () {
expect(domInstanceOfString(embedder.glassPaneShadow, 'ShadowRoot'), isTrue);
expect(embedder.glassPaneShadow.host, embedder.glassPaneElement);
expect(embedder.glassPaneShadow, embedder.glassPaneElement.shadowRoot);
expect(domInstanceOfString(embedder.glassPaneShadowDEPRECATED, 'ShadowRoot'), isTrue);
expect(embedder.glassPaneShadowDEPRECATED.host, embedder.glassPaneElementDEPRECATED);
expect(embedder.glassPaneShadowDEPRECATED, embedder.glassPaneElementDEPRECATED.shadowRoot);
// The shadow root should be initialized with correct parameters.
expect(embedder.glassPaneShadow.mode, 'open');
expect(embedder.glassPaneShadowDEPRECATED.mode, 'open');
if (browserEngine != BrowserEngine.firefox &&
browserEngine != BrowserEngine.webkit) {
// Older versions of Safari and Firefox don't support this flag yet.
// See: https://caniuse.com/mdn-api_shadowroot_delegatesfocus
expect(embedder.glassPaneShadow.delegatesFocus, isFalse);
expect(embedder.glassPaneShadowDEPRECATED.delegatesFocus, isFalse);
}
});
test('Attaches a stylesheet to the shadow root', () {
final DomElement? style =
embedder.glassPaneShadow.querySelector('#flt-internals-stylesheet');
embedder.glassPaneShadowDEPRECATED.querySelector('#flt-internals-stylesheet');
expect(style, isNotNull);
expect(style!.tagName, equalsIgnoringCase('style'));
expect(style.parentNode, embedder.glassPaneShadow);
expect(style.parentNode, embedder.glassPaneShadowDEPRECATED);
});
});
}

View File

@@ -42,7 +42,8 @@ void main() {
void testMain() {
ensureFlutterViewEmbedderInitialized();
final DomElement flutterViewElement = flutterViewEmbedder.flutterViewElement;
final DomElement rootElement =
EnginePlatformDispatcher.instance.implicitView!.dom.rootElement;
late double dpi;
setUp(() {
@@ -496,7 +497,7 @@ void testMain() {
};
final DomElement child = createDomHTMLDivElement();
flutterViewElement.append(child);
rootElement.append(child);
final DomEventListener stopPropagationListener = createDomEventListener((DomEvent event) {
event.stopPropagation();
@@ -532,7 +533,7 @@ void testMain() {
receivedPacket = packet;
};
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(receivedPacket, isNotNull);
expect(receivedPacket!.data[0].buttons, equals(1));
@@ -553,7 +554,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(packets, hasLength(1));
expect(packets.single.data, hasLength(2));
@@ -587,7 +588,7 @@ void testMain() {
expect(keyboardConverter.keyIsPressed(physicalLeft), false);
expect(keyboardConverter.keyIsPressed(physicalRight), false);
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(keyDataList.length, 1);
expectKeyData(keyDataList.last,
type: ui.KeyEventType.down,
@@ -639,7 +640,7 @@ void testMain() {
expect(keyboardConverter.keyIsPressed(physicalRight), false);
keyDataList.clear(); // Remove key data generated by handleEvent
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(keyDataList.length, 0);
}
@@ -659,7 +660,7 @@ void testMain() {
expect(keyboardConverter.keyIsPressed(physicalRight), true);
keyDataList.clear(); // Remove key data generated by handleEvent
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(keyDataList.length, 0);
}
@@ -709,7 +710,7 @@ void testMain() {
expect(keyboardConverter.keyIsPressed(physicalRight), false);
keyDataList.clear(); // Remove key data generated by handleEvent
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(keyDataList.length, 1);
expectKeyData(keyDataList.last,
type: ui.KeyEventType.up,
@@ -738,7 +739,7 @@ void testMain() {
expect(keyboardConverter.keyIsPressed(physicalRight), true);
keyDataList.clear(); // Remove key data generated by handleEvent
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(keyDataList.length, 1);
expectKeyData(keyDataList.last,
type: ui.KeyEventType.up,
@@ -790,7 +791,7 @@ void testMain() {
expect(keyboardConverter.keyIsPressed(physicalRight), false);
keyDataList.clear(); // Remove key data generated by handleEvent
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(keyDataList.length, 0);
}
@@ -827,7 +828,7 @@ void testMain() {
expect(keyboardConverter.keyIsPressed(physicalAltRight), true);
keyDataList.clear(); // Remove key data generated by handleEvent.
flutterViewElement.dispatchEvent(context.primaryDown());
rootElement.dispatchEvent(context.primaryDown());
expect(keyDataList.length, 1);
expectKeyData(keyDataList.last,
type: ui.KeyEventType.up,
@@ -855,7 +856,7 @@ void testMain() {
final DomElement semanticsPlaceholder =
createDomElement('flt-semantics-placeholder');
flutterViewElement.append(semanticsPlaceholder);
rootElement.append(semanticsPlaceholder);
// Press on the semantics placeholder.
semanticsPlaceholder.dispatchEvent(context.primaryDown(
@@ -894,7 +895,7 @@ void testMain() {
packets.clear();
// Release the pointer on the semantics placeholder.
flutterViewElement.dispatchEvent(context.primaryUp(
rootElement.dispatchEvent(context.primaryUp(
clientX: 100.0,
clientY: 200.0,
));
@@ -928,7 +929,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.hover());
rootElement.dispatchEvent(context.hover());
expect(packets, hasLength(1));
expect(packets.single.data, hasLength(2));
@@ -952,7 +953,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.primaryDown(
rootElement.dispatchEvent(context.primaryDown(
clientX: 10.0,
clientY: 10.0,
));
@@ -964,7 +965,7 @@ void testMain() {
expect(packets[0].data[1].change, equals(ui.PointerChange.down));
packets.clear();
flutterViewElement.dispatchEvent(context.primaryDown(
rootElement.dispatchEvent(context.primaryDown(
clientX: 20.0,
clientY: 20.0,
));
@@ -989,7 +990,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -997,7 +998,7 @@ void testMain() {
deltaY: 10,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 20,
clientY: 50,
@@ -1005,14 +1006,14 @@ void testMain() {
deltaY: 10,
));
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 0,
buttons: 1,
clientX: 20.0,
clientY: 50.0,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 1,
clientX: 30,
clientY: 60,
@@ -1114,7 +1115,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1163,7 +1164,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1211,7 +1212,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1222,7 +1223,7 @@ void testMain() {
timeStamp: 0,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1233,7 +1234,7 @@ void testMain() {
timeStamp: 10,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1244,7 +1245,7 @@ void testMain() {
timeStamp: 20,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1255,7 +1256,7 @@ void testMain() {
timeStamp: 1000,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1266,7 +1267,7 @@ void testMain() {
timeStamp: 1010,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1277,7 +1278,7 @@ void testMain() {
timeStamp: 2000,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1452,7 +1453,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1460,7 +1461,7 @@ void testMain() {
deltaY: 120,
));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1472,7 +1473,7 @@ void testMain() {
debugOperatingSystemOverride = OperatingSystem.macOs;
KeyboardBinding.instance?.converter.handleEvent(keyDownEvent('ControlLeft', 'Control', kCtrl));
flutterViewElement.dispatchEvent(context.wheel(
rootElement.dispatchEvent(context.wheel(
buttons: 0,
clientX: 10,
clientY: 10,
@@ -1557,7 +1558,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.hover(
rootElement.dispatchEvent(context.hover(
clientX: 10.0,
clientY: 10.0,
));
@@ -1580,7 +1581,7 @@ void testMain() {
expect(packets[0].data[1].physicalDeltaY, equals(0.0));
packets.clear();
flutterViewElement.dispatchEvent(context.hover(
rootElement.dispatchEvent(context.hover(
clientX: 20.0,
clientY: 20.0,
));
@@ -1595,7 +1596,7 @@ void testMain() {
expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi));
packets.clear();
flutterViewElement.dispatchEvent(context.primaryDown(
rootElement.dispatchEvent(context.primaryDown(
clientX: 20.0,
clientY: 20.0,
));
@@ -1610,7 +1611,7 @@ void testMain() {
expect(packets[0].data[0].physicalDeltaY, equals(0.0));
packets.clear();
flutterViewElement.dispatchEvent(context.primaryMove(
rootElement.dispatchEvent(context.primaryMove(
clientX: 40.0,
clientY: 30.0,
));
@@ -1625,7 +1626,7 @@ void testMain() {
expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi));
packets.clear();
flutterViewElement.dispatchEvent(context.primaryUp(
rootElement.dispatchEvent(context.primaryUp(
clientX: 40.0,
clientY: 30.0,
));
@@ -1640,7 +1641,7 @@ void testMain() {
expect(packets[0].data[0].physicalDeltaY, equals(0.0));
packets.clear();
flutterViewElement.dispatchEvent(context.hover(
rootElement.dispatchEvent(context.hover(
clientX: 20.0,
clientY: 10.0,
));
@@ -1655,7 +1656,7 @@ void testMain() {
expect(packets[0].data[0].physicalDeltaY, equals(-20.0 * dpi));
packets.clear();
flutterViewElement.dispatchEvent(context.primaryDown(
rootElement.dispatchEvent(context.primaryDown(
clientX: 20.0,
clientY: 10.0,
));
@@ -1687,7 +1688,7 @@ void testMain() {
// Add and hover
flutterViewElement.dispatchEvent(context.hover(
rootElement.dispatchEvent(context.hover(
clientX: 10,
clientY: 11,
));
@@ -1706,7 +1707,7 @@ void testMain() {
expect(packets[0].data[1].buttons, equals(0));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 0,
buttons: 1,
clientX: 10.0,
@@ -1721,7 +1722,7 @@ void testMain() {
expect(packets[0].data[0].buttons, equals(1));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: _kNoButtonChange,
buttons: 1,
clientX: 20.0,
@@ -1736,7 +1737,7 @@ void testMain() {
expect(packets[0].data[0].buttons, equals(1));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 0,
clientX: 20.0,
clientY: 21.0,
@@ -1751,7 +1752,7 @@ void testMain() {
packets.clear();
// Drag with secondary button
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
clientX: 20.0,
@@ -1766,7 +1767,7 @@ void testMain() {
expect(packets[0].data[0].buttons, equals(2));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: _kNoButtonChange,
buttons: 2,
clientX: 30.0,
@@ -1781,7 +1782,7 @@ void testMain() {
expect(packets[0].data[0].buttons, equals(2));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 2,
clientX: 30.0,
clientY: 31.0,
@@ -1796,7 +1797,7 @@ void testMain() {
packets.clear();
// Drag with middle button
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 1,
buttons: 4,
clientX: 30.0,
@@ -1811,7 +1812,7 @@ void testMain() {
expect(packets[0].data[0].buttons, equals(4));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: _kNoButtonChange,
buttons: 4,
clientX: 40.0,
@@ -1826,7 +1827,7 @@ void testMain() {
expect(packets[0].data[0].buttons, equals(4));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 1,
clientX: 40.0,
clientY: 41.0,
@@ -1842,7 +1843,7 @@ void testMain() {
// Leave
flutterViewElement.dispatchEvent(context.mouseLeave(
rootElement.dispatchEvent(context.mouseLeave(
buttons: 1,
clientX: 1000.0,
clientY: 2000.0,
@@ -1850,7 +1851,7 @@ void testMain() {
expect(packets, isEmpty);
packets.clear();
flutterViewElement.dispatchEvent(context.mouseLeave(
rootElement.dispatchEvent(context.mouseLeave(
buttons: 0,
clientX: 1000.0,
clientY: 2000.0,
@@ -1880,7 +1881,7 @@ void testMain() {
};
// Press LMB.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 0,
buttons: 1,
));
@@ -1895,7 +1896,7 @@ void testMain() {
packets.clear();
// Press MMB.
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: 1,
buttons: 5,
));
@@ -1907,7 +1908,7 @@ void testMain() {
packets.clear();
// Release LMB.
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: 0,
buttons: 4,
));
@@ -1919,7 +1920,7 @@ void testMain() {
packets.clear();
// Release MMB.
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 1,
));
expect(packets, hasLength(1));
@@ -1947,7 +1948,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
clientX: 10,
@@ -1968,7 +1969,7 @@ void testMain() {
expect(packets[0].data[1].buttons, equals(2));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: _kNoButtonChange,
buttons: 2,
clientX: 20.0,
@@ -1983,7 +1984,7 @@ void testMain() {
expect(packets[0].data[0].buttons, equals(2));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: _kNoButtonChange,
buttons: 2,
clientX: 20.0,
@@ -1998,7 +1999,7 @@ void testMain() {
expect(packets[0].data[0].buttons, equals(2));
packets.clear();
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 2,
clientX: 20.0,
clientY: 21.0,
@@ -2034,7 +2035,7 @@ void testMain() {
};
// Press RMB and hold, popping up the context menu.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
));
@@ -2051,7 +2052,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.
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: _kNoButtonChange,
buttons: 3,
));
@@ -2063,7 +2064,7 @@ void testMain() {
packets.clear();
// Release LMB.
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: 0,
buttons: 2,
));
@@ -2075,7 +2076,7 @@ void testMain() {
packets.clear();
// Release RMB.
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 2,
));
expect(packets, hasLength(1));
@@ -2107,7 +2108,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.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
));
@@ -2122,7 +2123,7 @@ void testMain() {
packets.clear();
// User now hovers.
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: _kNoButtonChange,
buttons: 0,
));
@@ -2156,7 +2157,7 @@ void testMain() {
};
// Press RMB and hold, popping up the context menu.
flutterViewElement.dispatchEvent(context.mouseMove(
rootElement.dispatchEvent(context.mouseMove(
button: -1,
buttons: 2,
clientX: 10.0,
@@ -2193,7 +2194,7 @@ void testMain() {
};
// Press RMB and hold, popping up the context menu.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
clientX: 10.0,
@@ -2212,7 +2213,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.
flutterViewElement.dispatchEvent(context.hover(
rootElement.dispatchEvent(context.hover(
clientX: 20.0,
clientY: 20.0,
));
@@ -2256,7 +2257,7 @@ void testMain() {
};
// Press RMB and hold, popping up the context menu.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
clientX: 10.0,
@@ -2273,7 +2274,7 @@ void testMain() {
packets.clear();
// Press LMB.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 0,
buttons: 3,
clientX: 20.0,
@@ -2287,7 +2288,7 @@ void testMain() {
packets.clear();
// Release LMB.
flutterViewElement.dispatchEvent(context.primaryUp(
rootElement.dispatchEvent(context.primaryUp(
clientX: 20.0,
clientY: 20.0,
));
@@ -2319,7 +2320,7 @@ void testMain() {
};
// Press RMB and hold, popping up the context menu.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
clientX: 10.0,
@@ -2337,7 +2338,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.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 0,
clientX: 20.0,
@@ -2359,7 +2360,7 @@ void testMain() {
packets.clear();
// Release RMB.
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 2,
clientX: 20.0,
clientY: 20.0,
@@ -2394,7 +2395,7 @@ void testMain() {
};
// Press RMB, popping up the context menu.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
clientX: 10.0,
@@ -2410,7 +2411,7 @@ void testMain() {
packets.clear();
// RMB up.
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 2,
clientX: 10.0,
clientY: 10.0,
@@ -2425,7 +2426,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".
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 0,
clientX: 20.0,
@@ -2442,7 +2443,7 @@ void testMain() {
packets.clear();
// Release RMB.
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 2,
clientX: 20.0,
clientY: 20.0,
@@ -2480,7 +2481,7 @@ void testMain() {
};
// Press RMB and hold, popping up the context menu.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
clientX: 10.0,
@@ -2496,7 +2497,7 @@ void testMain() {
packets.clear();
// Release RMB.
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 2,
clientX: 10.0,
clientY: 10.0,
@@ -2508,7 +2509,7 @@ void testMain() {
packets.clear();
// Press RMB again, in a different location.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 2,
clientX: 20.0,
@@ -2546,7 +2547,7 @@ void testMain() {
};
// Press and hold LMB.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 0,
buttons: 1,
clientX: 5.0,
@@ -2565,7 +2566,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.
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
button: 2,
buttons: 3,
clientX: 20.0,
@@ -2581,7 +2582,7 @@ void testMain() {
// Release LMB. The pointer is still down (RMB), so we only send a move to
// update the position of the pointer.
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 0,
buttons: 2,
clientX: 30.0,
@@ -2596,7 +2597,7 @@ void testMain() {
packets.clear();
// Release RMB. There's no more buttons down, so we send an up event.
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
button: 2,
buttons: 0,
clientX: 30.0,
@@ -2627,26 +2628,26 @@ void testMain() {
};
// Press and drag around.
flutterViewElement.dispatchEvent(context.primaryDown(
rootElement.dispatchEvent(context.primaryDown(
clientX: 10.0,
clientY: 10.0,
));
flutterViewElement.dispatchEvent(context.primaryMove(
rootElement.dispatchEvent(context.primaryMove(
clientX: 12.0,
clientY: 10.0,
));
flutterViewElement.dispatchEvent(context.primaryMove(
rootElement.dispatchEvent(context.primaryMove(
clientX: 15.0,
clientY: 10.0,
));
flutterViewElement.dispatchEvent(context.primaryMove(
rootElement.dispatchEvent(context.primaryMove(
clientX: 20.0,
clientY: 10.0,
));
packets.clear();
// Move outside the flutterViewElement.
flutterViewElement.dispatchEvent(context.primaryMove(
rootElement.dispatchEvent(context.primaryMove(
clientX: 900.0,
clientY: 1900.0,
));
@@ -2658,7 +2659,7 @@ void testMain() {
packets.clear();
// Release outside the flutterViewElement.
flutterViewElement.dispatchEvent(context.primaryUp(
rootElement.dispatchEvent(context.primaryUp(
clientX: 1000.0,
clientY: 2000.0,
));
@@ -2694,7 +2695,7 @@ void testMain() {
context.multiTouchDown(const <_TouchDetails>[
_TouchDetails(pointer: 2, clientX: 100, clientY: 101),
_TouchDetails(pointer: 3, clientX: 200, clientY: 201),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
if (context.runtimeType == _PointerEventContext) {
expect(packets.length, 2);
expect(packets[0].data.length, 2);
@@ -2741,7 +2742,7 @@ void testMain() {
context.multiTouchMove(const <_TouchDetails>[
_TouchDetails(pointer: 3, clientX: 300, clientY: 302),
_TouchDetails(pointer: 2, clientX: 400, clientY: 402),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
if (context.runtimeType == _PointerEventContext) {
expect(packets.length, 2);
expect(packets[0].data.length, 1);
@@ -2775,7 +2776,7 @@ void testMain() {
// One pointer up
context.multiTouchUp(const <_TouchDetails>[
_TouchDetails(pointer: 3, clientX: 300, clientY: 302),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(1));
expect(packets[0].data, hasLength(2));
expect(packets[0].data[0].change, equals(ui.PointerChange.up));
@@ -2798,7 +2799,7 @@ void testMain() {
// Another pointer up
context.multiTouchUp(const <_TouchDetails>[
_TouchDetails(pointer: 2, clientX: 400, clientY: 402),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(1));
expect(packets[0].data, hasLength(2));
expect(packets[0].data[0].change, equals(ui.PointerChange.up));
@@ -2822,7 +2823,7 @@ void testMain() {
context.multiTouchDown(const <_TouchDetails>[
_TouchDetails(pointer: 3, clientX: 500, clientY: 501),
_TouchDetails(pointer: 2, clientX: 600, clientY: 601),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
if (context.runtimeType == _PointerEventContext) {
expect(packets.length, 2);
expect(packets[0].data.length, 2);
@@ -2884,13 +2885,13 @@ void testMain() {
context.multiTouchDown(const <_TouchDetails>[
_TouchDetails(pointer: 2, clientX: 100, clientY: 101),
_TouchDetails(pointer: 3, clientX: 200, clientY: 201),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.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(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets.length, 1);
expect(packets[0].data.length, 2);
expect(packets[0].data[0].change, equals(ui.PointerChange.cancel));
@@ -2928,7 +2929,7 @@ void testMain() {
context.multiTouchDown(const <_TouchDetails>[
_TouchDetails(pointer: 1, clientX: 100, clientY: 101),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(1));
// An add will be synthesized.
expect(packets[0].data, hasLength(2));
@@ -2941,7 +2942,7 @@ void testMain() {
context.multiTouchDown(const <_TouchDetails>[
_TouchDetails(pointer: 2, clientX: 200, clientY: 202),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
// An add will be synthesized.
expect(packets, hasLength(1));
expect(packets[0].data, hasLength(2));
@@ -2968,12 +2969,12 @@ void testMain() {
context.multiTouchUp(const <_TouchDetails>[
_TouchDetails(pointer: 23, clientX: 200, clientY: 202),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(0));
context.multiTouchCancel(const <_TouchDetails>[
_TouchDetails(pointer: 24, clientX: 200, clientY: 202),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(0));
},
);
@@ -2996,7 +2997,7 @@ void testMain() {
packets.add(packet);
};
flutterViewElement.dispatchEvent(context.mouseDown(
rootElement.dispatchEvent(context.mouseDown(
pointerId: 12,
button: 0,
buttons: 1,
@@ -3014,7 +3015,7 @@ void testMain() {
expect(
() {
flutterViewElement.dispatchEvent(context.mouseUp(
rootElement.dispatchEvent(context.mouseUp(
pointerId: 41,
button: 0,
buttons: 0,
@@ -3049,7 +3050,7 @@ void testMain() {
context.multiTouchDown(const <_TouchDetails>[
_TouchDetails(pointer: 1, clientX: 20, clientY: 20),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(1));
expect(packets[0].data, hasLength(2));
expect(packets[0].data[0].change, equals(ui.PointerChange.add));
@@ -3071,7 +3072,7 @@ void testMain() {
context.multiTouchMove(const <_TouchDetails>[
_TouchDetails(pointer: 1, clientX: 40, clientY: 30),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(1));
expect(packets[0].data, hasLength(1));
expect(packets[0].data[0].change, equals(ui.PointerChange.move));
@@ -3085,7 +3086,7 @@ void testMain() {
context.multiTouchUp(const <_TouchDetails>[
_TouchDetails(pointer: 1, clientX: 40, clientY: 30),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(1));
expect(packets[0].data, hasLength(2));
expect(packets[0].data[0].change, equals(ui.PointerChange.up));
@@ -3107,7 +3108,7 @@ void testMain() {
context.multiTouchDown(const <_TouchDetails>[
_TouchDetails(pointer: 2, clientX: 20, clientY: 10),
]).forEach(flutterViewElement.dispatchEvent);
]).forEach(rootElement.dispatchEvent);
expect(packets, hasLength(1));
expect(packets[0].data, hasLength(2));
expect(packets[0].data[0].change, equals(ui.PointerChange.add));

View File

@@ -11,6 +11,7 @@ import 'package:ui/src/engine/dom.dart';
import 'package:ui/src/engine/embedder.dart';
import 'package:ui/src/engine/semantics.dart';
import 'package:ui/src/engine/services.dart';
import 'package:ui/src/engine/view_embedder/dom_manager.dart';
const StandardMessageCodec codec = StandardMessageCodec();
@@ -19,26 +20,27 @@ void main() {
}
void testMain() {
late FlutterViewEmbedder embedder;
late DomManager domManager;
late AccessibilityAnnouncements accessibilityAnnouncements;
setUp(() {
embedder = FlutterViewEmbedder();
final FlutterViewEmbedder embedder = FlutterViewEmbedder();
domManager = DomManager.fromFlutterViewEmbedderDEPRECATED(embedder);
accessibilityAnnouncements = embedder.accessibilityAnnouncements;
setLiveMessageDurationForTest(const Duration(milliseconds: 10));
expect(
embedder.glassPaneShadow.querySelector('flt-announcement-polite'),
domManager.renderingHost.querySelector('flt-announcement-polite'),
accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.polite),
);
expect(
embedder.glassPaneShadow.querySelector('flt-announcement-assertive'),
domManager.renderingHost.querySelector('flt-announcement-assertive'),
accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.assertive),
);
});
tearDown(() async {
await Future<void>.delayed(liveMessageDuration * 2);
embedder.glassPaneElement.remove();
domManager.rootElement.remove();
});
group('$AccessibilityAnnouncements', () {

View File

@@ -22,6 +22,11 @@ DateTime _testTime = DateTime(2018, 12, 17);
EngineSemanticsOwner semantics() => EngineSemanticsOwner.instance;
DomShadowRoot get renderingHost =>
EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost;
DomElement get platformViewsHost =>
EnginePlatformDispatcher.instance.implicitView!.dom.platformViewsHost;
void main() {
internalBootstrapBrowserTest(() {
return testMain;
@@ -224,8 +229,8 @@ void _testEngineSemanticsOwner() {
expect(semantics().semanticsEnabled, isFalse);
// Synthesize a click on the placeholder.
final DomElement placeholder = flutterViewEmbedder.glassPaneShadow
.querySelector('flt-semantics-placeholder')!;
final DomElement placeholder =
renderingHost.querySelector('flt-semantics-placeholder')!;
expect(placeholder.isConnected, isTrue);
@@ -320,8 +325,8 @@ void _testEngineSemanticsOwner() {
.instance.accessibilityFeatures.accessibleNavigation,
isFalse);
final DomElement placeholder = flutterViewEmbedder.glassPaneShadow
.querySelector('flt-semantics-placeholder')!;
final DomElement placeholder =
renderingHost.querySelector('flt-semantics-placeholder')!;
expect(placeholder.isConnected, isTrue);
@@ -795,9 +800,9 @@ void _testContainer() {
</sem>''');
final DomElement parentElement =
appHostNode.querySelector('flt-semantics')!;
rootElement.querySelector('flt-semantics')!;
final DomElement container =
appHostNode.querySelector('flt-semantics-container')!;
rootElement.querySelector('flt-semantics-container')!;
if (isMacOrIOS) {
expect(parentElement.style.top, '0px');
@@ -846,9 +851,9 @@ void _testContainer() {
</sem>''');
final DomElement parentElement =
appHostNode.querySelector('flt-semantics')!;
rootElement.querySelector('flt-semantics')!;
final DomElement container =
appHostNode.querySelector('flt-semantics-container')!;
rootElement.querySelector('flt-semantics-container')!;
expect(parentElement.style.transform, 'matrix(1, 0, 0, 1, 10, 10)');
expect(parentElement.style.transformOrigin, '0px 0px 0px');
@@ -886,9 +891,9 @@ void _testContainer() {
</sem>''');
final DomElement parentElement =
appHostNode.querySelector('flt-semantics')!;
rootElement.querySelector('flt-semantics')!;
final DomElement container =
appHostNode.querySelector('flt-semantics-container')!;
rootElement.querySelector('flt-semantics-container')!;
if (isMacOrIOS) {
expect(parentElement.style.top, '0px');
@@ -1028,15 +1033,15 @@ void _testContainer() {
</sem-c>
</sem>''');
final DomElement root = appHostNode.querySelector('#flt-semantic-node-0')!;
final DomElement root = rootElement.querySelector('#flt-semantic-node-0')!;
expect(root.style.pointerEvents, 'none');
final DomElement child1 =
appHostNode.querySelector('#flt-semantic-node-1')!;
rootElement.querySelector('#flt-semantic-node-1')!;
expect(child1.style.pointerEvents, 'all');
final DomElement child2 =
appHostNode.querySelector('#flt-semantic-node-2')!;
rootElement.querySelector('#flt-semantic-node-2')!;
expect(child2.style.pointerEvents, 'all');
semantics().semanticsEnabled = false;
@@ -1472,7 +1477,7 @@ void _testIncrementables() {
</sem>''');
final DomHTMLInputElement input =
appHostNode.querySelector('input')! as DomHTMLInputElement;
rootElement.querySelector('input')! as DomHTMLInputElement;
input.value = '2';
input.dispatchEvent(createDomEvent('Event', 'change'));
@@ -1505,7 +1510,7 @@ void _testIncrementables() {
</sem>''');
final DomHTMLInputElement input =
appHostNode.querySelector('input')! as DomHTMLInputElement;
rootElement.querySelector('input')! as DomHTMLInputElement;
input.value = '0';
input.dispatchEvent(createDomEvent('Event', 'change'));
@@ -1640,13 +1645,13 @@ void _testTextField() {
semantics().updateSemantics(builder.build());
final DomElement textField =
appHostNode.querySelector('input[data-semantics-role="text-field"]')!;
rootElement.querySelector('input[data-semantics-role="text-field"]')!;
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
expect(rootElement.ownerDocument?.activeElement, isNot(textField));
textField.focus();
expect(appHostNode.ownerDocument?.activeElement, textField);
expect(rootElement.ownerDocument?.activeElement, textField);
expect(await logger.idLog.first, 0);
expect(await logger.actionLog.first, ui.SemanticsAction.didGainAccessibilityFocus);
@@ -2370,7 +2375,7 @@ void _testPlatformView() {
semantics().updateSemantics(builder.build());
expectSemanticsTree('<sem aria-owns="flt-pv-5" style="$rootSemanticStyle"></sem>');
final DomElement element = appHostNode.querySelector('flt-semantics')!;
final DomElement element = rootElement.querySelector('flt-semantics')!;
expect(element.style.pointerEvents, 'none');
semantics().semanticsEnabled = false;
@@ -2459,11 +2464,11 @@ void _testPlatformView() {
</sem-c>
</sem>''');
final DomElement root = appHostNode.querySelector('#flt-semantic-node-0')!;
final DomElement root = rootElement.querySelector('#flt-semantic-node-0')!;
expect(root.style.pointerEvents, 'none');
final DomElement child1 =
appHostNode.querySelector('#flt-semantic-node-1')!;
rootElement.querySelector('#flt-semantic-node-1')!;
expect(child1.style.pointerEvents, 'all');
final DomRect child1Rect = child1.getBoundingClientRect();
expect(child1Rect.left, 0);
@@ -2472,7 +2477,7 @@ void _testPlatformView() {
expect(child1Rect.bottom, 25);
final DomElement child2 =
appHostNode.querySelector('#flt-semantic-node-2')!;
rootElement.querySelector('#flt-semantic-node-2')!;
expect(child2.style.pointerEvents, 'none');
final DomRect child2Rect = child2.getBoundingClientRect();
expect(child2Rect.left, 0);
@@ -2481,7 +2486,7 @@ void _testPlatformView() {
expect(child2Rect.bottom, 45);
final DomElement child3 =
appHostNode.querySelector('#flt-semantic-node-3')!;
rootElement.querySelector('#flt-semantic-node-3')!;
expect(child3.style.pointerEvents, 'all');
final DomRect child3Rect = child3.getBoundingClientRect();
expect(child3Rect.left, 0);
@@ -2490,7 +2495,7 @@ void _testPlatformView() {
expect(child3Rect.bottom, 60);
final DomElement platformViewElement =
flutterViewEmbedder.glassPaneElement.querySelector('#view-0')!;
platformViewsHost.querySelector('#view-0')!;
final DomRect platformViewRect =
platformViewElement.getBoundingClientRect();
expect(platformViewRect.left, 0);

View File

@@ -7,7 +7,7 @@ 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/platform_dispatcher.dart';
import 'package:ui/src/engine/semantics.dart';
import 'package:ui/src/engine/util.dart';
import 'package:ui/src/engine/vector_math.dart';
@@ -22,7 +22,8 @@ import '../../common/matchers.dart';
/// 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;
DomElement get rootElement =>
EnginePlatformDispatcher.instance.implicitView!.dom.rootElement;
/// CSS style applied to the root of the semantics tree.
// TODO(yjbanov): this should be handled internally by [expectSemanticsTree].
@@ -354,14 +355,14 @@ class SemanticsTester {
void expectSemanticsTree(String semanticsHtml) {
const List<String> ignoredAttributes = <String>['pointer-events'];
expect(
canonicalizeHtml(appHostNode.querySelector('flt-semantics')!.outerHTML!, ignoredAttributes: ignoredAttributes),
canonicalizeHtml(rootElement.querySelector('flt-semantics')!.outerHTML!, ignoredAttributes: ignoredAttributes),
canonicalizeHtml(semanticsHtml),
);
}
/// Finds the first HTML element in the semantics tree used for scrolling.
DomElement? findScrollable() {
return appHostNode.querySelectorAll('flt-semantics').firstWhereOrNull(
return rootElement.querySelectorAll('flt-semantics').firstWhereOrNull(
(DomElement? element) {
return element!.style.overflow == 'hidden' ||
element.style.overflowY == 'scroll' ||

View File

@@ -98,20 +98,20 @@ void testMain() {
final SemanticsActionLogger logger = SemanticsActionLogger();
createTextFieldSemantics(value: 'hello');
final DomElement textField = appHostNode
final DomElement textField = rootElement
.querySelector('input[data-semantics-role="text-field"]')!;
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
expect(rootElement.ownerDocument?.activeElement, isNot(textField));
textField.focus();
expect(appHostNode.ownerDocument?.activeElement, textField);
expect(rootElement.ownerDocument?.activeElement, textField);
expect(await logger.idLog.first, 0);
expect(await logger.actionLog.first, ui.SemanticsAction.didGainAccessibilityFocus);
textField.blur();
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
expect(rootElement.ownerDocument?.activeElement, isNot(textField));
expect(await logger.idLog.first, 0);
expect(await logger.actionLog.first, ui.SemanticsAction.didLoseAccessibilityFocus);
}, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638
@@ -119,7 +119,7 @@ void testMain() {
skip: browserEngine != BrowserEngine.blink);
test('Syncs semantic state from framework', () {
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
int changeCount = 0;
int actionCount = 0;
@@ -142,7 +142,7 @@ void testMain() {
);
final TextField textField = textFieldSemantics.primaryRole! as TextField;
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
expect(textField.editableElement, strategy.domElement);
expect(textField.activeEditableElement.getAttribute('aria-label'), 'greeting');
expect(textField.activeEditableElement.style.width, '10px');
@@ -155,7 +155,7 @@ void testMain() {
rect: const ui.Rect.fromLTWH(0, 0, 12, 17),
);
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
expect(strategy.domElement, null);
expect(textField.activeEditableElement.getAttribute('aria-label'), 'farewell');
expect(textField.activeEditableElement.style.width, '12px');
@@ -200,7 +200,7 @@ void testMain() {
test(
'Updates editing state when receiving framework messages from the text input channel',
() {
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
strategy.enable(
singlelineConfig,
@@ -243,7 +243,7 @@ void testMain() {
});
test('Gives up focus after DOM blur', () {
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
strategy.enable(
singlelineConfig,
@@ -257,11 +257,11 @@ void testMain() {
final TextField textField = textFieldSemantics.primaryRole! as TextField;
expect(textField.editableElement, strategy.domElement);
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
// The input should not refocus after blur.
textField.activeEditableElement.blur();
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
strategy.disable();
});
@@ -281,17 +281,17 @@ void testMain() {
isFocused: true,
);
expect(strategy.domElement, isNotNull);
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
strategy.disable();
expect(strategy.domElement, isNull);
// It doesn't remove the DOM element.
final TextField textField = textFieldSemantics.primaryRole! as TextField;
expect(appHostNode.contains(textField.editableElement), isTrue);
expect(rootElement.contains(textField.editableElement), isTrue);
// Editing element is not enabled.
expect(strategy.isEnabled, isFalse);
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
});
test('Refocuses when setting editing state', () {
@@ -306,11 +306,11 @@ void testMain() {
isFocused: true,
);
expect(strategy.domElement, isNotNull);
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
// Blur the element without telling the framework.
strategy.activeDomElement.blur();
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
// The input will have focus after editing state is set and semantics updated.
strategy.setEditingState(EditingState(text: 'foo'));
@@ -328,7 +328,7 @@ void testMain() {
value: 'hello',
isFocused: true,
);
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
strategy.disable();
});
@@ -348,7 +348,7 @@ void testMain() {
final DomHTMLTextAreaElement textArea =
strategy.domElement! as DomHTMLTextAreaElement;
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
strategy.enable(
singlelineConfig,
@@ -357,11 +357,11 @@ void testMain() {
);
textArea.blur();
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
strategy.disable();
// It doesn't remove the textarea from the DOM.
expect(appHostNode.contains(textArea), isTrue);
expect(rootElement.contains(textArea), isTrue);
// Editing element is not enabled.
expect(strategy.isEnabled, isFalse);
});
@@ -441,13 +441,13 @@ void testMain() {
createTwoFieldSemantics(tester, focusFieldId: 1);
expect(tester.apply().length, 3);
expect(appHostNode.ownerDocument?.activeElement,
expect(rootElement.ownerDocument?.activeElement,
tester.getTextField(1).editableElement);
expect(strategy.domElement, tester.getTextField(1).editableElement);
createTwoFieldSemantics(tester, focusFieldId: 2);
expect(tester.apply().length, 3);
expect(appHostNode.ownerDocument?.activeElement,
expect(rootElement.ownerDocument?.activeElement,
tester.getTextField(2).editableElement);
expect(strategy.domElement, tester.getTextField(2).editableElement);
}
@@ -478,16 +478,16 @@ void testMain() {
});
test('does not render a text field', () {
expect(appHostNode.querySelector('flt-semantics[role="textbox"]'), isNull);
expect(rootElement.querySelector('flt-semantics[role="textbox"]'), isNull);
createTextFieldSemanticsForIos(value: 'hello');
expect(appHostNode.querySelector('flt-semantics[role="textbox"]'), isNotNull);
expect(rootElement.querySelector('flt-semantics[role="textbox"]'), isNotNull);
});
test('tap detection works', () async {
final SemanticsActionLogger logger = SemanticsActionLogger();
createTextFieldSemanticsForIos(value: 'hello');
final DomElement textField = appHostNode
final DomElement textField = rootElement
.querySelector('flt-semantics[role="textbox"]')!;
simulateTap(textField);
@@ -496,7 +496,7 @@ void testMain() {
});
test('Syncs semantic state from framework', () {
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
int changeCount = 0;
int actionCount = 0;
@@ -519,7 +519,7 @@ void testMain() {
);
final TextField textField = textFieldSemantics.primaryRole! as TextField;
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
expect(textField.editableElement, strategy.domElement);
expect(textField.activeEditableElement.getAttribute('aria-label'), 'greeting');
expect(textField.activeEditableElement.style.width, '10px');
@@ -532,10 +532,10 @@ void testMain() {
rect: const ui.Rect.fromLTWH(0, 0, 12, 17),
);
final DomElement textBox =
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
rootElement.querySelector('flt-semantics[role="textbox"]')!;
expect(strategy.domElement, null);
expect(appHostNode.ownerDocument?.activeElement, textBox);
expect(rootElement.ownerDocument?.activeElement, textBox);
expect(textBox.getAttribute('aria-label'), 'farewell');
strategy.disable();
@@ -577,7 +577,7 @@ void testMain() {
test(
'Updates editing state when receiving framework messages from the text input channel',
() {
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
strategy.enable(
singlelineConfig,
@@ -620,7 +620,7 @@ void testMain() {
});
test('Gives up focus after DOM blur', () {
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);
expect(rootElement.ownerDocument?.activeElement, domDocument.body);
strategy.enable(
singlelineConfig,
@@ -634,13 +634,13 @@ void testMain() {
final TextField textField = textFieldSemantics.primaryRole! as TextField;
expect(textField.editableElement, strategy.domElement);
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
// The input should not refocus after blur.
textField.activeEditableElement.blur();
final DomElement textBox =
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
expect(appHostNode.ownerDocument?.activeElement, textBox);
rootElement.querySelector('flt-semantics[role="textbox"]')!;
expect(rootElement.ownerDocument?.activeElement, textBox);
strategy.disable();
});
@@ -661,20 +661,20 @@ void testMain() {
isFocused: true,
);
expect(strategy.domElement, isNotNull);
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
strategy.disable();
expect(strategy.domElement, isNull);
// It removes the DOM element.
final TextField textField = textFieldSemantics.primaryRole! as TextField;
expect(appHostNode.contains(textField.editableElement), isFalse);
expect(rootElement.contains(textField.editableElement), isFalse);
// Editing element is not enabled.
expect(strategy.isEnabled, isFalse);
// Focus is on the semantic object
final DomElement textBox =
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
expect(appHostNode.ownerDocument?.activeElement, textBox);
rootElement.querySelector('flt-semantics[role="textbox"]')!;
expect(rootElement.ownerDocument?.activeElement, textBox);
});
test('Refocuses when setting editing state', () {
@@ -689,13 +689,13 @@ void testMain() {
isFocused: true,
);
expect(strategy.domElement, isNotNull);
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
// Blur the element without telling the framework.
strategy.activeDomElement.blur();
final DomElement textBox =
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
expect(appHostNode.ownerDocument?.activeElement, textBox);
rootElement.querySelector('flt-semantics[role="textbox"]')!;
expect(rootElement.ownerDocument?.activeElement, textBox);
// The input will have focus after editing state is set and semantics updated.
strategy.setEditingState(EditingState(text: 'foo'));
@@ -713,7 +713,7 @@ void testMain() {
value: 'hello',
isFocused: true,
);
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
strategy.disable();
});
@@ -732,7 +732,7 @@ void testMain() {
final DomHTMLTextAreaElement textArea =
strategy.domElement! as DomHTMLTextAreaElement;
expect(appHostNode.ownerDocument?.activeElement, strategy.domElement);
expect(rootElement.ownerDocument?.activeElement, strategy.domElement);
strategy.enable(
singlelineConfig,
@@ -740,17 +740,17 @@ void testMain() {
onAction: (_) {},
);
expect(appHostNode.contains(textArea), isTrue);
expect(rootElement.contains(textArea), isTrue);
textArea.blur();
final DomElement textBox =
appHostNode.querySelector('flt-semantics[role="textbox"]')!;
rootElement.querySelector('flt-semantics[role="textbox"]')!;
expect(appHostNode.ownerDocument?.activeElement, textBox);
expect(rootElement.ownerDocument?.activeElement, textBox);
strategy.disable();
// It removes the textarea from the DOM.
expect(appHostNode.contains(textArea), isFalse);
expect(rootElement.contains(textArea), isFalse);
// Editing element is not enabled.
expect(strategy.isEnabled, isFalse);
});
@@ -809,13 +809,13 @@ void testMain() {
createTwoFieldSemanticsForIos(tester, focusFieldId: 1);
expect(tester.apply().length, 3);
expect(appHostNode.ownerDocument?.activeElement,
expect(rootElement.ownerDocument?.activeElement,
tester.getTextField(1).editableElement);
expect(strategy.domElement, tester.getTextField(1).editableElement);
createTwoFieldSemanticsForIos(tester, focusFieldId: 2);
expect(tester.apply().length, 3);
expect(appHostNode.ownerDocument?.activeElement,
expect(rootElement.ownerDocument?.activeElement,
tester.getTextField(2).editableElement);
expect(strategy.domElement, tester.getTextField(2).editableElement);
}
@@ -976,7 +976,7 @@ Map<int, SemanticsObject> createTwoFieldSemanticsForIos(SemanticsTester builder,
builder.apply();
final String label = focusFieldId == 1 ? 'Hello' : 'World';
final DomElement textBox =
appHostNode.querySelector('flt-semantics[aria-label="$label"]')!;
rootElement.querySelector('flt-semantics[aria-label="$label"]')!;
simulateTap(textBox);

View File

@@ -0,0 +1,27 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
void main() {
internalBootstrapBrowserTest(() => doTests);
}
void doTests() {
group('DomManager', () {
test('fromFlutterViewEmbedderDEPRECATED', () {
final FlutterViewEmbedder embedder = FlutterViewEmbedder();
final DomManager domManager =
DomManager.fromFlutterViewEmbedderDEPRECATED(embedder);
expect(domManager.rootElement, embedder.flutterViewElementDEPRECATED);
expect(domManager.renderingHost, embedder.glassPaneShadowDEPRECATED);
expect(domManager.platformViewsHost, embedder.glassPaneElementDEPRECATED);
expect(domManager.textEditingHost, embedder.textEditingHostNodeDEPRECATED);
expect(domManager.semanticsHost, embedder.semanticsHostElementDEPRECATED);
});
});
}

View File

@@ -13,6 +13,10 @@ import 'package:web_engine_tester/golden_tester.dart';
import '../common/test_initialization.dart';
import 'paragraph/helper.dart';
DomElement get sceneHost =>
EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost
.querySelector('flt-scene-host')!;
void main() {
internalBootstrapBrowserTest(() => testMain);
}
@@ -32,7 +36,7 @@ Future<void> testMain() async {
testScene.style.transform = 'scale(0.3)';
}
testScene.append(canvas.rootElement);
flutterViewEmbedder.glassPaneShadow.querySelector('flt-scene-host')!.append(testScene);
sceneHost.append(testScene);
}
setUpUnitTests(
@@ -41,7 +45,7 @@ Future<void> testMain() async {
);
tearDown(() {
flutterViewEmbedder.glassPaneShadow.querySelector('flt-scene')?.remove();
sceneHost.querySelector('flt-scene')?.remove();
});
/// Draws several lines, some aligned precisely with the pixel grid, and some
@@ -263,7 +267,7 @@ Future<void> testMain() async {
}
sceneElement.querySelector('flt-clip')!.append(canvas.rootElement);
flutterViewEmbedder.glassPaneShadow.querySelector('flt-scene-host')!.append(sceneElement);
sceneHost.append(sceneElement);
await matchGoldenFile(
'bitmap_canvas_draws_text_on_top_of_canvas.png',

View File

@@ -17,6 +17,10 @@ import '../../common/test_initialization.dart';
/// test/golden_tests/engine/shader_mask_golden_test.dart --profile
const bool debugTest = false;
DomElement get sceneHost =>
EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost
.querySelector('flt-scene-host')!;
Future<void> main() async {
if (!debugTest) {
internalBootstrapBrowserTest(() => testMain);
@@ -42,8 +46,7 @@ Future<void> testMain() async {
setUp(() async {
SurfaceSceneBuilder.debugForgetFrameScene();
for (final DomNode scene in
flutterViewEmbedder.sceneHostElement!.querySelectorAll('flt-scene').cast<DomNode>()) {
for (final DomNode scene in sceneHost.querySelectorAll('flt-scene').cast<DomNode>()) {
scene.remove();
}
initWebGl();
@@ -164,7 +167,7 @@ void _renderCirclesScene(BlendMode blendMode) {
builder.addPicture(Offset.zero, circles2);
builder.pop();
flutterViewEmbedder.sceneHostElement!.append(builder.build().webOnlyRootElement!);
sceneHost.append(builder.build().webOnlyRootElement!);
}
Picture _drawTestPictureWithText(
@@ -219,5 +222,5 @@ void _renderTextScene(BlendMode blendMode) {
builder.addPicture(Offset.zero, textPicture2);
builder.pop();
flutterViewEmbedder.sceneHostElement!.append(builder.build().webOnlyRootElement!);
sceneHost.append(builder.build().webOnlyRootElement!);
}