Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[web] Migrate Flutter Web DOM usage to JS static interop - 31. #33361

Merged
merged 2 commits into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 74 additions & 3 deletions lib/web_ui/lib/src/engine/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ extension DomHTMLDocumentExtension on DomHTMLDocument {
external DomHTMLHeadElement? get head;
external DomHTMLBodyElement? get body;
external set title(String? value);
Iterable<DomElement> getElementsByTagName(String tag) =>
createDomListWrapper<DomElement>(js_util
.callMethod<_DomList>(this, 'getElementsByTagName', <Object>[tag]));
}

@JS('document')
Expand All @@ -124,6 +127,8 @@ extension DomEventTargetExtension on DomEventTarget {
<Object>[type, listener, if (useCapture != null) useCapture]);
}
}

external bool dispatchEvent(DomEvent event);
}

typedef DomEventListener = void Function(DomEvent event);
Expand Down Expand Up @@ -220,6 +225,23 @@ extension DomElementExtension on DomElement {
external void remove();
external void setAttribute(String name, Object value);
void appendText(String text) => append(createDomText(text));
external void removeAttribute(String name);
external set tabIndex(int? value);
external int? get tabIndex;
external void focus();

/// [scrollTop] and [scrollLeft] can both return non-integers when using
/// display scaling.
///
/// The setters have a spurious round just in case the supplied [int] flowed
/// from the non-static interop JS API. When all of Flutter Web has been
/// migrated to static interop we can probably remove the rounds.
int get scrollTop => js_util.getProperty(this, 'scrollTop').round();
set scrollTop(int value) =>
js_util.setProperty<num>(this, 'scrollTop', value.round());
int get scrollLeft => js_util.getProperty(this, 'scrollLeft').round();
set scrollLeft(int value) =>
js_util.setProperty<num>(this, 'scrollLeft', value.round());
}

@JS()
Expand Down Expand Up @@ -287,6 +309,10 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
set alignItems(String value) => setProperty('align-items', value);
set margin(String value) => setProperty('margin', value);
set background(String value) => setProperty('background', value);
set touchAction(String value) => setProperty('touch-action', value);
set overflowY(String value) => setProperty('overflow-y', value);
set overflowX(String value) => setProperty('overflow-x', value);
set outline(String value) => setProperty('outline', value);
String get width => getPropertyValue('width');
String get height => getPropertyValue('height');
String get position => getPropertyValue('position');
Expand Down Expand Up @@ -342,6 +368,10 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
String get alignItems => getPropertyValue('align-items');
String get margin => getPropertyValue('margin');
String get background => getPropertyValue('background');
String get touchAction => getPropertyValue('touch-action');
String get overflowY => getPropertyValue('overflow-y');
String get overflowX => getPropertyValue('overflow-x');
String get outline => getPropertyValue('outline');

external String getPropertyValue(String property);
void setProperty(String propertyName, String value, [String? priority]) {
Expand All @@ -359,7 +389,6 @@ class DomHTMLElement extends DomElement {}

extension DomHTMLElementExtension on DomHTMLElement {
int get offsetWidth => js_util.getProperty<num>(this, 'offsetWidth') as int;
external void focus();
}

@JS()
Expand Down Expand Up @@ -927,11 +956,19 @@ class DomMouseEvent extends DomUIEvent {}
extension DomMouseEventExtension on DomMouseEvent {
external num get clientX;
external num get clientY;
external num get offsetX;
external num get offsetY;
DomPoint get client => DomPoint(clientX, clientY);
DomPoint get offset => DomPoint(offsetX, offsetY);
external int get button;
external int? get buttons;
external bool getModifierState(String keyArg);
}

DomMouseEvent createDomMouseEvent(String type, [Map<dynamic, dynamic>? init]) =>
js_util.callConstructor(domGetConstructor('MouseEvent')!,
<Object>[type, if (init != null) js_util.jsify(init)]);

@JS()
@staticInterop
class DomPointerEvent extends DomMouseEvent {}
Expand All @@ -947,6 +984,11 @@ extension DomPointerEventExtension on DomPointerEvent {
this, 'getCoalescedEvents', <Object>[]).cast<DomPointerEvent>();
}

DomPointerEvent createDomPointerEvent(String type,
[Map<dynamic, dynamic>? init]) =>
js_util.callConstructor(domGetConstructor('PointerEvent')!,
<Object>[type, if (init != null) js_util.jsify(init)]);

@JS()
@staticInterop
class DomWheelEvent extends DomMouseEvent {}
Expand All @@ -973,8 +1015,37 @@ class DomTouch {}

