[web] Migrate Flutter Web DOM usage to JS static interop - 11. (#33190)

diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart
index b7d94b5..c461219 100644
--- a/lib/web_ui/lib/src/engine/dom.dart
+++ b/lib/web_ui/lib/src/engine/dom.dart
@@ -15,6 +15,8 @@
 /// class name with `Dom`.
 /// NOTE: After the new static interop DOM API is released in the Dart SDK,
 /// these classes will be replaced by typedefs.
+/// NOTE: Currently, optional parameters do not behave as expected.
+/// For the time being, avoid passing optional parameters directly to JS.
 
 @JS()
 @staticInterop
@@ -59,7 +61,9 @@
 extension DomDocumentExtension on DomDocument {
   external DomElement? querySelector(String selectors);
   external /* List<Node> */ List<Object?> querySelectorAll(String selectors);
-  external DomElement createElement(String name, [dynamic options]);
+  DomElement createElement(String name, [Object? options]) =>
+      js_util.callMethod(this, 'createElement',
+          <Object>[name, if (options != null) options]) as DomElement;
   external DomHTMLScriptElement? get currentScript;
   external DomElement createElementNS(
       String namespaceURI, String qualifiedName);
@@ -82,10 +86,21 @@
 class DomEventTarget {}
 
 extension DomEventTargetExtension on DomEventTarget {
-  external void addEventListener(String type, DomEventListener? listener,
-      [bool? useCapture]);
-  external void removeEventListener(String type, DomEventListener? listener,
-      [bool? useCapture]);
+  void addEventListener(String type, DomEventListener? listener,
+      [bool? useCapture]) {
+    if (listener != null) {
+      js_util.callMethod(this, 'addEventListener',
+          <Object>[type, listener, if (useCapture != null) useCapture]);
+    }
+  }
+
+  void removeEventListener(String type, DomEventListener? listener,
+      [bool? useCapture]) {
+    if (listener != null) {
+      js_util.callMethod(this, 'removeEventListener',
+          <Object>[type, listener, if (useCapture != null) useCapture]);
+    }
+  }
 }
 
 typedef DomEventListener = void Function(DomEvent event);
@@ -139,6 +154,7 @@
   external /* List<DomElement> */ List<Object?> get children;
   external String get id;
   external set id(String id);
+  external set spellcheck(bool? value);
   external DomCSSStyleDeclaration get style;
   external void append(DomNode node);
   external String? getAttribute(String attributeName);
@@ -152,14 +168,22 @@
 class DomCSSStyleDeclaration {}
 
 extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
-  set width(String value) => setProperty('width', value);
-  set height(String value) => setProperty('height', value);
-  set position(String value) => setProperty('position', value);
-  set clip(String value) => setProperty('clip', value);
-  set clipPath(String value) => setProperty('clip-path', value);
-  set transform(String value) => setProperty('transform', value);
-  set transformOrigin(String value) => setProperty('transform-origin', value);
-  set opacity(String value) => setProperty('opacity', value);
+  set width(String value) => setProperty('width', value, '');
+  set height(String value) => setProperty('height', value, '');
+  set position(String value) => setProperty('position', value, '');
+  set clip(String value) => setProperty('clip', value, '');
+  set clipPath(String value) => setProperty('clip-path', value, '');
+  set transform(String value) => setProperty('transform', value, '');
+  set transformOrigin(String value) =>
+      setProperty('transform-origin', value, '');
+  set opacity(String value) => setProperty('opacity', value, '');
+  set color(String value) => setProperty('color', value, '');
+  set top(String value) => setProperty('top', value, '');
+  set left(String value) => setProperty('left', value, '');
+  set right(String value) => setProperty('right', value, '');
+  set bottom(String value) => setProperty('bottom', value, '');
+  set backgroundColor(String value) =>
+      setProperty('background-color', value, '');
   String get width => getPropertyValue('width');
   String get height => getPropertyValue('height');
   String get position => getPropertyValue('position');
@@ -168,10 +192,21 @@
   String get transform => getPropertyValue('transform');
   String get transformOrigin => getPropertyValue('transform-origin');
   String get opacity => getPropertyValue('opacity');
+  String get color => getPropertyValue('color');
+  String get top => getPropertyValue('top');
+  String get left => getPropertyValue('left');
+  String get right => getPropertyValue('right');
+  String get bottom => getPropertyValue('bottom');
+  String get backgroundColor => getPropertyValue('background-color');
 
   external String getPropertyValue(String property);
-  external void setProperty(String propertyName, String value,
-      [String priority]);
+  void setProperty(String propertyName, String value, [String? priority]) {
+    priority ??= '';
+    js_util.callMethod(
+        this, 'setProperty', <Object>[propertyName, value, priority]);
+  }
+
+  external String removeProperty(String property);
 }
 
 @JS()
@@ -253,7 +288,8 @@
   external set width(int? value);
   external int? get height;
   external set height(int? value);
-  external String toDataURL([String? type]);
+  String toDataURL([String? type]) =>
+      js_util.callMethod(this, 'toDataURL', <Object>[if (type != null) type]);
 
   Object? getContext(String contextType, [Map<dynamic, dynamic>? attributes]) {
     return js_util.callMethod(this, 'getContext', <Object?>[
@@ -307,7 +343,8 @@
   external String get responseType;
   external int? get status;
   external set responseType(String value);
-  external void open(String method, String url, [bool? async]);
+  void open(String method, String url, [bool? async]) => js_util.callMethod(
+      this, 'open', <Object>[method, url, if (async != null) async]);
   external void send();
 }
 
diff --git a/lib/web_ui/lib/src/engine/embedder.dart b/lib/web_ui/lib/src/engine/embedder.dart
index 9c30eb6..0116e1a 100644
--- a/lib/web_ui/lib/src/engine/embedder.dart
+++ b/lib/web_ui/lib/src/engine/embedder.dart
@@ -200,7 +200,7 @@
       hasAutofillOverlay: browserHasAutofillOverlay(),
     );
 
-    final html.BodyElement bodyElement = html.document.body!;
+    final DomHTMLBodyElement bodyElement = domDocument.body!;
 
     bodyElement.setAttribute(
       'flt-renderer',
@@ -270,8 +270,8 @@
     // IMPORTANT: the glass pane element must come after the scene element in the DOM node list so
     //            it can intercept input events.
     _glassPaneElement?.remove();
-    final html.Element glassPaneElement = html.document.createElement(_glassPaneTagName);
-    _glassPaneElement = glassPaneElement;
+    final DomElement glassPaneElement = domDocument.createElement(_glassPaneTagName);
+    _glassPaneElement = glassPaneElement as html.Element;
     glassPaneElement.style
       ..position = 'absolute'
       ..top = '0'
@@ -285,7 +285,8 @@
 
     // Create a [HostNode] under the glass pane element, and attach everything
     // there, instead of directly underneath the glass panel.
-    final HostNode glassPaneElementHostNode = _createHostNode(glassPaneElement);
+    final HostNode glassPaneElementHostNode = _createHostNode(glassPaneElement
+        as html.Element);
     _glassPaneShadow = glassPaneElementHostNode;
 
     // Don't allow the scene to receive pointer events.
@@ -335,8 +336,8 @@
       _sceneHostElement!.style.opacity = '0.3';
     }
 
-    PointerBinding.initInstance(glassPaneElement);
-    KeyboardBinding.initInstance(glassPaneElement);
+    PointerBinding.initInstance(glassPaneElement as html.Element);
+    KeyboardBinding.initInstance(glassPaneElement as html.Element);
 
     if (html.window.visualViewport == null && isWebKit) {
       // Older Safari versions sometimes give us bogus innerWidth/innerHeight
diff --git a/lib/web_ui/lib/src/engine/html/backdrop_filter.dart b/lib/web_ui/lib/src/engine/html/backdrop_filter.dart
index 73e7d71..065f58f 100644
--- a/lib/web_ui/lib/src/engine/html/backdrop_filter.dart
+++ b/lib/web_ui/lib/src/engine/html/backdrop_filter.dart
@@ -7,6 +7,7 @@
 import 'package:ui/ui.dart' as ui;
 
 import '../browser_detection.dart';
+import '../dom.dart';
 import '../util.dart';
 import '../vector_math.dart';
 import 'shaders/shader.dart';
@@ -25,9 +26,9 @@
   /// [rootElement] is used to host child in front of [filterElement] that
   /// is transformed to cover background.
   @override
-  html.Element? get childContainer => _childContainer;
-  html.Element? _childContainer;
-  html.Element? _filterElement;
+  html.Element? get childContainer => _childContainer as html.Element?;
+  DomElement? _childContainer;
+  DomElement? _filterElement;
   ui.Rect? _activeClipBounds;
   // Cached inverted transform for [transform].
   late Matrix4 _invertedTransform;
@@ -44,18 +45,19 @@
 
   @override
   html.Element createElement() {
-    final html.Element element = defaultCreateElement('flt-backdrop')
-      ..style.transformOrigin = '0 0 0';
-    _childContainer = html.Element.tag('flt-backdrop-interior');
+    final DomElement element = defaultCreateElement('flt-backdrop') as
+        DomElement;
+    element.style.transformOrigin = '0 0 0';
+    _childContainer = createDomElement('flt-backdrop-interior');
     _childContainer!.style.position = 'absolute';
     if (debugExplainSurfaceStats) {
       // This creates an additional interior element. Count it too.
       surfaceStatsFor(this).allocatedDomNodeCount++;
     }
-    _filterElement = defaultCreateElement('flt-backdrop-filter');
+    _filterElement = defaultCreateElement('flt-backdrop-filter') as DomElement;
     _filterElement!.style.transformOrigin = '0 0 0';
     element..append(_filterElement!)..append(_childContainer!);
-    return element;
+    return element as html.Element;
   }
 
   @override
@@ -102,7 +104,7 @@
       }
       parentSurface = parentSurface.parent;
     }
-    final html.CssStyleDeclaration filterElementStyle = _filterElement!.style;
+    final DomCSSStyleDeclaration filterElementStyle = _filterElement!.style;
     filterElementStyle
       ..position = 'absolute'
       ..left = '${left}px'
diff --git a/lib/web_ui/lib/src/engine/html/offset.dart b/lib/web_ui/lib/src/engine/html/offset.dart
index a23b512..ff75d47 100644
--- a/lib/web_ui/lib/src/engine/html/offset.dart
+++ b/lib/web_ui/lib/src/engine/html/offset.dart
@@ -6,6 +6,7 @@
 
 import 'package:ui/ui.dart' as ui;
 
+import '../dom.dart';
 import '../util.dart';
 import '../vector_math.dart';
 import 'surface.dart';
@@ -41,10 +42,10 @@
 
   @override
   html.Element createElement() {
-    final html.Element element = html.document.createElement('flt-offset');
+    final DomElement element = domDocument.createElement('flt-offset');
     setElementStyle(element, 'position', 'absolute');
     setElementStyle(element, 'transform-origin', '0 0 0');
-    return element;
+    return element as html.Element;
   }
 
   @override
diff --git a/lib/web_ui/lib/src/engine/html/opacity.dart b/lib/web_ui/lib/src/engine/html/opacity.dart
index 4348fc3..9fbc00b 100644
--- a/lib/web_ui/lib/src/engine/html/opacity.dart
+++ b/lib/web_ui/lib/src/engine/html/opacity.dart
@@ -6,6 +6,7 @@
 
 import 'package:ui/ui.dart' as ui;
 
+import '../dom.dart';
 import '../util.dart';
 import '../vector_math.dart';
 import 'surface.dart';
@@ -43,15 +44,15 @@
 
   @override
   html.Element createElement() {
-    final html.Element element = html.document.createElement('flt-opacity');
+    final DomElement element = domDocument.createElement('flt-opacity');
     setElementStyle(element, 'position', 'absolute');
     setElementStyle(element, 'transform-origin', '0 0 0');
-    return element;
+    return element as html.Element;
   }
 
   @override
   void apply() {
-    final html.Element element = rootElement!;
+    final DomElement element = rootElement! as DomElement;
     setElementStyle(element, 'opacity', '${alpha / 255}');
     element.style.transform = 'translate(${offset.dx}px, ${offset.dy}px)';
   }
diff --git a/lib/web_ui/lib/src/engine/html/transform.dart b/lib/web_ui/lib/src/engine/html/transform.dart
index d3e1f8f..41b3d93 100644
--- a/lib/web_ui/lib/src/engine/html/transform.dart
+++ b/lib/web_ui/lib/src/engine/html/transform.dart
@@ -7,6 +7,7 @@
 
 import 'package:ui/ui.dart' as ui;
 
+import '../dom.dart';
 import '../util.dart';
 import '../vector_math.dart';
 import 'surface.dart';
@@ -42,10 +43,10 @@
 
   @override
   html.Element createElement() {
-    final html.Element element = html.document.createElement('flt-transform');
+    final DomElement element = domDocument.createElement('flt-transform');
     setElementStyle(element, 'position', 'absolute');
     setElementStyle(element, 'transform-origin', '0 0 0');
-    return element;
+    return element as html.Element;
   }
 
   @override
diff --git a/lib/web_ui/lib/src/engine/mouse_cursor.dart b/lib/web_ui/lib/src/engine/mouse_cursor.dart
index 3e5979b..1d05a24 100644
--- a/lib/web_ui/lib/src/engine/mouse_cursor.dart
+++ b/lib/web_ui/lib/src/engine/mouse_cursor.dart
@@ -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 'embedder.dart';
 import 'util.dart';
 
@@ -67,7 +68,7 @@
 
   void activateSystemCursor(String? kind) {
     setElementStyle(
-      flutterViewEmbedder.glassPaneElement!,
+      flutterViewEmbedder.glassPaneElement! as DomElement,
       'cursor',
       _mapKindToCssValue(kind),
     );
diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart
index b76328b..3316d8c 100644
--- a/lib/web_ui/lib/src/engine/text/paragraph.dart
+++ b/lib/web_ui/lib/src/engine/text/paragraph.dart
@@ -8,6 +8,7 @@
 import 'package:ui/ui.dart' as ui;
 
 import '../browser_detection.dart';
+import '../dom.dart';
 import '../embedder.dart';
 import '../util.dart';
 import 'layout_service.dart';
@@ -743,7 +744,7 @@
       if (textDecoration != null) {
         if (browserEngine == BrowserEngine.webkit) {
           setElementStyle(
-              element, '-webkit-text-decoration', textDecoration);
+              element as DomElement, '-webkit-text-decoration', textDecoration);
         } else {
           cssStyle.textDecoration = textDecoration;
         }
diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart
index d2fe1c5..eb46f40 100644
--- a/lib/web_ui/lib/src/engine/util.dart
+++ b/lib/web_ui/lib/src/engine/util.dart
@@ -656,7 +656,7 @@
 /// [name] is the name of the property. [value] is the value of the property.
 /// If [value] is null, removes the style property.
 void setElementStyle(
-    html.Element element, String name, String? value) {
+    DomElement element, String name, String? value) {
   if (value == null) {
     element.style.removeProperty(name);
   } else {
diff --git a/lib/web_ui/test/engine/util_test.dart b/lib/web_ui/test/engine/util_test.dart
index 5509919..57b2ac5 100644
--- a/lib/web_ui/test/engine/util_test.dart
+++ b/lib/web_ui/test/engine/util_test.dart
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:html' as html;
 import 'dart:typed_data';
 
 import 'package:test/bootstrap/browser.dart';
@@ -107,13 +106,13 @@
   });
 
   test('can set style properties on elements', () {
-    final html.Element element = html.document.createElement('div');
+    final DomElement element = domDocument.createElement('div');
     setElementStyle(element, 'color', 'red');
     expect(element.style.color, 'red');
   });
 
   test('can remove style properties from elements', () {
-    final html.Element element = html.document.createElement('div');
+    final DomElement element = domDocument.createElement('div');
     setElementStyle(element, 'color', 'blue');
     expect(element.style.color, 'blue');
     setElementStyle(element, 'color', null);