diff --git a/packages/flutter/lib/src/foundation/_platform_web.dart b/packages/flutter/lib/src/foundation/_platform_web.dart index babeee421a..30f757012e 100644 --- a/packages/flutter/lib/src/foundation/_platform_web.dart +++ b/packages/flutter/lib/src/foundation/_platform_web.dart @@ -4,7 +4,7 @@ import 'dart:ui_web' as ui_web; -import '../services/dom.dart'; +import 'package:web/web.dart' as web; import 'platform.dart' as platform; @@ -38,7 +38,7 @@ final platform.TargetPlatform? _testPlatform = () { // 0.20ms. As `defaultTargetPlatform` is routinely called dozens of times per // frame this value should be cached. final platform.TargetPlatform _browserPlatform = () { - final String navigatorPlatform = domWindow.navigator.platform?.toLowerCase() ?? ''; + final String navigatorPlatform = web.window.navigator.platform.toLowerCase(); if (navigatorPlatform.startsWith('mac')) { return platform.TargetPlatform.macOS; } @@ -58,7 +58,7 @@ final platform.TargetPlatform _browserPlatform = () { // indicates that a device has a "fine pointer" (mouse) as the primary // pointing device, then we'll assume desktop linux, and otherwise we'll // assume Android. - if (domWindow.matchMedia('only screen and (pointer: fine)').matches) { + if (web.window.matchMedia('only screen and (pointer: fine)').matches) { return platform.TargetPlatform.linux; } return platform.TargetPlatform.android; diff --git a/packages/flutter/lib/src/painting/_network_image_web.dart b/packages/flutter/lib/src/painting/_network_image_web.dart index 95e97e0c28..af45a0eb33 100644 --- a/packages/flutter/lib/src/painting/_network_image_web.dart +++ b/packages/flutter/lib/src/painting/_network_image_web.dart @@ -7,17 +7,17 @@ import 'dart:js_interop'; import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; +import 'package:web/web.dart' as web; -import '../services/dom.dart'; import 'image_provider.dart' as image_provider; import 'image_stream.dart'; /// Creates a type for an overridable factory function for testing purposes. -typedef HttpRequestFactory = DomXMLHttpRequest Function(); +typedef HttpRequestFactory = web.XMLHttpRequest Function(); /// Default HTTP client. -DomXMLHttpRequest _httpClient() { - return DomXMLHttpRequest(); +web.XMLHttpRequest _httpClient() { + return web.XMLHttpRequest(); } /// Creates an overridable factory function. @@ -135,9 +135,9 @@ class NetworkImage // We use a different method when headers are set because the // `ui.webOnlyInstantiateImageCodecFromUrl` method is not capable of handling headers. if (isCanvasKit || containsNetworkImageHeaders) { - final Completer completer = - Completer(); - final DomXMLHttpRequest request = httpRequestFactory(); + final Completer completer = + Completer(); + final web.XMLHttpRequest request = httpRequestFactory(); request.open('GET', key.url, true); request.responseType = 'arraybuffer'; @@ -147,9 +147,9 @@ class NetworkImage }); } - request.addEventListener('load', createDomEventListener((DomEvent e) { - final int? status = request.status; - final bool accepted = status! >= 200 && status < 300; + request.addEventListener('load', (web.Event e) { + final int status = request.status; + final bool accepted = status >= 200 && status < 300; final bool fileUri = status == 0; // file:// URIs have status of 0. final bool notModified = status == 304; final bool unknownRedirect = status > 307 && status < 400; @@ -161,12 +161,11 @@ class NetworkImage } else { completer.completeError(e); throw image_provider.NetworkImageLoadException( - statusCode: request.status ?? 400, uri: resolved); + statusCode: status, uri: resolved); } - })); + }.toJS); - request.addEventListener('error', - createDomEventListener(completer.completeError)); + request.addEventListener('error', completer.completeError.toJS); request.send(); @@ -176,7 +175,7 @@ class NetworkImage if (bytes.lengthInBytes == 0) { throw image_provider.NetworkImageLoadException( - statusCode: request.status!, uri: resolved); + statusCode: request.status, uri: resolved); } if (decode != null) { diff --git a/packages/flutter/lib/src/services/dom.dart b/packages/flutter/lib/src/services/dom.dart deleted file mode 100644 index 42235a84cf..0000000000 --- a/packages/flutter/lib/src/services/dom.dart +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright 2014 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 'dart:js_interop'; - -/// This file includes static interop helpers for Flutter Web. -// TODO(joshualitt): This file will eventually be removed, -// https://github.com/flutter/flutter/issues/113402. - -/// [DomWindow] interop object. -@JS() -@staticInterop -class DomWindow {} - -/// [DomWindow] required extension. -extension DomWindowExtension on DomWindow { - @JS('matchMedia') - external DomMediaQueryList _matchMedia(JSString? query); - - /// Returns a [DomMediaQueryList] of the media that matches [query]. - DomMediaQueryList matchMedia(String? query) => _matchMedia(query?.toJS); - - /// Returns the [DomNavigator] associated with this window. - external DomNavigator get navigator; - - /// Gets the current selection. - external DomSelection? getSelection(); -} - -/// The underyling window. -@JS('window') -external DomWindow get domWindow; - -/// [DomMediaQueryList] interop object. -@JS() -@staticInterop -class DomMediaQueryList {} - -/// [DomMediaQueryList] required extension. -extension DomMediaQueryListExtension on DomMediaQueryList { - @JS('matches') - external JSBoolean get _matches; - - /// Whether or not the query matched. - bool get matches => _matches.toDart; -} - -/// [DomNavigator] interop object. -@JS() -@staticInterop -class DomNavigator {} - -/// [DomNavigator] required extension. -extension DomNavigatorExtension on DomNavigator { - @JS('platform') - external JSString? get _platform; - - /// The underyling platform string. - String? get platform => _platform?.toDart; -} - -/// A DOM event target. -@JS() -@staticInterop -class DomEventTarget {} - -/// [DomEventTarget]'s required extension. -extension DomEventTargetExtension on DomEventTarget { - @JS('addEventListener') - external JSVoid _addEventListener1(JSString type, DomEventListener? listener); - - @JS('addEventListener') - external JSVoid _addEventListener2( - JSString type, DomEventListener? listener, JSBoolean useCapture); - - /// Adds an event listener to this event target. - @JS('addEventListener') - void addEventListener(String type, DomEventListener? listener, - [bool? useCapture]) { - if (listener != null) { - if (useCapture == null) { - _addEventListener1(type.toJS, listener); - } else { - _addEventListener2(type.toJS, listener, useCapture.toJS); - } - } - } -} - -/// [DomXMLHttpRequest] interop class. -@JS('XMLHttpRequest') -@staticInterop -class DomXMLHttpRequest extends DomEventTarget { - /// Constructor for [DomXMLHttpRequest]. - external factory DomXMLHttpRequest(); -} - -/// [DomXMLHttpRequest] extension. -extension DomXMLHttpRequestExtension on DomXMLHttpRequest { - /// Gets the response. - external JSAny? get response; - - @JS('responseText') - external JSString? get _responseText; - - /// Gets the response text. - String? get responseText => _responseText?.toDart; - - @JS('responseType') - external JSString get _responseType; - - /// Gets the response type. - String get responseType => _responseType.toDart; - - @JS('status') - external JSNumber? get _status; - - /// Gets the status. - int? get status => _status?.toDartInt; - - @JS('responseType') - external set _responseType(JSString value); - - /// Set the response type. - set responseType(String value) => _responseType = value.toJS; - - @JS('setRequestHeader') - external void _setRequestHeader(JSString header, JSString value); - - /// Set the request header. - void setRequestHeader(String header, String value) => - _setRequestHeader(header.toJS, value.toJS); - - @JS('open') - external JSVoid _open(JSString method, JSString url, JSBoolean isAsync); - - /// Open the request. - void open(String method, String url, bool isAsync) => - _open(method.toJS, url.toJS, isAsync.toJS); - - /// Send the request. - external JSVoid send(); -} - -/// Type for event listener. -typedef DartDomEventListener = JSVoid Function(DomEvent event); - -/// The type of [JSFunction] expected as an `EventListener`. -@JS() -@staticInterop -class DomEventListener {} - -/// Creates a [DomEventListener] from a [DartDomEventListener]. -DomEventListener createDomEventListener(DartDomEventListener listener) => - listener.toJS as DomEventListener; - -/// [DomEvent] interop object. -@JS() -@staticInterop -class DomEvent {} - -/// [DomEvent] required extension. -extension DomEventExtension on DomEvent { - @JS('type') - external JSString get _type; - - /// Get the event type. - String get type => _type.toDart; - - /// Initialize an event. - external JSVoid initEvent( - JSString type, JSBoolean bubbles, JSBoolean cancelable); -} - -/// [DomProgressEvent] interop object. -@JS() -@staticInterop -class DomProgressEvent extends DomEvent {} - -/// [DomProgressEvent] required extension. -extension DomProgressEventExtension on DomProgressEvent { - @JS('loaded') - external JSNumber? get _loaded; - - /// Amount of work done. - int? get loaded => _loaded?.toDartInt; - - @JS('total') - external JSNumber? get _total; - - /// Total amount of work. - int? get total => _total?.toDartInt; -} - -/// The underlying DOM document. -@JS() -@staticInterop -class DomDocument {} - -/// [DomDocument]'s required extension. -extension DomDocumentExtension on DomDocument { - @JS('createEvent') - external DomEvent _createEvent(JSString eventType); - - /// Creates an event. - DomEvent createEvent(String eventType) => _createEvent(eventType.toJS); - - /// Creates a range. - external DomRange createRange(); - - /// Gets the head element. - external DomHTMLHeadElement? get head; - - /// Creates a [DomElement]. - @JS('createElement') - external DomElement createElement(JSString name); -} - -/// Returns the top level document. -@JS('window.document') -external DomDocument get domDocument; - -/// Creates a new DOM event. -DomEvent createDomEvent(String type, String name) { - final DomEvent event = domDocument.createEvent(type); - event.initEvent(name.toJS, true.toJS, true.toJS); - return event; -} - -/// A Range object. -@JS() -@staticInterop -class DomRange {} - -/// [DomRange]'s required extension. -extension DomRangeExtension on DomRange { - /// Selects the provided node. - external JSVoid selectNode(DomNode node); -} - -/// A node in the DOM. -@JS() -@staticInterop -class DomNode extends DomEventTarget {} - -/// [DomNode]'s required extension. -extension DomNodeExtension on DomNode { - @JS('innerText') - external set _innerText(JSString text); - - /// Sets the innerText of this node. - set innerText(String text) => _innerText = text.toJS; - - /// Appends a node this node. - external JSVoid append(DomNode node); -} - -/// An element in the DOM. -@JS() -@staticInterop -class DomElement extends DomNode {} - -/// [DomElement]'s required extension. -extension DomElementExtension on DomElement { - /// Returns the style of this element. - external DomCSSStyleDeclaration get style; - - /// Returns the class list of this element. - external DomTokenList get classList; -} - -/// An HTML element in the DOM. -@JS() -@staticInterop -class DomHTMLElement extends DomElement {} - -/// A UI event. -@JS() -@staticInterop -class DomUIEvent extends DomEvent {} - -/// A mouse event. -@JS() -@staticInterop -class DomMouseEvent extends DomUIEvent {} - -/// [DomMouseEvent]'s required extension. -extension DomMouseEventExtension on DomMouseEvent { - @JS('offsetX') - external JSNumber get _offsetX; - - /// Returns the current x offset. - num get offsetX => _offsetX.toDartDouble; - - @JS('offsetY') - external JSNumber get _offsetY; - - /// Returns the current y offset. - num get offsetY => _offsetY.toDartDouble; - - @JS('button') - external JSNumber get _button; - - /// Returns the current button. - int get button => _button.toDartInt; -} - -/// A DOM selection. -@JS() -@staticInterop -class DomSelection {} - -/// [DomSelection]'s required extension. -extension DomSelectionExtension on DomSelection { - /// Removes all ranges from this selection. - external JSVoid removeAllRanges(); - - /// Adds a range to this selection. - external JSVoid addRange(DomRange range); -} - -/// A DOM html div element. -@JS() -@staticInterop -class DomHTMLDivElement extends DomHTMLElement {} - -/// Factory constructor for [DomHTMLDivElement]. -DomHTMLDivElement createDomHTMLDivElement() => - domDocument.createElement('div'.toJS) as DomHTMLDivElement; - -/// An html style element. -@JS() -@staticInterop -class DomHTMLStyleElement extends DomHTMLElement {} - -/// [DomHTMLStyleElement]'s required extension. -extension DomHTMLStyleElementExtension on DomHTMLStyleElement { - /// Get's the style sheet of this element. - external DomStyleSheet? get sheet; -} - -/// Factory constructor for [DomHTMLStyleElement]. -DomHTMLStyleElement createDomHTMLStyleElement() => - domDocument.createElement('style'.toJS) as DomHTMLStyleElement; - -/// CSS styles. -@JS() -@staticInterop -class DomCSSStyleDeclaration {} - -/// [DomCSSStyleDeclaration]'s required extension. -extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { - /// Sets the width. - set width(String value) => setProperty('width', value); - - /// Sets the height. - set height(String value) => setProperty('height', value); - - @JS('setProperty') - external JSVoid _setProperty( - JSString propertyName, JSString value, JSString priority); - - /// Sets a CSS property by name. - void setProperty(String propertyName, String value, [String? priority]) { - priority ??= ''; - _setProperty(propertyName.toJS, value.toJS, priority.toJS); - } -} - -/// The HTML head element. -@JS() -@staticInterop -class DomHTMLHeadElement extends DomHTMLElement {} - -/// A DOM style sheet. -@JS() -@staticInterop -class DomStyleSheet {} - -/// A DOM CSS style sheet. -@JS() -@staticInterop -class DomCSSStyleSheet extends DomStyleSheet {} - -/// [DomCSSStyleSheet]'s required extension. -extension DomCSSStyleSheetExtension on DomCSSStyleSheet { - @JS('insertRule') - external JSNumber _insertRule1(JSString rule); - - @JS('insertRule') - external JSNumber _insertRule2(JSString rule, JSNumber index); - - /// Inserts a rule into this style sheet. - int insertRule(String rule, [int? index]) { - if (index == null) { - return _insertRule1(rule.toJS).toDartInt; - } else { - return _insertRule2(rule.toJS, index.toJS).toDartInt; - } - } -} - -/// A list of token. -@JS() -@staticInterop -class DomTokenList {} - -/// [DomTokenList]'s required extension. -extension DomTokenListExtension on DomTokenList { - @JS('add') - external JSVoid _add(JSString value); - - /// Adds a token to this token list. - void add(String value) => _add(value.toJS); -} diff --git a/packages/flutter/lib/src/widgets/_platform_selectable_region_context_menu_web.dart b/packages/flutter/lib/src/widgets/_platform_selectable_region_context_menu_web.dart index 2a5fa323b8..9e29598855 100644 --- a/packages/flutter/lib/src/widgets/_platform_selectable_region_context_menu_web.dart +++ b/packages/flutter/lib/src/widgets/_platform_selectable_region_context_menu_web.dart @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:js_interop'; import 'dart:ui_web' as ui_web; import 'package:flutter/rendering.dart'; +import 'package:web/web.dart' as web; -import '../services/dom.dart'; import 'basic.dart'; import 'framework.dart'; import 'platform_view.dart'; @@ -27,7 +28,7 @@ const String _kClassRule = ''' '''; const int _kRightClickButton = 2; -typedef _WebSelectionCallBack = void Function(DomHTMLElement, DomMouseEvent); +typedef _WebSelectionCallBack = void Function(web.HTMLElement, web.MouseEvent); /// Function signature for `ui_web.platformViewRegistry.registerViewFactory`. @visibleForTesting @@ -80,11 +81,11 @@ class PlatformSelectableRegionContextMenu extends StatelessWidget { // Registers the view factories for the interceptor widgets. static void _register() { assert(_registeredViewType == null); - _registeredViewType = _registerWebSelectionCallback((DomHTMLElement element, DomMouseEvent event) { + _registeredViewType = _registerWebSelectionCallback((web.HTMLElement element, web.MouseEvent event) { final SelectionContainerDelegate? client = _activeClient; if (client != null) { // Converts the html right click event to flutter coordinate. - final Offset localOffset = Offset(event.offsetX.toDouble(), event.offsetY.toDouble()); + final Offset localOffset = Offset(event.offsetX, event.offsetY); final Matrix4 transform = client.getTransformTo(null); final Offset globalOffset = MatrixUtils.transformPoint(transform, localOffset); client.dispatchSelectionEvent(SelectWordSelectionEvent(globalPosition: globalOffset)); @@ -93,9 +94,9 @@ class PlatformSelectableRegionContextMenu extends StatelessWidget { element.innerText = client.getSelectedContent()?.plainText ?? ''; // Programmatically select the dom element in browser. - final DomRange range = domDocument.createRange(); + final web.Range range = web.document.createRange(); range.selectNode(element); - final DomSelection? selection = domWindow.getSelection(); + final web.Selection? selection = web.window.getSelection(); if (selection != null) { selection.removeAllRanges(); selection.addRange(range); @@ -106,26 +107,26 @@ class PlatformSelectableRegionContextMenu extends StatelessWidget { static String _registerWebSelectionCallback(_WebSelectionCallBack callback) { _registerViewFactory(_viewType, (int viewId) { - final DomHTMLElement htmlElement = createDomHTMLDivElement(); + final web.HTMLElement htmlElement = web.document.createElement('div') as web.HTMLElement; htmlElement ..style.width = '100%' ..style.height = '100%' ..classList.add(_kClassName); // Create css style for _kClassName. - final DomHTMLStyleElement styleElement = createDomHTMLStyleElement(); - domDocument.head!.append(styleElement); - final DomCSSStyleSheet sheet = styleElement.sheet! as DomCSSStyleSheet; + final web.HTMLStyleElement styleElement = web.document.createElement('style') as web.HTMLStyleElement; + web.document.head!.append(styleElement); + final web.CSSStyleSheet sheet = styleElement.sheet!; sheet.insertRule(_kClassRule, 0); sheet.insertRule(_kClassSelectionRule, 1); - htmlElement.addEventListener('mousedown', createDomEventListener((DomEvent event) { - final DomMouseEvent mouseEvent = event as DomMouseEvent; + htmlElement.addEventListener('mousedown', (web.Event event) { + final web.MouseEvent mouseEvent = event as web.MouseEvent; if (mouseEvent.button != _kRightClickButton) { return; } callback(htmlElement, mouseEvent); - })); + }.toJS); return htmlElement; }, isVisible: false); return _viewType; diff --git a/packages/flutter/test/painting/_network_image_test_web.dart b/packages/flutter/test/painting/_network_image_test_web.dart index f82a6e1610..307d6f9108 100644 --- a/packages/flutter/test/painting/_network_image_test_web.dart +++ b/packages/flutter/test/painting/_network_image_test_web.dart @@ -5,8 +5,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/painting/_network_image_web.dart'; -import 'package:flutter/src/services/dom.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:web/web.dart' as web; import '../image_data.dart'; import '_test_http_request.dart'; @@ -20,7 +20,7 @@ void runTests() { (WidgetTester tester) async { final TestHttpRequest testHttpRequest = TestHttpRequest() ..status = 200 - ..mockEvent = MockEvent('load', createDomEvent('Event', 'test error')) + ..mockEvent = MockEvent('load', web.Event('test error')) ..response = (Uint8List.fromList(kTransparentImage)).buffer; httpRequestFactory = () { @@ -46,7 +46,7 @@ void runTests() { (WidgetTester tester) async { final TestHttpRequest testHttpRequest = TestHttpRequest() ..status = 404 - ..mockEvent = MockEvent('error', createDomEvent('Event', 'test error')); + ..mockEvent = MockEvent('error', web.Event('test error')); httpRequestFactory = () { @@ -64,14 +64,14 @@ void runTests() { ); await tester.pumpWidget(image); - expect((tester.takeException() as DomProgressEvent).type, 'test error'); + expect((tester.takeException() as web.ProgressEvent).type, 'test error'); }); testWidgets('loads an image from the network with empty response', (WidgetTester tester) async { final TestHttpRequest testHttpRequest = TestHttpRequest() ..status = 200 - ..mockEvent = MockEvent('load', createDomEvent('Event', 'test error')) + ..mockEvent = MockEvent('load', web.Event('test error')) ..response = (Uint8List.fromList([])).buffer; httpRequestFactory = () { diff --git a/packages/flutter/test/painting/_test_http_request.dart b/packages/flutter/test/painting/_test_http_request.dart index ccfbf1d752..86e03ae80c 100644 --- a/packages/flutter/test/painting/_test_http_request.dart +++ b/packages/flutter/test/painting/_test_http_request.dart @@ -4,16 +4,16 @@ import 'dart:js_interop'; -import 'package:flutter/src/services/dom.dart'; +import 'package:web/web.dart' as web; /// Defines a new property on an Object. @JS('Object.defineProperty') -external JSVoid objectDefineProperty(JSAny o, JSString symbol, JSAny desc); +external void objectDefineProperty(JSAny o, String symbol, JSAny desc); void createGetter(JSAny mock, String key, JSAny? Function() get) { objectDefineProperty( mock, - key.toJS, + key, { 'get': (() => get()).toJS, }.jsify()!, @@ -35,6 +35,8 @@ class DomXMLHttpRequestMock { }); } +typedef _DartDomEventListener = JSVoid Function(web.Event event); + class TestHttpRequest { TestHttpRequest() { _mock = DomXMLHttpRequestMock( @@ -58,26 +60,26 @@ class TestHttpRequest { Object? response; Map get responseHeaders => headers; - JSVoid open(JSString method, JSString url, JSBoolean async) {} + JSVoid open(String method, String url, bool async) {} JSVoid send() {} - JSVoid setRequestHeader(JSString name, JSString value) { - headers[name.toDart] = value.toDart; + JSVoid setRequestHeader(String name, String value) { + headers[name] = value; } - JSVoid addEventListener(JSString type, DomEventListener listener) { - if (type.toDart == mockEvent?.type) { - final DartDomEventListener dartListener = - (listener as JSExportedDartFunction).toDart as DartDomEventListener; + JSVoid addEventListener(String type, web.EventListener listener) { + if (type == mockEvent?.type) { + final _DartDomEventListener dartListener = + (listener as JSExportedDartFunction).toDart as _DartDomEventListener; dartListener(mockEvent!.event); } } - DomXMLHttpRequest getMock() => _mock as DomXMLHttpRequest; + web.XMLHttpRequest getMock() => _mock as web.XMLHttpRequest; } class MockEvent { MockEvent(this.type, this.event); final String type; - final DomEvent event; + final web.Event event; }