extension DomTouchExtension on DomTouch {
external int? get identifier;
external num? get clientX;
external num? get clientY;
external num get clientX;
external num get clientY;
DomPoint get client => DomPoint(clientX, clientY);
}

DomTouchEvent createDomTouchEvent(String type, [Map<dynamic, dynamic>? init]) =>
js_util.callConstructor(domGetConstructor('TouchEvent')!,
<Object>[type, if (init != null) js_util.jsify(init)]);

@JS()
@staticInterop
class DomHTMLInputElement extends DomHTMLElement {}

extension DomHTMLInputElementExtension on DomHTMLInputElement {
external set type(String? value);
external set max(String? value);
external set min(String value);
external set value(String? value);
external String? get value;
external bool? get disabled;
external set disabled(bool? value);
}

DomHTMLInputElement createDomHTMLInputElement() =>
domDocument.createElement('input') as DomHTMLInputElement;

class DomPoint {
final num x;
final num y;

DomPoint(this.x, this.y);
}

Object? domGetConstructor(String constructorName) =>
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/embedder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ class FlutterViewEmbedder {

final html.Element _accessibilityPlaceholder = EngineSemanticsOwner
.instance.semanticsHelper
.prepareAccessibilityPlaceholder();
.prepareAccessibilityPlaceholder() as html.Element;

glassPaneElementHostNode.nodes.addAll(<html.Node>[
_accessibilityPlaceholder,
Expand Down
4 changes: 1 addition & 3 deletions lib/web_ui/lib/src/engine/keyboard_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:html' as html;

import 'package:ui/ui.dart' as ui;

import '../engine.dart' show registerHotRestartListener;
Expand Down Expand Up @@ -117,7 +115,7 @@ class KeyboardBinding {
if (_debugLogKeyEvents) {
print(event.type);
}
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event as html.Event)) {
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) {
return handler(event);
}
return null;
Expand Down
7 changes: 3 additions & 4 deletions lib/web_ui/lib/src/engine/pointer_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:html' as html;
import 'dart:math' as math;

import 'package:meta/meta.dart';
Expand Down Expand Up @@ -293,7 +292,7 @@ abstract class _BaseAdapter {
// Report the event to semantics. This information is used to debounce
// browser gestures. Semantics tells us whether it is safe to forward
// the event to the framework.
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event as html.Event)) {
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) {
handler(event);
}
}
Expand Down Expand Up @@ -868,8 +867,8 @@ class _TouchAdapter extends _BaseAdapter {
kind: ui.PointerDeviceKind.touch,
signalKind: ui.PointerSignalKind.none,
device: touch.identifier!,
physicalX: touch.clientX!.toDouble() * ui.window.devicePixelRatio,
physicalY: touch.clientY!.toDouble() * ui.window.devicePixelRatio,
physicalX: touch.clientX.toDouble() * ui.window.devicePixelRatio,
physicalY: touch.clientY.toDouble() * ui.window.devicePixelRatio,
buttons: pressed ? _kPrimaryMouseButton : 0,
pressure: 1.0,
pressureMin: 0.0,
Expand Down
7 changes: 3 additions & 4 deletions lib/web_ui/lib/src/engine/semantics/checkable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
// framework. Currently the framework does not report the
// grouping of radio buttons.

import 'dart:html' as html;

import 'package:ui/ui.dart' as ui;

import '../dom.dart';
import 'semantics.dart';

/// The specific type of checkable control.
Expand Down Expand Up @@ -104,7 +103,7 @@ class Checkable extends RoleManager {

void _updateDisabledAttribute() {
if (semanticsObject.enabledState() == EnabledState.disabled) {
final html.Element element = semanticsObject.element;
final DomElement element = semanticsObject.element;
element
..setAttribute('aria-disabled', 'true')
..setAttribute('disabled', 'true');
Expand All @@ -114,7 +113,7 @@ class Checkable extends RoleManager {
}

void _removeDisabledAttribute() {
final html.Element element = semanticsObject.element;
final DomElement element = semanticsObject.element;
element..removeAttribute('aria-disabled')..removeAttribute('disabled');
}
}
11 changes: 5 additions & 6 deletions lib/web_ui/lib/src/engine/semantics/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:html' as html;

import '../dom.dart';
import 'semantics.dart';

/// Represents semantic objects that deliver information in a visual manner.
Expand All @@ -18,13 +17,13 @@ class ImageRoleManager extends RoleManager {
/// The element with role="img" and aria-label could block access to all
/// children elements, therefore create an auxiliary element and describe the
/// image in that if the semantic object have child nodes.
html.Element? _auxiliaryImageElement;
DomElement? _auxiliaryImageElement;

@override
void update() {
if (semanticsObject.isVisualOnly && semanticsObject.hasChildren) {
if (_auxiliaryImageElement == null) {
_auxiliaryImageElement = html.Element.tag('flt-semantics-img');
_auxiliaryImageElement = domDocument.createElement('flt-semantics-img');
// Absolute positioning and sizing of leaf text elements confuses
// VoiceOver. So we let the browser size the value node. The node will
// still have a bigger tap area. However, if the node is a parent to
Expand Down Expand Up @@ -54,7 +53,7 @@ class ImageRoleManager extends RoleManager {
}
}

void _setLabel(html.Element? element) {
void _setLabel(DomElement? element) {
if (semanticsObject.hasLabel) {
element!.setAttribute('aria-label', semanticsObject.label!);
}
Expand All @@ -69,7 +68,7 @@ class ImageRoleManager extends RoleManager {

void _cleanupElement() {
semanticsObject.setAriaRole('img', false);
semanticsObject.element.attributes.remove('aria-label');
semanticsObject.element.removeAttribute('aria-label');
}

@override
Expand Down
10 changes: 5 additions & 5 deletions lib/web_ui/lib/src/engine/semantics/incrementable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:html' as html;

import 'package:ui/ui.dart' as ui;

import '../dom.dart';
import '../platform_dispatcher.dart';
import '../safe_browser_api.dart';
import 'semantics.dart';

/// Adds increment/decrement event handling to a semantics object.
Expand All @@ -20,7 +20,7 @@ import 'semantics.dart';
/// gestures must be interpreted by the Flutter framework.
class Incrementable extends RoleManager {
/// The HTML element used to render semantics to the browser.
final html.InputElement _element = html.InputElement();
final DomHTMLInputElement _element = createDomHTMLInputElement();

/// The value used by the input element.
///
Expand Down Expand Up @@ -49,7 +49,7 @@ class Incrementable extends RoleManager {
_element.type = 'range';
_element.setAttribute('role', 'slider');

_element.addEventListener('change', (_) {
_element.addEventListener('change', allowInterop((_) {
if (_element.disabled!) {
return;
}
Expand All @@ -64,7 +64,7 @@ class Incrementable extends RoleManager {
EnginePlatformDispatcher.instance.invokeOnSemanticsAction(
semanticsObject.id, ui.SemanticsAction.decrease, null);
}
});
}));

// Store the callback as a closure because Dart does not guarantee that
// tear-offs produce the same function object.
Expand Down
9 changes: 4 additions & 5 deletions lib/web_ui/lib/src/engine/semantics/label_and_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:html' as html;

import 'package:ui/ui.dart' as ui;

import '../configuration.dart';
import '../dom.dart';
import 'semantics.dart';

/// Renders [_label] and [_value] to the semantics DOM.
Expand Down Expand Up @@ -47,7 +46,7 @@ class LabelAndValue extends RoleManager {
/// its label is not reachable via accessibility focus. This happens, for
/// example in popup dialogs, such as the alert dialog. The text of the
/// alert is supplied as a label on the parent node.
html.Element? _auxiliaryValueElement;
DomElement? _auxiliaryValueElement;

@override
void update() {
Expand Down Expand Up @@ -90,7 +89,7 @@ class LabelAndValue extends RoleManager {
}

if (_auxiliaryValueElement == null) {
_auxiliaryValueElement = html.Element.tag('flt-semantics-value');
_auxiliaryValueElement = domDocument.createElement('flt-semantics-value');
// Absolute positioning and sizing of leaf text elements confuses
// VoiceOver. So we let the browser size the value node. The node will
// still have a bigger tap area. However, if the node is a parent to other
Expand Down Expand Up @@ -119,7 +118,7 @@ class LabelAndValue extends RoleManager {
_auxiliaryValueElement!.remove();
_auxiliaryValueElement = null;
}
semanticsObject.element.attributes.remove('aria-label');
semanticsObject.element.removeAttribute('aria-label');
semanticsObject.setAriaRole('heading', false);
}

Expand Down
3 changes: 2 additions & 1 deletion lib/web_ui/lib/src/engine/semantics/live_region.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import '../dom.dart';
import 'semantics.dart';

/// Manages semantics configurations that represent live regions.
Expand Down Expand Up @@ -31,7 +32,7 @@ class LiveRegion extends RoleManager {
}

void _cleanupDom() {
semanticsObject.element.attributes.remove('aria-live');
semanticsObject.element.removeAttribute('aria-live');
}

@override
Expand Down
Loading