mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[web] Migrate Flutter Web DOM usage to JS static interop - 24. (flutter/engine#33352)
This commit is contained in:
parent
71b57d8468
commit
65e48da91c
@ -26,8 +26,10 @@ extension DomWindowExtension on DomWindow {
|
||||
external DomConsole get console;
|
||||
external num get devicePixelRatio;
|
||||
external DomDocument get document;
|
||||
external DomHistory get history;
|
||||
external int? get innerHeight;
|
||||
external int? get innerWidth;
|
||||
external DomLocation get location;
|
||||
external DomNavigator get navigator;
|
||||
external DomVisualViewport? get visualViewport;
|
||||
external DomPerformance get performance;
|
||||
@ -61,7 +63,7 @@ extension DomNavigatorExtension on DomNavigator {
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomDocument {}
|
||||
class DomDocument extends DomNode {}
|
||||
|
||||
extension DomDocumentExtension on DomDocument {
|
||||
external DomElement? get documentElement;
|
||||
@ -142,6 +144,7 @@ extension DomProgressEventExtension on DomProgressEvent {
|
||||
class DomNode extends DomEventTarget {}
|
||||
|
||||
extension DomNodeExtension on DomNode {
|
||||
external String? get baseUri;
|
||||
external DomNode? get firstChild;
|
||||
external String get innerText;
|
||||
external DomNode? get lastChild;
|
||||
@ -691,6 +694,67 @@ extension DomKeyboardEventExtension on DomKeyboardEvent {
|
||||
external bool getModifierState(String keyArg);
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomHistory {}
|
||||
|
||||
extension DomHistoryExtension on DomHistory {
|
||||
dynamic get state => js_util.dartify(js_util.getProperty(this, 'state'));
|
||||
external void go([int? delta]);
|
||||
void pushState(dynamic data, String title, String? url) =>
|
||||
js_util.callMethod(this, 'pushState', <Object?>[
|
||||
if (data is Map || data is Iterable) js_util.jsify(data) else data,
|
||||
title,
|
||||
url
|
||||
]);
|
||||
void replaceState(dynamic data, String title, String? url) =>
|
||||
js_util.callMethod(this, 'replaceState', <Object?>[
|
||||
if (data is Map || data is Iterable) js_util.jsify(data) else data,
|
||||
title,
|
||||
url
|
||||
]);
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomLocation {}
|
||||
|
||||
extension DomLocationExtension on DomLocation {
|
||||
external String? get pathname;
|
||||
external String? get search;
|
||||
// We have to change the name here because 'hash' is inherited from [Object].
|
||||
String get locationHash => js_util.getProperty(this, 'hash');
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomPopStateEvent extends DomEvent {}
|
||||
|
||||
DomPopStateEvent createDomPopStateEvent(
|
||||
String type, Map<Object?, Object?>? eventInitDict) =>
|
||||
domCallConstructorString('PopStateEvent', <Object>[
|
||||
type,
|
||||
if (eventInitDict != null) js_util.jsify(eventInitDict)
|
||||
])! as DomPopStateEvent;
|
||||
|
||||
extension DomPopStateEventExtension on DomPopStateEvent {
|
||||
dynamic get state => js_util.dartify(js_util.getProperty(this, 'state'));
|
||||
}
|
||||
|
||||
Object? domGetConstructor(String constructorName) =>
|
||||
js_util.getProperty(domWindow, constructorName);
|
||||
|
||||
Object? domCallConstructorString(String constructorName, List<Object?> args) {
|
||||
final Object? constructor = domGetConstructor(constructorName);
|
||||
if (constructor == null) {
|
||||
return null;
|
||||
}
|
||||
return js_util.callConstructor(constructor, args);
|
||||
}
|
||||
|
||||
bool domInstanceOfString(Object? element, String objectType) =>
|
||||
js_util.instanceof(element, domGetConstructor(objectType)!);
|
||||
|
||||
/// [_DomElementList] is the shared interface for APIs that return either
|
||||
/// `NodeList` or `HTMLCollection`. Do *not* add any API to this class that
|
||||
/// isn't support by both JS objects. Furthermore, this is an internal class and
|
||||
@ -741,17 +805,3 @@ class _DomElementListWrapper extends Iterable<DomElement> {
|
||||
@override
|
||||
int get length => elementList.length;
|
||||
}
|
||||
|
||||
Object? domGetConstructor(String constructorName) =>
|
||||
js_util.getProperty(domWindow, constructorName);
|
||||
|
||||
Object? domCallConstructorString(String constructorName, List<Object?> args) {
|
||||
final Object? constructor = domGetConstructor(constructorName);
|
||||
if (constructor == null) {
|
||||
return null;
|
||||
}
|
||||
return js_util.callConstructor(constructor, args);
|
||||
}
|
||||
|
||||
bool domInstanceOfString(Object? element, String objectType) =>
|
||||
js_util.instanceof(element, domGetConstructor(objectType)!);
|
||||
|
||||
@ -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:meta/meta.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../dom.dart';
|
||||
import '../platform_dispatcher.dart';
|
||||
import '../services/message_codec.dart';
|
||||
import '../services/message_codecs.dart';
|
||||
@ -52,9 +51,7 @@ abstract class BrowserHistory {
|
||||
bool _isDisposed = false;
|
||||
|
||||
void _setupStrategy(UrlStrategy strategy) {
|
||||
_unsubscribe = strategy.addPopStateListener(
|
||||
onPopState as html.EventListener,
|
||||
);
|
||||
_unsubscribe = strategy.addPopStateListener(onPopState as DomEventListener);
|
||||
}
|
||||
|
||||
/// Release any resources held by this [BrowserHistory] instance.
|
||||
@ -103,7 +100,7 @@ abstract class BrowserHistory {
|
||||
///
|
||||
/// Subclasses should send appropriate system messages to update the flutter
|
||||
/// applications accordingly.
|
||||
void onPopState(covariant html.PopStateEvent event);
|
||||
void onPopState(covariant DomPopStateEvent event);
|
||||
|
||||
/// Restore any modifications to the html browser history during the lifetime
|
||||
/// of this class.
|
||||
@ -186,7 +183,7 @@ class MultiEntriesBrowserHistory extends BrowserHistory {
|
||||
}
|
||||
|
||||
@override
|
||||
void onPopState(covariant html.PopStateEvent event) {
|
||||
void onPopState(covariant DomPopStateEvent event) {
|
||||
assert(urlStrategy != null);
|
||||
// May be a result of direct url access while the flutter application is
|
||||
// already running.
|
||||
@ -263,7 +260,7 @@ class SingleEntryBrowserHistory extends BrowserHistory {
|
||||
_setupStrategy(strategy);
|
||||
|
||||
final String path = currentPath;
|
||||
if (!_isFlutterEntry(html.window.history.state)) {
|
||||
if (!_isFlutterEntry(domWindow.history.state)) {
|
||||
// An entry may not have come from Flutter, for example, when the user
|
||||
// refreshes the page. They land directly on the "flutter" entry, so
|
||||
// there's no need to set up the "origin" and "flutter" entries, we can
|
||||
@ -314,7 +311,7 @@ class SingleEntryBrowserHistory extends BrowserHistory {
|
||||
|
||||
String? _userProvidedRouteName;
|
||||
@override
|
||||
void onPopState(covariant html.PopStateEvent event) {
|
||||
void onPopState(covariant DomPopStateEvent event) {
|
||||
if (_isOriginEntry(event.state)) {
|
||||
_setupFlutterEntry(urlStrategy!);
|
||||
|
||||
|
||||
@ -5,16 +5,16 @@
|
||||
@JS()
|
||||
library js_url_strategy;
|
||||
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../dom.dart';
|
||||
|
||||
typedef _PathGetter = String Function();
|
||||
|
||||
typedef _StateGetter = Object? Function();
|
||||
|
||||
typedef _AddPopStateListener = ui.VoidCallback Function(html.EventListener);
|
||||
typedef _AddPopStateListener = ui.VoidCallback Function(DomEventListener);
|
||||
|
||||
typedef _StringToString = String Function(String);
|
||||
|
||||
@ -47,7 +47,7 @@ abstract class JsUrlStrategy {
|
||||
extension JsUrlStrategyExtension on JsUrlStrategy {
|
||||
/// Adds a listener to the `popstate` event and returns a function that, when
|
||||
/// invoked, removes the listener.
|
||||
external ui.VoidCallback addPopStateListener(html.EventListener fn);
|
||||
external ui.VoidCallback addPopStateListener(DomEventListener fn);
|
||||
|
||||
/// Returns the active path in the browser.
|
||||
external String getPath();
|
||||
|
||||
@ -3,11 +3,12 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'package:js/js.dart' as js;
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../dom.dart';
|
||||
import '../safe_browser_api.dart';
|
||||
import 'js_url_strategy.dart';
|
||||
|
||||
/// Represents and reads route state from the browser's URL.
|
||||
@ -21,7 +22,7 @@ abstract class UrlStrategy {
|
||||
|
||||
/// Adds a listener to the `popstate` event and returns a function that, when
|
||||
/// invoked, removes the listener.
|
||||
ui.VoidCallback addPopStateListener(html.EventListener fn);
|
||||
ui.VoidCallback addPopStateListener(DomEventListener fn);
|
||||
|
||||
/// Returns the active path in the browser.
|
||||
String getPath();
|
||||
@ -82,9 +83,10 @@ class HashUrlStrategy extends UrlStrategy {
|
||||
final PlatformLocation _platformLocation;
|
||||
|
||||
@override
|
||||
ui.VoidCallback addPopStateListener(html.EventListener fn) {
|
||||
_platformLocation.addPopStateListener(fn);
|
||||
return () => _platformLocation.removePopStateListener(fn);
|
||||
ui.VoidCallback addPopStateListener(DomEventListener fn) {
|
||||
final DomEventListener wrappedFn = allowInterop(fn);
|
||||
_platformLocation.addPopStateListener(wrappedFn);
|
||||
return () => _platformLocation.removePopStateListener(wrappedFn);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -156,7 +158,7 @@ class CustomUrlStrategy extends UrlStrategy {
|
||||
final JsUrlStrategy delegate;
|
||||
|
||||
@override
|
||||
ui.VoidCallback addPopStateListener(html.EventListener fn) =>
|
||||
ui.VoidCallback addPopStateListener(DomEventListener fn) =>
|
||||
delegate.addPopStateListener(js.allowInterop(fn));
|
||||
|
||||
@override
|
||||
@ -194,13 +196,13 @@ abstract class PlatformLocation {
|
||||
/// Registers an event listener for the `popstate` event.
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate
|
||||
void addPopStateListener(html.EventListener fn);
|
||||
void addPopStateListener(DomEventListener fn);
|
||||
|
||||
/// Unregisters the given listener (added by [addPopStateListener]) from the
|
||||
/// `popstate` event.
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate
|
||||
void removePopStateListener(html.EventListener fn);
|
||||
void removePopStateListener(DomEventListener fn);
|
||||
|
||||
/// The `pathname` part of the URL in the browser address bar.
|
||||
///
|
||||
@ -256,17 +258,17 @@ class BrowserPlatformLocation extends PlatformLocation {
|
||||
/// Default constructor for [BrowserPlatformLocation].
|
||||
const BrowserPlatformLocation();
|
||||
|
||||
html.Location get _location => html.window.location;
|
||||
html.History get _history => html.window.history;
|
||||
DomLocation get _location => domWindow.location;
|
||||
DomHistory get _history => domWindow.history;
|
||||
|
||||
@override
|
||||
void addPopStateListener(html.EventListener fn) {
|
||||
html.window.addEventListener('popstate', fn);
|
||||
void addPopStateListener(DomEventListener fn) {
|
||||
domWindow.addEventListener('popstate', fn);
|
||||
}
|
||||
|
||||
@override
|
||||
void removePopStateListener(html.EventListener fn) {
|
||||
html.window.removeEventListener('popstate', fn);
|
||||
void removePopStateListener(DomEventListener fn) {
|
||||
domWindow.removeEventListener('popstate', fn);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -276,7 +278,7 @@ class BrowserPlatformLocation extends PlatformLocation {
|
||||
String get search => _location.search!;
|
||||
|
||||
@override
|
||||
String get hash => _location.hash;
|
||||
String get hash => _location.locationHash;
|
||||
|
||||
@override
|
||||
Object? get state => _history.state;
|
||||
@ -297,5 +299,5 @@ class BrowserPlatformLocation extends PlatformLocation {
|
||||
}
|
||||
|
||||
@override
|
||||
String? getBaseHref() => html.document.baseUri;
|
||||
String? getBaseHref() => domDocument.baseUri;
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// https://github.com/flutter/flutter/issues/100394
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
@ -149,15 +148,16 @@ class TestUrlStrategy extends UrlStrategy {
|
||||
});
|
||||
}
|
||||
|
||||
final List<html.EventListener> listeners = <html.EventListener>[];
|
||||
final List<DomEventListener> listeners = <DomEventListener>[];
|
||||
|
||||
@override
|
||||
ui.VoidCallback addPopStateListener(html.EventListener fn) {
|
||||
listeners.add(fn);
|
||||
ui.VoidCallback addPopStateListener(DomEventListener fn) {
|
||||
final DomEventListener wrappedFn = allowInterop(fn);
|
||||
listeners.add(wrappedFn);
|
||||
return () {
|
||||
// Schedule a micro task here to avoid removing the listener during
|
||||
// iteration in [_firePopStateEvent].
|
||||
scheduleMicrotask(() => listeners.remove(fn));
|
||||
scheduleMicrotask(() => listeners.remove(wrappedFn));
|
||||
};
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ class TestUrlStrategy extends UrlStrategy {
|
||||
/// like a real browser.
|
||||
void _firePopStateEvent() {
|
||||
assert(withinAppHistory);
|
||||
final html.PopStateEvent event = html.PopStateEvent(
|
||||
final DomPopStateEvent event = createDomPopStateEvent(
|
||||
'popstate',
|
||||
<String, dynamic>{'state': currentEntry.state},
|
||||
);
|
||||
|
||||
@ -6,12 +6,11 @@
|
||||
// TODO(mdebbar): https://github.com/flutter/flutter/issues/51169
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'package:quiver/testing/async.dart';
|
||||
import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart' show window;
|
||||
import 'package:ui/src/engine.dart' show window, DomEventListener;
|
||||
import 'package:ui/src/engine/browser_detection.dart';
|
||||
import 'package:ui/src/engine/navigation.dart';
|
||||
import 'package:ui/src/engine/services.dart';
|
||||
@ -723,12 +722,12 @@ class TestPlatformLocation extends PlatformLocation {
|
||||
String get search => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
void addPopStateListener(html.EventListener fn) {
|
||||
void addPopStateListener(DomEventListener fn) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
void removePopStateListener(html.EventListener fn) {
|
||||
void removePopStateListener(DomEventListener fn) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ void testMain() {
|
||||
final JsUrlStrategy jsUrlStrategy = JsUrlStrategy(
|
||||
getPath: allowInterop(() => '/initial'),
|
||||
getState: allowInterop(() => state),
|
||||
addPopStateListener: allowInterop((html.EventListener listener) => () {}),
|
||||
addPopStateListener: allowInterop((DomEventListener listener) => () {}),
|
||||
prepareExternalUrl: allowInterop((String value) => ''),
|
||||
pushState: allowInterop((Object? newState, String title, String url) {
|
||||
expect(newState is Map, true);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user