[web] Remove package:js in favor of dart:js_interop (#165324)

Removes package:js and uses dart:js_interop instead

This is taken directly from
https://github.com/flutter/flutter/pull/164254 and will be iterated on.

----

Initial commit is the original PR % rebasing. Follow-up commits address
test/analysis failures and commits after those clean up JS interop code:

- Remove remaining uses of `@staticInterop`.
- Code that uses JSBoolean, JSString, or JSNumber are mostly
historical relics due to not supporting primitives in externals.
Remove the redirecting functions and let the compiler handles the
conversions.
- Replace domInstanceOfString calls with isA and add @JS annotations
to all extension types where needed.
- Remove unnecessary @JS annotations.
- Remove unneeded implements JSObject clauses.
- Standardize naming of extension types in some cases.
- Remove JSVoid in favor of void.
- Enabled invalid_runtime_checks_with_js_interop_types lint.
- Removed unnecessary JSUint8Array constructors.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: Kevin Moore <kevmoo@google.com>
This commit is contained in:
Srujan Gaddam 2025-03-28 10:43:50 -07:00 committed by GitHub
parent 943dd30bfd
commit 7cfaf04fa9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
78 changed files with 1771 additions and 4265 deletions

View File

@ -10,7 +10,6 @@ linter:
rules:
avoid_print: false
avoid_setters_without_getters: false
invalid_runtime_check_with_js_interop_types: false
library_private_types_in_public_api: false
no_default_cases: false
prefer_relative_imports: false

View File

@ -116,7 +116,7 @@ int? _cachedWebGLVersion;
///
/// Our CanvasKit backend is affected due to: https://github.com/emscripten-core/emscripten/issues/11819
int _detectWebGLVersion() {
final DomCanvasElement canvas = createDomCanvasElement(width: 1, height: 1);
final DomHTMLCanvasElement canvas = createDomCanvasElement(width: 1, height: 1);
if (canvas.getContext('webgl2') != null) {
if (_workAroundBug91333) {
return WebGLVersion.webgl1;

View File

@ -401,14 +401,14 @@ Future<Uint8List> readChunked(
int contentLength,
ui_web.ImageCodecChunkCallback chunkCallback,
) async {
final JSUint8Array result = createUint8ArrayFromLength(contentLength);
final JSUint8Array result = JSUint8Array.withLength(contentLength);
int position = 0;
int cumulativeBytesLoaded = 0;
await payload.read<JSUint8Array>((JSUint8Array chunk) {
cumulativeBytesLoaded += chunk.length.toDartInt;
await payload.read((JSUint8Array chunk) {
cumulativeBytesLoaded += chunk.length;
chunkCallback(cumulativeBytesLoaded, contentLength);
result.set(chunk, position.toJS);
position += chunk.length.toDartInt;
result.set(chunk, position);
position += chunk.length;
});
return result.toDart;
}
@ -533,8 +533,8 @@ class CkImage implements ui.Image, StackTraceDebugger {
return readPixelsFromDomImageSource(
imageBitmap,
format,
imageBitmap.width.toDartInt,
imageBitmap.height.toDartInt,
imageBitmap.width,
imageBitmap.height,
);
case VideoFrameImageSource():
final VideoFrame videoFrame = (imageSource! as VideoFrameImageSource).videoFrame;
@ -725,10 +725,10 @@ class ImageBitmapImageSource extends ImageSource {
}
@override
int get height => imageBitmap.height.toDartInt;
int get height => imageBitmap.height;
@override
int get width => imageBitmap.width.toDartInt;
int get width => imageBitmap.width;
@override
DomCanvasImageSource get canvasImageSource => imageBitmap;

View File

@ -176,7 +176,7 @@ Future<ByteBuffer> readVideoFramePixelsUnmodified(VideoFrame videoFrame) async {
// In dart2wasm, Uint8List is not the same as a JS Uint8Array. So we
// explicitly construct the JS object here.
final JSUint8Array destination = createUint8ArrayFromLength(size);
final JSUint8Array destination = JSUint8Array.withLength(size);
final JSPromise<JSAny?> copyPromise = videoFrame.copyTo(destination);
await promiseToFuture<void>(copyPromise);
@ -190,7 +190,7 @@ ByteBuffer readDomImageSourcePixelsUnmodified(
int width,
int height,
) {
final DomCanvasElement htmlCanvas = createDomCanvasElement(width: width, height: height);
final DomHTMLCanvasElement htmlCanvas = createDomCanvasElement(width: width, height: height);
final DomCanvasRenderingContext2D ctx =
htmlCanvas.getContext('2d')! as DomCanvasRenderingContext2D;
ctx.drawImage(imageSource, 0, 0);
@ -207,7 +207,7 @@ Future<Uint8List> encodeDomImageSourceAsPng(
int width,
int height,
) async {
final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height);
final DomHTMLCanvasElement canvas = createDomCanvasElement(width: width, height: height);
final DomCanvasRenderingContext2D ctx = canvas.context2D;
ctx.drawImage(imageSource, 0, 0);
final String pngBase64 = canvas.toDataURL().substring('data:image/png;base64,'.length);

View File

@ -21,7 +21,7 @@ import 'package:ui/src/engine.dart';
/// 5. The finalizer function is called with the SkPaint as the sole argument.
/// 6. We call `delete` on SkPaint.
DomFinalizationRegistry _finalizationRegistry = DomFinalizationRegistry(
((ExternalDartReference<UniqueRef<Object>> boxedUniq) => boxedUniq.toDartObject.collect()).toJS,
((ExternalDartReference<UniqueRef<JSObject>> boxedUniq) => boxedUniq.toDartObject.collect()).toJS,
);
NativeMemoryFinalizationRegistry nativeMemoryFinalizationRegistry =
@ -30,7 +30,7 @@ NativeMemoryFinalizationRegistry nativeMemoryFinalizationRegistry =
/// An indirection to [DomFinalizationRegistry] to enable tests provide a
/// mock implementation of a finalization registry.
class NativeMemoryFinalizationRegistry {
void register(Object owner, UniqueRef<Object> ref) {
void register(Object owner, UniqueRef<JSObject> ref) {
if (browserSupportsFinalizationRegistry) {
_finalizationRegistry.register(owner.toExternalReference, ref.toExternalReference);
}
@ -44,7 +44,7 @@ class NativeMemoryFinalizationRegistry {
///
/// To prevent memory leaks, the underlying C++ object is deleted by the GC if
/// it wasn't previously disposed of explicitly.
class UniqueRef<T extends Object> {
class UniqueRef<T extends JSObject> {
UniqueRef(Object owner, T nativeObject, this._debugOwnerLabel) {
_nativeObject = nativeObject;
if (Instrumentation.enabled) {
@ -134,7 +134,7 @@ abstract class StackTraceDebugger {
/// deleted. This is mostly done to prevent memory leaks in production. Well
/// behaving framework and app code are expected to rely on [ref] and [unref]
/// for timely collection of resources.
class CountedRef<R extends StackTraceDebugger, T extends Object> {
class CountedRef<R extends StackTraceDebugger, T extends JSObject> {
/// Creates a counted reference.
CountedRef(T nativeObject, R debugReferrer, String debugLabel) {
_ref = UniqueRef<T>(this, nativeObject, debugLabel);

View File

@ -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:js_interop';
import '../display.dart';
import '../dom.dart';
import '../util.dart';
@ -47,12 +45,11 @@ class RenderCanvas extends DisplayCanvas {
final DomElement hostElement = createDomElement('flt-canvas-container');
/// The underlying `<canvas>` element used to display the pixels.
final DomCanvasElement canvasElement = createDomCanvasElement();
final DomHTMLCanvasElement canvasElement = createDomCanvasElement();
int _pixelWidth = 0;
int _pixelHeight = 0;
late final DomCanvasRenderingContextBitmapRenderer renderContext =
canvasElement.contextBitmapRenderer;
late final DomImageBitmapRenderingContext renderContext = canvasElement.contextBitmapRenderer;
late final DomCanvasRenderingContext2D renderContext2d = canvasElement.context2D;
@ -75,7 +72,7 @@ class RenderCanvas extends DisplayCanvas {
/// The canvas will be resized to accomodate the bitmap immediately before
/// rendering it.
void render(DomImageBitmap bitmap) {
_ensureSize(BitmapSize(bitmap.width.toDartInt, bitmap.height.toDartInt));
_ensureSize(BitmapSize(bitmap.width, bitmap.height));
renderContext.transferFromImageBitmap(bitmap);
}

View File

@ -103,7 +103,7 @@ class Surface extends DisplayCanvas {
/// The <canvas> backing this Surface in the case that OffscreenCanvas isn't
/// supported.
DomCanvasElement? _canvasElement;
DomHTMLCanvasElement? _canvasElement;
/// Note, if this getter is called, then this Surface is being used as an
/// overlay and must be backed by an onscreen <canvas> element.
@ -153,7 +153,7 @@ class Surface extends DisplayCanvas {
if (useOffscreenCanvas) {
bitmap = _offscreenCanvas!.transferToImageBitmap();
} else {
bitmapSource = _canvasElement! as JSObject;
bitmapSource = _canvasElement!;
bitmap = await createImageBitmap(bitmapSource, (
x: 0,
y: _pixelHeight - bitmapSize.height,
@ -291,7 +291,7 @@ class Surface extends DisplayCanvas {
return _surface = _createNewSurface(size);
}
JSVoid _contextRestoredListener(DomEvent event) {
void _contextRestoredListener(DomEvent event) {
assert(
_contextLost,
'Received "webglcontextrestored" event but never received '
@ -304,7 +304,7 @@ class Surface extends DisplayCanvas {
event.preventDefault();
}
JSVoid _contextLostListener(DomEvent event) {
void _contextLostListener(DomEvent event) {
assert(
event.target == _offscreenCanvas || event.target == _canvasElement,
'Received a context lost event for a disposed canvas',
@ -323,9 +323,13 @@ class Surface extends DisplayCanvas {
_offscreenCanvas!.removeEventListener(
'webglcontextrestored',
_cachedContextRestoredListener,
false,
false.toJS,
);
_offscreenCanvas!.removeEventListener(
'webglcontextlost',
_cachedContextLostListener,
false.toJS,
);
_offscreenCanvas!.removeEventListener('webglcontextlost', _cachedContextLostListener, false);
_offscreenCanvas = null;
_cachedContextRestoredListener = null;
_cachedContextLostListener = null;
@ -333,9 +337,13 @@ class Surface extends DisplayCanvas {
_canvasElement!.removeEventListener(
'webglcontextrestored',
_cachedContextRestoredListener,
false,
false.toJS,
);
_canvasElement!.removeEventListener(
'webglcontextlost',
_cachedContextLostListener,
false.toJS,
);
_canvasElement!.removeEventListener('webglcontextlost', _cachedContextLostListener, false);
_canvasElement!.remove();
_canvasElement = null;
_cachedContextRestoredListener = null;
@ -356,7 +364,7 @@ class Surface extends DisplayCanvas {
_offscreenCanvas = offscreenCanvas;
_canvasElement = null;
} else {
final DomCanvasElement canvas = createDomCanvasElement(
final DomHTMLCanvasElement canvas = createDomCanvasElement(
width: _pixelWidth,
height: _pixelHeight,
);
@ -379,8 +387,8 @@ class Surface extends DisplayCanvas {
// See also: https://www.khronos.org/webgl/wiki/HandlingContextLost
_cachedContextRestoredListener = createDomEventListener(_contextRestoredListener);
_cachedContextLostListener = createDomEventListener(_contextLostListener);
htmlCanvas.addEventListener('webglcontextlost', _cachedContextLostListener, false);
htmlCanvas.addEventListener('webglcontextrestored', _cachedContextRestoredListener, false);
htmlCanvas.addEventListener('webglcontextlost', _cachedContextLostListener, false.toJS);
htmlCanvas.addEventListener('webglcontextrestored', _cachedContextRestoredListener, false.toJS);
_forceNewContext = false;
_contextLost = false;
@ -490,11 +498,15 @@ class Surface extends DisplayCanvas {
@override
void dispose() {
_offscreenCanvas?.removeEventListener('webglcontextlost', _cachedContextLostListener, false);
_offscreenCanvas?.removeEventListener(
'webglcontextlost',
_cachedContextLostListener,
false.toJS,
);
_offscreenCanvas?.removeEventListener(
'webglcontextrestored',
_cachedContextRestoredListener,
false,
false.toJS,
);
_cachedContextLostListener = null;
_cachedContextRestoredListener = null;

View File

@ -347,67 +347,25 @@ class FlutterConfiguration {
external JsFlutterConfiguration? get _jsConfiguration;
/// The JS bindings for the object that's set as `window.flutterConfiguration`.
@JS()
@anonymous
@staticInterop
class JsFlutterConfiguration {
external factory JsFlutterConfiguration();
}
extension JsFlutterConfigurationExtension on JsFlutterConfiguration {
@JS('assetBase')
external JSString? get _assetBase;
String? get assetBase => _assetBase?.toDart;
@JS('canvasKitBaseUrl')
external JSString? get _canvasKitBaseUrl;
String? get canvasKitBaseUrl => _canvasKitBaseUrl?.toDart;
@JS('canvasKitVariant')
external JSString? get _canvasKitVariant;
String? get canvasKitVariant => _canvasKitVariant?.toDart;
@JS('canvasKitForceCpuOnly')
external JSBoolean? get _canvasKitForceCpuOnly;
bool? get canvasKitForceCpuOnly => _canvasKitForceCpuOnly?.toDart;
@JS('canvasKitForceMultiSurfaceRasterizer')
external JSBoolean? get _canvasKitForceMultiSurfaceRasterizer;
bool? get canvasKitForceMultiSurfaceRasterizer => _canvasKitForceMultiSurfaceRasterizer?.toDart;
@JS('canvasKitMaximumSurfaces')
external JSNumber? get _canvasKitMaximumSurfaces;
double? get canvasKitMaximumSurfaces => _canvasKitMaximumSurfaces?.toDartDouble;
@JS('debugShowSemanticsNodes')
external JSBoolean? get _debugShowSemanticsNodes;
bool? get debugShowSemanticsNodes => _debugShowSemanticsNodes?.toDart;
extension type JsFlutterConfiguration._(JSObject _) implements JSObject {
factory JsFlutterConfiguration() => JSObject() as JsFlutterConfiguration;
external String? get assetBase;
external String? get canvasKitBaseUrl;
external String? get canvasKitVariant;
external bool? get canvasKitForceCpuOnly;
external bool? get canvasKitForceMultiSurfaceRasterizer;
external double? get canvasKitMaximumSurfaces;
external bool? get debugShowSemanticsNodes;
external DomElement? get hostElement;
@JS('multiViewEnabled')
external JSBoolean? get _multiViewEnabled;
bool? get multiViewEnabled => _multiViewEnabled?.toDart;
@JS('nonce')
external JSString? get _nonce;
String? get nonce => _nonce?.toDart;
@JS('renderer')
external JSString? get _renderer;
String? get renderer => _renderer?.toDart;
@JS('fontFallbackBaseUrl')
external JSString? get _fontFallbackBaseUrl;
String? get fontFallbackBaseUrl => _fontFallbackBaseUrl?.toDart;
@JS('forceSingleThreadedSkwasm')
external JSBoolean? get _forceSingleThreadedSkwasm;
bool? get forceSingleThreadedSkwasm => _forceSingleThreadedSkwasm?.toDart;
external bool? get multiViewEnabled;
external String? get nonce;
external String? get renderer;
external String? get fontFallbackBaseUrl;
external bool? get forceSingleThreadedSkwasm;
}
/// A JavaScript entrypoint that allows developer to set rendering backend
/// at runtime before launching the application.
@JS('window.flutterWebRenderer')
external JSString? get __requestedRendererType;
String? get _requestedRendererType => __requestedRendererType?.toDart;
external String? get _requestedRendererType;

File diff suppressed because it is too large Load Diff

View File

@ -21,9 +21,7 @@ class HighContrastSupport {
final DomMediaQueryList _highContrastMediaQuery = domWindow.matchMedia(
_highContrastMediaQueryString,
);
late final DomEventListener _onHighContrastChangeListener = createDomEventListener(
_onHighContrastChange,
);
late final DomEventListener _onHighContrastChangeListener = _onHighContrastChange.toJS;
bool get isHighContrastEnabled => _highContrastMediaQuery.matches;
@ -43,7 +41,7 @@ class HighContrastSupport {
}
}
JSVoid _onHighContrastChange(DomEvent event) {
void _onHighContrastChange(DomEvent event) {
final DomMediaQueryListEvent mqEvent = event as DomMediaQueryListEvent;
final bool isHighContrastEnabled = mqEvent.matches!;
for (final HighContrastListener listener in _listeners) {

View File

@ -102,18 +102,18 @@ abstract class BrowserImageDecoder implements ui.Codec {
try {
final ImageDecoder webDecoder = ImageDecoder(
ImageDecoderOptions(
type: contentType.toJS,
type: contentType,
data: dataSource,
// Flutter always uses premultiplied alpha when decoding.
premultiplyAlpha: 'premultiply'.toJS,
premultiplyAlpha: 'premultiply',
// "default" gives the browser the liberty to convert to display-appropriate
// color space, typically SRGB, which is what we want.
colorSpaceConversion: 'default'.toJS,
colorSpaceConversion: 'default',
// Flutter doesn't give the developer a way to customize this, so if this
// is an animated image we should prefer the animated track.
preferAnimation: true.toJS,
preferAnimation: true,
),
);
@ -121,8 +121,6 @@ abstract class BrowserImageDecoder implements ui.Codec {
// Flutter doesn't have an API for progressive loading of images, so we
// wait until the image is fully decoded.
// package:js bindings don't work with getters that return a Promise, which
// is why js_util is used instead.
await promiseToFuture<void>(getJsProperty(webDecoder, 'completed'));
frameCount = webDecoder.tracks.selectedTrack!.frameCount.toInt();
@ -151,7 +149,11 @@ abstract class BrowserImageDecoder implements ui.Codec {
return webDecoder;
} catch (error) {
if (domInstanceOfString(error, 'DOMException')) {
// TODO(srujzs): Replace this with `error.isJSAny` when we have that API
// in `dart:js_interop`.
// https://github.com/dart-lang/sdk/issues/56905
// ignore: invalid_runtime_check_with_js_interop_types
if (error is JSAny && error.isA<DomException>()) {
if ((error as DomException).name == DomException.notSupported) {
throw ImageCodecException(
"Image file format ($contentType) is not supported by this browser's ImageDecoder API.\n"
@ -172,7 +174,7 @@ abstract class BrowserImageDecoder implements ui.Codec {
_debugCheckNotDisposed();
final ImageDecoder webDecoder = await _getOrCreateWebDecoder();
final DecodeResult result = await promiseToFuture<DecodeResult>(
webDecoder.decode(DecodeOptions(frameIndex: _nextFrameIndex.toJS)),
webDecoder.decode(DecodeOptions(frameIndex: _nextFrameIndex)),
);
final VideoFrame frame = result.image;
_nextFrameIndex = (_nextFrameIndex + 1) % frameCount;

View File

@ -2,22 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@JS()
library js_app;
import 'dart:js_interop';
import 'package:ui/src/engine.dart';
/// The JS bindings for the configuration object passed to [FlutterApp.addView].
@JS()
@anonymous
@staticInterop
class JsFlutterViewOptions {
external factory JsFlutterViewOptions();
}
extension type JsFlutterViewOptions._(JSObject _) implements JSObject {
factory JsFlutterViewOptions() => JSObject() as JsFlutterViewOptions;
/// The attributes of the [JsFlutterViewOptions] object.
extension JsFlutterViewOptionsExtension on JsFlutterViewOptions {
@JS('hostElement')
external DomElement? get _hostElement;
DomElement get hostElement {
@ -25,32 +16,21 @@ extension JsFlutterViewOptionsExtension on JsFlutterViewOptions {
return _hostElement!;
}
@JS('viewConstraints')
external JsViewConstraints? get _viewConstraints;
JsViewConstraints? get viewConstraints {
return _viewConstraints;
}
external JsViewConstraints? get viewConstraints;
external JSAny? get initialData;
}
/// The JS bindings for a [ViewConstraints] object.
@JS()
@anonymous
@staticInterop
class JsViewConstraints {
///
/// Attributes are expressed in *logical* pixels.
extension type JsViewConstraints._(JSObject _) implements JSObject {
external factory JsViewConstraints({
double? minWidth,
double? maxWidth,
double? minHeight,
double? maxHeight,
});
}
/// The attributes of a [JsViewConstraints] object.
///
/// These attributes are expressed in *logical* pixels.
extension JsViewConstraintsExtension on JsViewConstraints {
external double? get maxHeight;
external double? get maxWidth;
external double? get minHeight;
@ -58,17 +38,11 @@ extension JsViewConstraintsExtension on JsViewConstraints {
}
/// The public JS API of a running Flutter Web App.
@JS()
@anonymous
@staticInterop
abstract class FlutterApp {
extension type FlutterApp._primary(JSObject _) implements JSObject {
factory FlutterApp({
required AddFlutterViewFn addView,
required RemoveFlutterViewFn removeView,
}) => FlutterApp._(
addView: addView.toJS,
removeView: ((JSNumber id) => removeView(id.toDartInt)).toJS,
);
}) => FlutterApp._(addView: addView.toJS, removeView: ((int id) => removeView(id)).toJS);
external factory FlutterApp._({required JSFunction addView, required JSFunction removeView});
}

View File

@ -10,11 +10,7 @@ import 'dart:js_interop_unsafe';
import 'package:ui/src/engine.dart';
@JS()
@staticInterop
class FlutterJS {}
extension FlutterJSExtension on FlutterJS {
extension type FlutterJS._(JSObject _) implements JSObject {
external FlutterLoader? get loader;
}
@ -23,13 +19,9 @@ extension FlutterJSExtension on FlutterJS {
@JS('_flutter')
external FlutterJS? get flutter;
@JS()
@staticInterop
class FlutterLoader {}
extension FlutterLoaderExtension on FlutterLoader {
extension type FlutterLoader._(JSObject _) implements JSObject {
external void didCreateEngineInitializer(FlutterEngineInitializer initializer);
bool get isAutoStart => !(this as JSObject).has('didCreateEngineInitializer');
bool get isAutoStart => !has('didCreateEngineInitializer');
}
/// Typedef for the function that initializes the flutter engine.
@ -49,10 +41,7 @@ typedef ImmediateRunAppFn = Future<FlutterApp> Function();
///
/// As a convenience method, [autoStart] allows the user to immediately initialize
/// and run a Flutter Web app, from JavaScript.
@JS()
@anonymous
@staticInterop
abstract class FlutterEngineInitializer {
extension type FlutterEngineInitializer._primary(JSObject _) implements JSObject {
factory FlutterEngineInitializer({
required InitializeEngineFn initializeEngine,
required ImmediateRunAppFn autoStart,
@ -73,13 +62,9 @@ abstract class FlutterEngineInitializer {
/// A class that exposes a function that runs the Flutter app,
/// and returns a promise of a FlutterAppCleaner.
@JS()
@anonymous
@staticInterop
abstract class FlutterAppRunner {
factory FlutterAppRunner({required RunAppFn runApp}) => FlutterAppRunner._(
runApp: (([RunAppFnParameters? args]) => (runApp(args) as Future<JSObject>).toPromise).toJS,
);
extension type FlutterAppRunner._primary(JSObject _) implements JSObject {
factory FlutterAppRunner({required RunAppFn runApp}) =>
FlutterAppRunner._(runApp: (([RunAppFnParameters? args]) => runApp(args).toPromise).toJS);
/// Runs a flutter app
external factory FlutterAppRunner._({
@ -89,10 +74,7 @@ abstract class FlutterAppRunner {
/// The shape of the object that can be passed as parameter to the
/// runApp function of the FlutterAppRunner object (from JS).
@JS()
@anonymous
@staticInterop
abstract class RunAppFnParameters {}
extension type RunAppFnParameters._(JSObject _) implements JSObject {}
/// Typedef for the function that runs the flutter app main entrypoint.
typedef RunAppFn = Future<FlutterApp> Function([RunAppFnParameters?]);

View File

@ -6,25 +6,7 @@ import 'dart:js_interop';
// Some APIs we need on typed arrays that are not exposed by the dart sdk yet
extension TypedArrayExtension on JSTypedArray {
external JSTypedArray slice(JSNumber start, JSNumber end);
external void set(JSTypedArray source, JSNumber start);
external JSNumber get length;
external JSTypedArray slice(int start, int end);
external void set(JSTypedArray source, int start);
external int get length;
}
// These are constructors on `Uint8Array` that we need that aren't exposed in
// the dart sdk yet
@JS('Uint8Array')
@staticInterop
class JSUint8Array1 {
external factory JSUint8Array1._create1(JSAny bufferOrLength);
external factory JSUint8Array1._create3(JSArrayBuffer buffer, JSNumber start, JSNumber length);
}
JSUint8Array createUint8ArrayFromBuffer(JSArrayBuffer buffer) =>
JSUint8Array1._create1(buffer) as JSUint8Array;
JSUint8Array createUint8ArrayFromSubBuffer(JSArrayBuffer buffer, int start, int length) =>
JSUint8Array1._create3(buffer, start.toJS, length.toJS) as JSUint8Array;
JSUint8Array createUint8ArrayFromLength(int length) =>
JSUint8Array1._create1(length.toJS) as JSUint8Array;

View File

@ -142,7 +142,7 @@ class KeyboardBinding {
final Map<String, DomEventListener> _listeners = <String, DomEventListener>{};
void _addEventListener(String eventName, DartDomEventListener handler) {
JSVoid loggedHandler(DomEvent event) {
void loggedHandler(DomEvent event) {
if (_debugLogKeyEvents) {
print(event.type);
}
@ -151,16 +151,16 @@ class KeyboardBinding {
}
}
final DomEventListener wrappedHandler = createDomEventListener(loggedHandler);
final DomEventListener wrappedHandler = loggedHandler.toJS;
assert(!_listeners.containsKey(eventName));
_listeners[eventName] = wrappedHandler;
domWindow.addEventListener(eventName, wrappedHandler, true);
domWindow.addEventListener(eventName, wrappedHandler, true.toJS);
}
/// Remove all active event listeners.
void _clearListeners() {
_listeners.forEach((String eventName, DomEventListener listener) {
domWindow.removeEventListener(eventName, listener, true);
domWindow.removeEventListener(eventName, listener, true.toJS);
});
_listeners.clear();
}

View File

@ -830,11 +830,15 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
return;
}
updateLocales(); // First time, for good measure.
_onLocaleChangedSubscription = DomSubscription(domWindow, 'languagechange', (DomEvent _) {
// Update internal config, then propagate the changes.
updateLocales();
invokeOnLocaleChanged();
});
_onLocaleChangedSubscription = DomSubscription(
domWindow,
'languagechange',
createDomEventListener((DomEvent _) {
// Update internal config, then propagate the changes.
updateLocales();
invokeOnLocaleChanged();
}),
);
}
/// Removes the [_onLocaleChangedSubscription].
@ -1079,10 +1083,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
_brightnessMediaQuery.matches ? ui.Brightness.dark : ui.Brightness.light,
);
_brightnessMediaQueryListener = createDomEventListener((DomEvent event) {
final DomMediaQueryListEvent mqEvent = event as DomMediaQueryListEvent;
_updatePlatformBrightness(mqEvent.matches! ? ui.Brightness.dark : ui.Brightness.light);
});
_brightnessMediaQueryListener =
(DomEvent event) {
final DomMediaQueryListEvent mqEvent = event as DomMediaQueryListEvent;
_updatePlatformBrightness(mqEvent.matches! ? ui.Brightness.dark : ui.Brightness.light);
}.toJS;
_brightnessMediaQuery.addListener(_brightnessMediaQueryListener);
}

View File

@ -3,6 +3,8 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:js_interop';
import 'package:meta/meta.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;
@ -85,7 +87,7 @@ final class ViewFocusBinding {
// The right event type needs to be checked because Chrome seems to be firing
// `Event` events instead of `KeyboardEvent` events when autofilling is used.
// See https://github.com/flutter/flutter/issues/149968 for more info.
if (event is DomKeyboardEvent && (event.shiftKey ?? false)) {
if (event.isA<DomKeyboardEvent>() && ((event as DomKeyboardEvent).shiftKey ?? false)) {
_viewFocusDirection = ui.ViewFocusDirection.backward;
}
});

View File

@ -91,7 +91,7 @@ class SafariPointerEventWorkaround {
void workAroundMissingPointerEvents() {
// We only need to attach the listener once.
if (_listener == null) {
_listener = createDomEventListener((_) {});
_listener = createDomEventListener((DomEvent _) {});
domDocument.addEventListener('touchstart', _listener);
}
}
@ -322,9 +322,9 @@ class ClickDebouncer {
assert(event.type == 'pointerdown', 'Click debouncing must begin with a pointerdown');
final DomEventTarget? target = event.target;
if (target is DomElement && target.hasAttribute('flt-tappable')) {
if (target.isA<DomElement>() && (target! as DomElement).hasAttribute('flt-tappable')) {
_state = (
target: target,
target: target as DomElement,
// The 200ms duration was chosen empirically by testing tapping, mouse
// clicking, trackpad tapping and clicking, as well as the following
// screen readers: TalkBack on Android, VoiceOver on macOS, Narrator/
@ -471,7 +471,7 @@ class Listener {
target.addEventListener(event, jsHandler);
} else {
final Map<String, Object> eventOptions = <String, Object>{'passive': passive};
target.addEventListenerWithOptions(event, jsHandler, eventOptions);
target.addEventListener(event, jsHandler, eventOptions.toJSAnyDeep);
}
final Listener listener = Listener._(event: event, target: target, handler: jsHandler);
@ -528,9 +528,9 @@ abstract class _BaseAdapter {
/// instead, because the browser doesn't fire the latter two for DOM elements
/// when the pointer is outside the window.
void addEventListener(DomEventTarget target, String eventName, DartDomEventListener handler) {
JSVoid loggedHandler(DomEvent event) {
void loggedHandler(DomEvent event) {
if (_debugLogPointerEvents) {
if (domInstanceOfString(event, 'PointerEvent')) {
if (event.isA<DomPointerEvent>()) {
final DomPointerEvent pointerEvent = event as DomPointerEvent;
final ui.Offset offset = computeEventOffsetToTarget(event, _view);
print(
@ -730,7 +730,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter {
return;
}
assert(domInstanceOfString(event, 'WheelEvent'));
assert(event.isA<DomWheelEvent>());
if (_debugLogPointerEvents) {
print(event.type);
}

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:js_interop';
import 'dart:typed_data';
import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
@ -83,7 +84,7 @@ class RawKeyboard {
}
void handleHtmlEvent(DomEvent domEvent) {
if (!domInstanceOfString(domEvent, 'KeyboardEvent')) {
if (!domEvent.isA<DomKeyboardEvent>()) {
return;
}

View File

@ -131,8 +131,8 @@ void vibrate(int durationMs) {
/// The [DomCanvasElement] factory assumes that element allocation will
/// succeed and will return a non-null element. This is not always true. For
/// example, when Safari on iOS runs out of memory it returns null.
DomCanvasElement? tryCreateCanvasElement(int width, int height) {
final DomCanvasElement? canvas = js_util.callMethod<DomCanvasElement?>(
DomHTMLCanvasElement? tryCreateCanvasElement(int width, int height) {
final DomHTMLCanvasElement? canvas = js_util.callMethod<DomHTMLCanvasElement?>(
domDocument,
'createElement',
<dynamic>['CANVAS'],
@ -198,20 +198,13 @@ bool get _isBrowserImageDecoderStable => ui_web.browser.browserEngine == ui_web.
///
/// * https://www.w3.org/TR/webcodecs/#imagedecoder-interface
@JS('window.ImageDecoder')
@staticInterop
class ImageDecoder {
external factory ImageDecoder(ImageDecoderOptions options);
}
extension type ImageDecoder._(JSObject _) implements JSObject {
external ImageDecoder(ImageDecoderOptions options);
extension ImageDecoderExtension on ImageDecoder {
external ImageTrackList get tracks;
@JS('complete')
external JSBoolean get _complete;
bool get complete => _complete.toDart;
external bool get complete;
external JSPromise<JSAny?> decode(DecodeOptions options);
external JSVoid close();
external void close();
}
/// Options passed to the `ImageDecoder` constructor.
@ -219,18 +212,15 @@ extension ImageDecoderExtension on ImageDecoder {
/// See also:
///
/// * https://www.w3.org/TR/webcodecs/#imagedecoderinit-interface
@JS()
@anonymous
@staticInterop
class ImageDecoderOptions {
external factory ImageDecoderOptions({
required JSString type,
extension type ImageDecoderOptions._(JSObject _) implements JSObject {
external ImageDecoderOptions({
required String type,
required JSAny data,
required JSString premultiplyAlpha,
JSNumber? desiredWidth,
JSNumber? desiredHeight,
required JSString colorSpaceConversion,
required JSBoolean preferAnimation,
required String premultiplyAlpha,
double? desiredWidth,
double? desiredHeight,
required String colorSpaceConversion,
required bool preferAnimation,
});
}
@ -239,17 +229,9 @@ class ImageDecoderOptions {
/// See also:
///
/// * https://www.w3.org/TR/webcodecs/#imagedecoderesult-interface
@JS()
@anonymous
@staticInterop
class DecodeResult {}
extension DecodeResultExtension on DecodeResult {
extension type DecodeResult(JSObject _) implements JSObject {
external VideoFrame get image;
@JS('complete')
external JSBoolean get _complete;
bool get complete => _complete.toDart;
external bool get complete;
}
/// Options passed to [ImageDecoder.decode].
@ -257,11 +239,8 @@ extension DecodeResultExtension on DecodeResult {
/// See also:
///
/// * https://www.w3.org/TR/webcodecs/#dictdef-imagedecodeoptions
@JS()
@anonymous
@staticInterop
class DecodeOptions {
external factory DecodeOptions({required JSNumber frameIndex});
extension type DecodeOptions._(JSObject _) implements JSObject {
external DecodeOptions({required int frameIndex});
}
/// The only frame in a static image, or one of the frames in an animated one.
@ -271,46 +250,21 @@ class DecodeOptions {
/// See also:
///
/// * https://www.w3.org/TR/webcodecs/#videoframe-interface
@JS()
@anonymous
@staticInterop
class VideoFrame implements DomCanvasImageSource {}
extension VideoFrameExtension on VideoFrame {
@JS('allocationSize')
external JSNumber _allocationSize();
double allocationSize() => _allocationSize().toDartDouble;
extension type VideoFrame(JSObject _) implements JSObject, DomCanvasImageSource {
external double allocationSize();
@JS('copyTo')
external JSPromise<JSAny?> _copyTo(JSAny destination);
JSPromise<JSAny?> copyTo(Object destination) => _copyTo(destination.toJSAnyShallow);
@JS('format')
external JSString? get _format;
String? get format => _format?.toDart;
@JS('codedWidth')
external JSNumber get _codedWidth;
double get codedWidth => _codedWidth.toDartDouble;
@JS('codedHeight')
external JSNumber get _codedHeight;
double get codedHeight => _codedHeight.toDartDouble;
@JS('displayWidth')
external JSNumber get _displayWidth;
double get displayWidth => _displayWidth.toDartDouble;
@JS('displayHeight')
external JSNumber get _displayHeight;
double get displayHeight => _displayHeight.toDartDouble;
@JS('duration')
external JSNumber? get _duration;
double? get duration => _duration?.toDartDouble;
external String? get format;
external double get codedWidth;
external double get codedHeight;
external double get displayWidth;
external double get displayHeight;
external double? get duration;
external VideoFrame clone();
external JSVoid close();
external void close();
}
/// Corresponds to the browser's `ImageTrackList` type.
@ -318,12 +272,7 @@ extension VideoFrameExtension on VideoFrame {
/// See also:
///
/// * https://www.w3.org/TR/webcodecs/#imagetracklist-interface
@JS()
@anonymous
@staticInterop
class ImageTrackList {}
extension ImageTrackListExtension on ImageTrackList {
extension type ImageTrackList(JSObject _) implements JSObject {
external JSPromise<JSAny?> get ready;
external ImageTrack? get selectedTrack;
}
@ -333,19 +282,9 @@ extension ImageTrackListExtension on ImageTrackList {
/// See also:
///
/// * https://www.w3.org/TR/webcodecs/#imagetrack
@JS()
@anonymous
@staticInterop
class ImageTrack {}
extension ImageTrackExtension on ImageTrack {
@JS('repetitionCount')
external JSNumber get _repetitionCount;
double get repetitionCount => _repetitionCount.toDartDouble;
@JS('frameCount')
external JSNumber get _frameCount;
double get frameCount => _frameCount.toDartDouble;
extension type ImageTrack(JSObject _) implements JSObject {
external double get repetitionCount;
external double get frameCount;
}
void scaleCanvas2D(Object context2d, num x, num y) {
@ -399,7 +338,7 @@ class GlContext {
_canvas = canvas;
}
GlContext._fromCanvasElement(DomCanvasElement canvas, bool useWebGl1)
GlContext._fromCanvasElement(DomHTMLCanvasElement canvas, bool useWebGl1)
: glContext =
canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2', <String, dynamic>{
'premultipliedAlpha': false,
@ -844,7 +783,7 @@ class GlContext {
);
return imageBitmap;
} else {
final DomCanvasElement canvas = createDomCanvasElement(
final DomHTMLCanvasElement canvas = createDomCanvasElement(
width: _widthInPixels,
height: _heightInPixels,
);
@ -856,7 +795,7 @@ class GlContext {
/// Returns image data in data url format.
String toImageUrl() {
final DomCanvasElement canvas = createDomCanvasElement(
final DomHTMLCanvasElement canvas = createDomCanvasElement(
width: _widthInPixels,
height: _heightInPixels,
);
@ -970,12 +909,12 @@ class OffScreenCanvas {
}
DomOffscreenCanvas? offScreenCanvas;
DomCanvasElement? canvasElement;
DomHTMLCanvasElement? canvasElement;
int width;
int height;
static bool? _supported;
void _updateCanvasCssSize(DomCanvasElement element) {
void _updateCanvasCssSize(DomHTMLCanvasElement element) {
final double cssWidth = width / EngineFlutterDisplay.instance.browserDevicePixelRatio;
final double cssHeight = height / EngineFlutterDisplay.instance.browserDevicePixelRatio;
element.style
@ -1012,11 +951,11 @@ class OffScreenCanvas {
: canvasElement!.getContext('2d');
}
DomCanvasRenderingContextBitmapRenderer? getBitmapRendererContext() {
DomImageBitmapRenderingContext? getBitmapRendererContext() {
return (offScreenCanvas != null
? offScreenCanvas!.getContext('bitmaprenderer')
: canvasElement!.getContext('bitmaprenderer'))
as DomCanvasRenderingContextBitmapRenderer?;
as DomImageBitmapRenderingContext?;
}
/// Feature detection for transferToImageBitmap on OffscreenCanvas.

View File

@ -240,7 +240,7 @@ sealed class SliceContainer {
final class PictureSliceContainer extends SliceContainer {
factory PictureSliceContainer(ui.Rect bounds) {
final DomElement container = domDocument.createElement(kCanvasContainerTag);
final DomCanvasElement canvas = createDomCanvasElement(
final DomHTMLCanvasElement canvas = createDomCanvasElement(
width: bounds.width.toInt(),
height: bounds.height.toInt(),
);
@ -289,13 +289,13 @@ final class PictureSliceContainer extends SliceContainer {
}
void renderBitmap(DomImageBitmap bitmap) {
final DomCanvasRenderingContextBitmapRenderer ctx = canvas.contextBitmapRenderer;
final DomImageBitmapRenderingContext ctx = canvas.contextBitmapRenderer;
ctx.transferFromImageBitmap(bitmap);
}
@override
final DomElement container;
final DomCanvasElement canvas;
final DomHTMLCanvasElement canvas;
}
final class PlatformViewContainer extends SliceContainer {

View File

@ -168,8 +168,12 @@ class AccessibilityFocusManager {
final _FocusTarget newTarget = (
semanticsNodeId: semanticsNodeId,
element: element,
domFocusListener: createDomEventListener((_) => _didReceiveDomFocus()),
domBlurListener: createDomEventListener((_) => _didReceiveDomBlur()),
domFocusListener: createDomEventListener((DomEvent _) {
_didReceiveDomFocus();
}),
domBlurListener: createDomEventListener((DomEvent _) {
_didReceiveDomBlur();
}),
);
_target = newTarget;
_lastEvent = AccessibilityFocusManagerEvent.nothing;

View File

@ -36,7 +36,7 @@ class SemanticIncrementable extends SemanticRole {
_element.addEventListener(
'change',
createDomEventListener((_) {
createDomEventListener((DomEvent _) {
if (_element.disabled!) {
return;
}

View File

@ -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 '../dom.dart';
import '../semantics.dart';
import '../util.dart';

View File

@ -163,7 +163,7 @@ class SemanticScrollable extends SemanticRole {
};
EngineSemantics.instance.addGestureModeListener(_gestureModeListener!);
scrollListener = createDomEventListener((_) {
scrollListener = createDomEventListener((DomEvent _) {
if (!_canScroll) {
return;
}

View File

@ -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 'dart:js_interop';
import 'dart:math' as math;
import 'dart:typed_data';
@ -615,11 +616,21 @@ abstract class SemanticRole {
void removeAttribute(String name) => element.removeAttribute(name);
void addEventListener(String type, DomEventListener? listener, [bool? useCapture]) =>
element.addEventListener(type, listener, useCapture);
void addEventListener(String type, DomEventListener? listener, [bool? useCapture]) {
if (useCapture != null) {
element.addEventListener(type, listener, useCapture.toJS);
} else {
element.addEventListener(type, listener);
}
}
void removeEventListener(String type, DomEventListener? listener, [bool? useCapture]) =>
element.removeEventListener(type, listener, useCapture);
void removeEventListener(String type, DomEventListener? listener, [bool? useCapture]) {
if (useCapture != null) {
element.removeEventListener(type, listener, useCapture.toJS);
} else {
element.removeEventListener(type, listener);
}
}
/// Convenience getter for the [Focusable] behavior, if any.
Focusable? get focusable => _focusable;

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:js_interop';
import 'package:meta/meta.dart';
import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
@ -184,7 +185,7 @@ class DesktopSemanticsEnabler extends SemanticsEnabler {
createDomEventListener((DomEvent event) {
tryEnableSemantics(event);
}),
true,
true.toJS,
);
// Adding roles to semantics placeholder. 'aria-live' will make sure that
@ -382,7 +383,7 @@ class MobileSemanticsEnabler extends SemanticsEnabler {
createDomEventListener((DomEvent event) {
tryEnableSemantics(event);
}),
true,
true.toJS,
);
placeholder

View File

@ -126,9 +126,15 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy {
}
// Subscribe to text and selection changes.
subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange));
subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction));
subscriptions.add(DomSubscription(domDocument, 'selectionchange', handleChange));
subscriptions.add(
DomSubscription(activeDomElement, 'input', createDomEventListener(handleChange)),
);
subscriptions.add(
DomSubscription(activeDomElement, 'keydown', createDomEventListener(maybeSendAction)),
);
subscriptions.add(
DomSubscription(domDocument, 'selectionchange', createDomEventListener(handleChange)),
);
preventDefaultForMouseEvents();
}

View File

@ -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:js_interop';
import 'package:ui/src/engine.dart';
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
import 'package:ui/ui.dart' as ui;
@ -20,8 +18,6 @@ class SkwasmImageDecoder extends BrowserImageDecoder {
final int width = frame.displayWidth.toInt();
final int height = frame.displayHeight.toInt();
final SkwasmSurface surface = (renderer as SkwasmRenderer).surface;
return SkwasmImage(
imageCreateFromTextureSource(frame as JSObject, width, height, surface.handle),
);
return SkwasmImage(imageCreateFromTextureSource(frame, width, height, surface.handle));
}
}

View File

@ -369,5 +369,7 @@ class SkwasmMaskFilter extends SkwasmObjectWrapper<RawMaskFilter> {
);
static final SkwasmFinalizationRegistry<RawMaskFilter> _registry =
SkwasmFinalizationRegistry<RawMaskFilter>(maskFilterDispose);
SkwasmFinalizationRegistry<RawMaskFilter>(
(MaskFilterHandle handle) => maskFilterDispose(handle),
);
}

View File

@ -23,7 +23,7 @@ class SkwasmTypeface extends SkwasmObjectWrapper<RawTypeface> {
SkwasmTypeface(SkDataHandle data) : super(typefaceCreate(data), _registry);
static final SkwasmFinalizationRegistry<RawTypeface> _registry =
SkwasmFinalizationRegistry<RawTypeface>(typefaceDispose);
SkwasmFinalizationRegistry<RawTypeface>((TypefaceHandle handle) => typefaceDispose(handle));
}
class SkwasmFontCollection implements FlutterFontCollection {
@ -108,15 +108,15 @@ class SkwasmFontCollection implements FlutterFontCollection {
int length = 0;
final List<JSUint8Array> chunks = <JSUint8Array>[];
await response.read((JSUint8Array chunk) {
length += chunk.length.toDartInt;
length += chunk.length;
chunks.add(chunk);
});
final SkDataHandle fontData = skDataCreate(length);
int dataAddress = skDataGetPointer(fontData).cast<Int8>().address;
final JSUint8Array wasmMemory = createUint8ArrayFromBuffer(skwasmInstance.wasmMemory.buffer);
final JSUint8Array wasmMemory = JSUint8Array(skwasmInstance.wasmMemory.buffer);
for (final JSUint8Array chunk in chunks) {
wasmMemory.set(chunk, dataAddress.toJS);
dataAddress += chunk.length.toDartInt;
wasmMemory.set(chunk, dataAddress);
dataAddress += chunk.length;
}
final SkwasmTypeface typeface = SkwasmTypeface(fontData);
skDataDispose(fontData);
@ -136,15 +136,15 @@ class SkwasmFontCollection implements FlutterFontCollection {
int length = 0;
final List<JSUint8Array> chunks = <JSUint8Array>[];
await response.read((JSUint8Array chunk) {
length += chunk.length.toDartInt;
length += chunk.length;
chunks.add(chunk);
});
final SkDataHandle fontData = skDataCreate(length);
int dataAddress = skDataGetPointer(fontData).cast<Int8>().address;
final JSUint8Array wasmMemory = createUint8ArrayFromBuffer(skwasmInstance.wasmMemory.buffer);
final JSUint8Array wasmMemory = JSUint8Array(skwasmInstance.wasmMemory.buffer);
for (final JSUint8Array chunk in chunks) {
wasmMemory.set(chunk, dataAddress.toJS);
dataAddress += chunk.length.toDartInt;
wasmMemory.set(chunk, dataAddress);
dataAddress += chunk.length;
}
final SkwasmTypeface typeface = SkwasmTypeface(fontData);

View File

@ -39,7 +39,7 @@ class SkwasmImage extends SkwasmObjectWrapper<RawImage> implements ui.Image {
}
static final SkwasmFinalizationRegistry<RawImage> _registry =
SkwasmFinalizationRegistry<RawImage>(imageDispose);
SkwasmFinalizationRegistry<RawImage>((ImageHandle handle) => imageDispose(handle));
@override
void dispose() {
@ -64,11 +64,11 @@ class SkwasmImage extends SkwasmObjectWrapper<RawImage> implements ui.Image {
recorder.endRecording() as SkwasmPicture,
])).imageBitmaps.single;
final DomOffscreenCanvas offscreenCanvas = createDomOffscreenCanvas(
bitmap.width.toDartInt,
bitmap.height.toDartInt,
bitmap.width,
bitmap.height,
);
final DomCanvasRenderingContextBitmapRenderer context =
offscreenCanvas.getContext('bitmaprenderer')! as DomCanvasRenderingContextBitmapRenderer;
final DomImageBitmapRenderingContext context =
offscreenCanvas.getContext('bitmaprenderer')! as DomImageBitmapRenderingContext;
context.transferFromImageBitmap(bitmap);
final DomBlob blob = await offscreenCanvas.convertToBlob();
final JSArrayBuffer arrayBuffer = (await blob.arrayBuffer().toDart)! as JSArrayBuffer;

View File

@ -51,7 +51,9 @@ class SkwasmLineMetrics extends SkwasmObjectWrapper<RawLineMetrics> implements u
SkwasmLineMetrics._(LineMetricsHandle handle) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawLineMetrics> _registry =
SkwasmFinalizationRegistry<RawLineMetrics>(lineMetricsDispose);
SkwasmFinalizationRegistry<RawLineMetrics>(
(LineMetricsHandle handle) => lineMetricsDispose(handle),
);
@override
bool get hardBreak => lineMetricsGetHardBreak(handle);
@ -88,7 +90,9 @@ class SkwasmParagraph extends SkwasmObjectWrapper<RawParagraph> implements ui.Pa
SkwasmParagraph(ParagraphHandle handle) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawParagraph> _registry =
SkwasmFinalizationRegistry<RawParagraph>(paragraphDispose);
SkwasmFinalizationRegistry<RawParagraph>(
(ParagraphHandle handle) => paragraphDispose(handle),
);
bool _hasCheckedForMissingCodePoints = false;
@ -301,7 +305,9 @@ class SkwasmNativeTextStyle extends SkwasmObjectWrapper<RawTextStyle> {
factory SkwasmNativeTextStyle.defaultTextStyle() => SkwasmNativeTextStyle(textStyleCreate());
static final SkwasmFinalizationRegistry<RawTextStyle> _registry =
SkwasmFinalizationRegistry<RawTextStyle>(textStyleDispose);
SkwasmFinalizationRegistry<RawTextStyle>(
(TextStyleHandle handle) => textStyleDispose(handle),
);
SkwasmNativeTextStyle copy() {
return SkwasmNativeTextStyle(textStyleCopy(handle));
@ -644,7 +650,9 @@ final class SkwasmStrutStyle extends SkwasmObjectWrapper<RawStrutStyle> implemen
) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawStrutStyle> _registry =
SkwasmFinalizationRegistry<RawStrutStyle>(strutStyleDispose);
SkwasmFinalizationRegistry<RawStrutStyle>(
(StrutStyleHandle handle) => strutStyleDispose(handle),
);
final String? _fontFamily;
final List<String>? _fontFamilyFallback;
@ -835,7 +843,9 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper<RawParagraphStyle>
) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawParagraphStyle> _registry =
SkwasmFinalizationRegistry<RawParagraphStyle>(paragraphStyleDispose);
SkwasmFinalizationRegistry<RawParagraphStyle>(
(ParagraphStyleHandle handle) => paragraphStyleDispose(handle),
);
final SkwasmNativeTextStyle textStyle;
final String? defaultFontFamily;
@ -929,7 +939,9 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper<RawParagraphBuilder>
SkwasmParagraphBuilder._(ParagraphBuilderHandle handle, this.style) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawParagraphBuilder> _registry =
SkwasmFinalizationRegistry<RawParagraphBuilder>(paragraphBuilderDispose);
SkwasmFinalizationRegistry<RawParagraphBuilder>(
(ParagraphBuilderHandle handle) => paragraphBuilderDispose(handle),
);
final SkwasmParagraphStyle style;
final List<SkwasmNativeTextStyle> textStyleStack = <SkwasmNativeTextStyle>[];
@ -994,9 +1006,9 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper<RawParagraphBuilder>
// than a slice, but the TextDecoder API doesn't work on shared buffer
// sources yet.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1012656
createUint8ArrayFromBuffer(
JSUint8Array(
skwasmInstance.wasmMemory.buffer,
).slice(utf8Data.address.toJS, (utf8Data.address + outSize.value).toJS),
).slice(utf8Data.address, utf8Data.address + outSize.value),
);
}

View File

@ -26,7 +26,7 @@ class SkwasmPath extends SkwasmObjectWrapper<RawPath> implements ScenePath {
SkwasmPath.fromHandle(PathHandle handle) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawPath> _registry = SkwasmFinalizationRegistry<RawPath>(
pathDispose,
(PathHandle handle) => pathDispose(handle),
);
@override

View File

@ -24,7 +24,9 @@ class SkwasmPathMetricIterator extends SkwasmObjectWrapper<RawContourMeasureIter
: super(contourMeasureIterCreate(path.handle, forceClosed, 1.0), _registry);
static final SkwasmFinalizationRegistry<RawContourMeasureIter> _registry =
SkwasmFinalizationRegistry<RawContourMeasureIter>(contourMeasureIterDispose);
SkwasmFinalizationRegistry<RawContourMeasureIter>(
(ContourMeasureIterHandle handle) => contourMeasureIterDispose(handle),
);
SkwasmPathMetric? _current;
int _nextIndex = 0;
@ -59,7 +61,9 @@ class SkwasmPathMetric extends SkwasmObjectWrapper<RawContourMeasure> implements
SkwasmPathMetric(ContourMeasureHandle handle, this.contourIndex) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawContourMeasure> _registry =
SkwasmFinalizationRegistry<RawContourMeasure>(contourMeasureDispose);
SkwasmFinalizationRegistry<RawContourMeasure>(
(ContourMeasureHandle handle) => contourMeasureDispose(handle),
);
@override
final int contourIndex;

View File

@ -10,7 +10,7 @@ class SkwasmPicture extends SkwasmObjectWrapper<RawPicture> implements ScenePict
SkwasmPicture.fromHandle(PictureHandle handle) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawPicture> _registry =
SkwasmFinalizationRegistry<RawPicture>(pictureDispose);
SkwasmFinalizationRegistry<RawPicture>((PictureHandle handle) => pictureDispose(handle));
@override
Future<ui.Image> toImage(int width, int height) async => toImageSync(width, height);
@ -43,7 +43,9 @@ class SkwasmPictureRecorder extends SkwasmObjectWrapper<RawPictureRecorder>
SkwasmPictureRecorder() : super(pictureRecorderCreate(), _registry);
static final SkwasmFinalizationRegistry<RawPictureRecorder> _registry =
SkwasmFinalizationRegistry<RawPictureRecorder>(pictureRecorderDispose);
SkwasmFinalizationRegistry<RawPictureRecorder>(
(PictureRecorderHandle handle) => pictureRecorderDispose(handle),
);
@override
SkwasmPicture endRecording() {

View File

@ -9,19 +9,11 @@ import 'dart:_wasm';
import 'dart:ffi';
import 'dart:js_interop';
@JS()
@staticInterop
class WebAssemblyMemory {}
extension WebAssemblyMemoryExtension on WebAssemblyMemory {
extension type WebAssemblyMemory._(JSObject _) implements JSObject {
external JSArrayBuffer get buffer;
}
@JS()
@staticInterop
class SkwasmInstance {}
extension SkwasmInstanceExtension on SkwasmInstance {
extension type SkwasmInstance._(JSObject _) implements JSObject {
external WebAssemblyMemory get wasmMemory;
}

View File

@ -362,7 +362,7 @@ class SkwasmRenderer implements Renderer {
}
final SkwasmImageDecoder decoder = SkwasmImageDecoder(
contentType: contentType,
dataSource: response.body as JSObject,
dataSource: response.body,
debugSource: uri.toString(),
);
await decoder.initialize();
@ -435,9 +435,9 @@ class SkwasmRenderer implements Renderer {
ui.Image createImageFromImageBitmap(DomImageBitmap imageSource) {
return SkwasmImage(
imageCreateFromTextureSource(
imageSource as JSObject,
imageSource.width.toDartInt,
imageSource.height.toDartInt,
imageSource,
imageSource.width,
imageSource.height,
surface.handle,
),
);

View File

@ -21,7 +21,7 @@ class SkwasmNativeShader extends SkwasmObjectWrapper<RawShader> implements Skwas
SkwasmNativeShader(ShaderHandle handle) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawShader> _registry =
SkwasmFinalizationRegistry<RawShader>(shaderDispose);
SkwasmFinalizationRegistry<RawShader>((ShaderHandle handle) => shaderDispose(handle));
}
class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient {
@ -223,7 +223,9 @@ class SkwasmFragmentProgram extends SkwasmObjectWrapper<RawRuntimeEffect>
}
static final SkwasmFinalizationRegistry<RawRuntimeEffect> _registry =
SkwasmFinalizationRegistry<RawRuntimeEffect>(runtimeEffectDispose);
SkwasmFinalizationRegistry<RawRuntimeEffect>(
(RuntimeEffectHandle handle) => runtimeEffectDispose(handle),
);
final String name;
final int floatUniformCount;
@ -239,7 +241,7 @@ class SkwasmShaderData extends SkwasmObjectWrapper<RawSkData> {
SkwasmShaderData(int size) : super(skDataCreate(size), _registry);
static final SkwasmFinalizationRegistry<RawSkData> _registry =
SkwasmFinalizationRegistry<RawSkData>(skDataDispose);
SkwasmFinalizationRegistry<RawSkData>((SkDataHandle handle) => skDataDispose(handle));
}
// This class does not inherit from SkwasmNativeShader, as its handle might

View File

@ -12,14 +12,9 @@ import 'package:ui/src/engine.dart';
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
import 'package:ui/ui.dart' as ui;
@JS()
@staticInterop
@anonymous
class RasterResult {}
extension RasterResultExtension on RasterResult {
external JSNumber get rasterStartMilliseconds;
external JSNumber get rasterEndMilliseconds;
extension type RasterResult._(JSObject _) implements JSObject {
external double get rasterStartMilliseconds;
external double get rasterEndMilliseconds;
external JSArray<JSAny> get imageBitmaps;
}
@ -104,8 +99,8 @@ class SkwasmSurface {
(await SkwasmCallbackHandler.instance.registerCallback(callbackId)) as RasterResult;
final RenderResult result = (
imageBitmaps: rasterResult.imageBitmaps.toDart.cast<DomImageBitmap>(),
rasterStartMicros: (rasterResult.rasterStartMilliseconds.toDartDouble * 1000).toInt(),
rasterEndMicros: (rasterResult.rasterEndMilliseconds.toDartDouble * 1000).toInt(),
rasterStartMicros: (rasterResult.rasterStartMilliseconds * 1000).toInt(),
rasterEndMicros: (rasterResult.rasterEndMilliseconds * 1000).toInt(),
);
return result;
});

View File

@ -68,5 +68,5 @@ class SkwasmVertices extends SkwasmObjectWrapper<RawVertices> implements ui.Vert
SkwasmVertices._(VerticesHandle handle) : super(handle, _registry);
static final SkwasmFinalizationRegistry<RawVertices> _registry =
SkwasmFinalizationRegistry<RawVertices>(verticesDispose);
SkwasmFinalizationRegistry<RawVertices>((VerticesHandle handle) => verticesDispose(handle));
}

View File

@ -6,20 +6,18 @@ import 'dart:js_interop';
import 'dom.dart';
@JS()
@staticInterop
class SVGElement extends DomElement {}
extension type SVGElement(JSObject _) implements JSObject, DomElement {}
SVGElement createSVGElement(String tag) =>
domDocument.createElementNS('http://www.w3.org/2000/svg', tag) as SVGElement;
@JS()
@staticInterop
class SVGGraphicsElement extends SVGElement {}
extension type SVGGraphicsElement(JSObject _) implements JSObject, SVGElement {}
@JS()
@staticInterop
class SVGSVGElement extends SVGGraphicsElement {}
extension type SVGSVGElement(JSObject _) implements JSObject, SVGGraphicsElement {
external SVGNumber createSVGNumber();
external SVGAnimatedLength? get height;
external SVGAnimatedLength? get width;
}
SVGSVGElement createSVGSVGElement() {
final SVGElement el = createSVGElement('svg');
@ -27,42 +25,24 @@ SVGSVGElement createSVGSVGElement() {
return el as SVGSVGElement;
}
extension SVGSVGElementExtension on SVGSVGElement {
external SVGNumber createSVGNumber();
external SVGAnimatedLength? get height;
external SVGAnimatedLength? get width;
}
@JS()
@staticInterop
class SVGClipPathElement extends SVGGraphicsElement {}
extension type SVGClipPathElement(JSObject _) implements JSObject, SVGGraphicsElement {}
SVGClipPathElement createSVGClipPathElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'clipPath') as SVGClipPathElement;
@JS()
@staticInterop
class SVGDefsElement extends SVGGraphicsElement {}
extension type SVGDefsElement(JSObject _) implements JSObject, SVGGraphicsElement {}
SVGDefsElement createSVGDefsElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'defs') as SVGDefsElement;
@JS()
@staticInterop
class SVGGeometryElement extends SVGGraphicsElement {}
extension type SVGGeometryElement(JSObject _) implements JSObject, SVGGraphicsElement {}
@JS()
@staticInterop
class SVGPathElement extends SVGGeometryElement {}
extension type SVGPathElement(JSObject _) implements JSObject, SVGGeometryElement {}
SVGPathElement createSVGPathElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'path') as SVGPathElement;
@JS()
@staticInterop
class SVGFilterElement extends SVGElement {}
extension SVGFilterElementExtension on SVGFilterElement {
extension type SVGFilterElement(JSObject _) implements JSObject, SVGElement {
external SVGAnimatedEnumeration? get filterUnits;
external SVGAnimatedLength? get height;
external SVGAnimatedLength? get width;
@ -73,46 +53,22 @@ extension SVGFilterElementExtension on SVGFilterElement {
SVGFilterElement createSVGFilterElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'filter') as SVGFilterElement;
@JS()
@staticInterop
class SVGAnimatedLength {}
extension SVGAnimatedLengthExtension on SVGAnimatedLength {
extension type SVGAnimatedLength(JSObject _) implements JSObject {
external SVGLength? get baseVal;
}
@JS()
@staticInterop
class SVGLength {}
extension SVGLengthExtension on SVGLength {
@JS('valueAsString')
external set _valueAsString(JSString? value);
set valueAsString(String? value) => _valueAsString = value?.toJS;
@JS('newValueSpecifiedUnits')
external JSVoid _newValueSpecifiedUnits(JSNumber unitType, JSNumber valueInSpecifiedUnits);
void newValueSpecifiedUnits(int unitType, num valueInSpecifiedUnits) =>
_newValueSpecifiedUnits(unitType.toJS, valueInSpecifiedUnits.toJS);
extension type SVGLength(JSObject _) implements JSObject {
external set valueAsString(String? value);
external void newValueSpecifiedUnits(int unitType, num valueInSpecifiedUnits);
}
const int svgLengthTypeNumber = 1;
@JS()
@staticInterop
class SVGAnimatedEnumeration {}
extension SVGAnimatedEnumerationExtenson on SVGAnimatedEnumeration {
@JS('baseVal')
external set _baseVal(JSNumber? value);
set baseVal(int? value) => _baseVal = value?.toJS;
extension type SVGAnimatedEnumeration(JSObject _) implements JSObject {
external set baseVal(int? value);
}
@JS()
@staticInterop
class SVGFEColorMatrixElement extends SVGElement {}
extension SVGFEColorMatrixElementExtension on SVGFEColorMatrixElement {
extension type SVGFEColorMatrixElement(JSObject _) implements JSObject, SVGElement {
external SVGAnimatedEnumeration? get type;
external SVGAnimatedString? get result;
external SVGAnimatedNumberList? get values;
@ -122,22 +78,14 @@ SVGFEColorMatrixElement createSVGFEColorMatrixElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'feColorMatrix')
as SVGFEColorMatrixElement;
@JS()
@staticInterop
class SVGFEFloodElement extends SVGElement {}
extension SVGFEFloodElementExtension on SVGFEFloodElement {
extension type SVGFEFloodElement(JSObject _) implements JSObject, SVGElement {
external SVGAnimatedString? get result;
}
SVGFEFloodElement createSVGFEFloodElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'feFlood') as SVGFEFloodElement;
@JS()
@staticInterop
class SVGFEBlendElement extends SVGElement {}
extension SVGFEBlendElementExtension on SVGFEBlendElement {
extension type SVGFEBlendElement(JSObject _) implements JSObject, SVGElement {
external SVGAnimatedString? get in1;
external SVGAnimatedString? get in2;
external SVGAnimatedEnumeration? get mode;
@ -146,11 +94,7 @@ extension SVGFEBlendElementExtension on SVGFEBlendElement {
SVGFEBlendElement createSVGFEBlendElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'feBlend') as SVGFEBlendElement;
@JS()
@staticInterop
class SVGFEImageElement extends SVGElement {}
extension SVGFEImageElementExtension on SVGFEImageElement {
extension type SVGFEImageElement(JSObject _) implements JSObject, SVGElement {
external SVGAnimatedLength? get height;
external SVGAnimatedLength? get width;
external SVGAnimatedString? get result;
@ -162,15 +106,7 @@ extension SVGFEImageElementExtension on SVGFEImageElement {
SVGFEImageElement createSVGFEImageElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'feImage') as SVGFEImageElement;
@JS()
@staticInterop
class SVGFECompositeElement extends SVGElement {}
SVGFECompositeElement createSVGFECompositeElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'feComposite')
as SVGFECompositeElement;
extension SVGFEBlendCompositeExtension on SVGFECompositeElement {
extension type SVGFECompositeElement(JSObject _) implements JSObject, SVGElement {
external SVGAnimatedString? get in1;
external SVGAnimatedString? get in2;
external SVGAnimatedNumber? get k1;
@ -181,48 +117,26 @@ extension SVGFEBlendCompositeExtension on SVGFECompositeElement {
external SVGAnimatedString? get result;
}
@JS()
@staticInterop
class SVGAnimatedString {}
SVGFECompositeElement createSVGFECompositeElement() =>
domDocument.createElementNS('http://www.w3.org/2000/svg', 'feComposite')
as SVGFECompositeElement;
extension SVGAnimatedStringExtension on SVGAnimatedString {
@JS('baseVal')
external set _baseVal(JSString? value);
set baseVal(String? value) => _baseVal = value?.toJS;
extension type SVGAnimatedString(JSObject _) implements JSObject {
external set baseVal(String? value);
}
@JS()
@staticInterop
class SVGAnimatedNumber {}
extension SVGAnimatedNumberExtension on SVGAnimatedNumber {
@JS('baseVal')
external set _baseVal(JSNumber? value);
set baseVal(num? value) => _baseVal = value?.toJS;
extension type SVGAnimatedNumber(JSObject _) implements JSObject {
external set baseVal(num? value);
}
@JS()
@staticInterop
class SVGAnimatedNumberList {}
extension SVGAnimatedNumberListExtension on SVGAnimatedNumberList {
extension type SVGAnimatedNumberList(JSObject _) implements JSObject {
external SVGNumberList? get baseVal;
}
@JS()
@staticInterop
class SVGNumberList {}
extension SVGNumberListExtension on SVGNumberList {
extension type SVGNumberList(JSObject _) implements JSObject {
external SVGNumber appendItem(SVGNumber newItem);
}
@JS()
@staticInterop
class SVGNumber {}
extension SVGNumberExtension on SVGNumber {
@JS('value')
external set _value(JSNumber? value);
set value(num? v) => _value = v?.toJS;
extension type SVGNumber(JSObject _) implements JSObject {
external set value(num? v);
}

View File

@ -59,17 +59,17 @@ mixin CompositionAwareMixin {
domElement.removeEventListener(_kCompositionEnd, _compositionEndListener);
}
JSVoid _handleCompositionStart(DomEvent event) {
void _handleCompositionStart(DomEvent event) {
composingText = null;
}
JSVoid _handleCompositionUpdate(DomEvent event) {
if (domInstanceOfString(event, 'CompositionEvent')) {
void _handleCompositionUpdate(DomEvent event) {
if (event.isA<DomCompositionEvent>()) {
composingText = (event as DomCompositionEvent).data;
}
}
JSVoid _handleCompositionEnd(DomEvent event) {
void _handleCompositionEnd(DomEvent event) {
composingText = null;
}

View File

@ -2,6 +2,8 @@
// 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 '../dom.dart';
/// Controls the capitalization of the text.
@ -68,10 +70,10 @@ class TextCapitalizationConfig {
case TextCapitalization.none:
autocapitalize = 'off';
}
if (domInstanceOfString(domElement, 'HTMLInputElement')) {
if (domElement.isA<DomHTMLInputElement>()) {
final DomHTMLInputElement element = domElement as DomHTMLInputElement;
element.setAttribute('autocapitalize', autocapitalize);
} else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) {
} else if (domElement.isA<DomHTMLTextAreaElement>()) {
final DomHTMLTextAreaElement element = domElement as DomHTMLTextAreaElement;
element.setAttribute('autocapitalize', autocapitalize);
}

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:js_interop';
import 'dart:math' as math;
import 'dart:typed_data';
@ -390,14 +391,18 @@ class EngineAutofillForm {
void addSubscriptionForKey(String key) {
final DomElement element = elements![key]!;
subscriptions.add(
DomSubscription(element, 'input', (DomEvent e) {
if (items![key] == null) {
throw StateError('AutofillInfo must have a valid uniqueIdentifier.');
} else {
final AutofillInfo autofillInfo = items![key]!;
handleChange(element, autofillInfo);
}
}),
DomSubscription(
element,
'input',
createDomEventListener((DomEvent e) {
if (items![key] == null) {
throw StateError('AutofillInfo must have a valid uniqueIdentifier.');
} else {
final AutofillInfo autofillInfo = items![key]!;
handleChange(element, autofillInfo);
}
}),
),
);
}
@ -502,7 +507,7 @@ class AutofillInfo {
void applyToDomElement(DomHTMLElement domElement, {bool focusedElement = false}) {
final String? autofillHint = this.autofillHint;
final String? placeholder = this.placeholder;
if (domInstanceOfString(domElement, 'HTMLInputElement')) {
if (domElement.isA<DomHTMLInputElement>()) {
final DomHTMLInputElement element = domElement as DomHTMLInputElement;
if (placeholder != null) {
element.placeholder = placeholder;
@ -517,7 +522,7 @@ class AutofillInfo {
}
}
element.autocomplete = autofillHint ?? 'on';
} else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) {
} else if (domElement.isA<DomHTMLTextAreaElement>()) {
final DomHTMLTextAreaElement element = domElement as DomHTMLTextAreaElement;
if (placeholder != null) {
element.placeholder = placeholder;
@ -830,8 +835,8 @@ class EditingState {
/// [domElement] can be a [InputElement] or a [TextAreaElement] depending on
/// the [InputType] of the text field.
factory EditingState.fromDomElement(DomHTMLElement? domElement) {
if (domInstanceOfString(domElement, 'HTMLInputElement')) {
final DomHTMLInputElement element = domElement! as DomHTMLInputElement;
if (domElement != null && domElement.isA<DomHTMLInputElement>()) {
final DomHTMLInputElement element = domElement as DomHTMLInputElement;
if (element.selectionDirection == 'backward') {
return EditingState(
text: element.value,
@ -845,8 +850,8 @@ class EditingState {
extentOffset: element.selectionEnd?.toInt(),
);
}
} else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) {
final DomHTMLTextAreaElement element = domElement! as DomHTMLTextAreaElement;
} else if (domElement != null && domElement.isA<DomHTMLTextAreaElement>()) {
final DomHTMLTextAreaElement element = domElement as DomHTMLTextAreaElement;
if (element.selectionDirection == 'backward') {
return EditingState(
text: element.value,
@ -959,12 +964,12 @@ class EditingState {
///
/// * [applyTextToDomElement], which is used for non-focused elements.
void applyToDomElement(DomHTMLElement? domElement) {
if (domInstanceOfString(domElement, 'HTMLInputElement')) {
final DomHTMLInputElement element = domElement! as DomHTMLInputElement;
if (domElement != null && domElement.isA<DomHTMLInputElement>()) {
final DomHTMLInputElement element = domElement as DomHTMLInputElement;
element.value = text;
element.setSelectionRange(minOffset, maxOffset);
} else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) {
final DomHTMLTextAreaElement element = domElement! as DomHTMLTextAreaElement;
} else if (domElement != null && domElement.isA<DomHTMLTextAreaElement>()) {
final DomHTMLTextAreaElement element = domElement as DomHTMLTextAreaElement;
element.value = text;
element.setSelectionRange(minOffset, maxOffset);
} else {
@ -982,11 +987,11 @@ class EditingState {
///
/// * [applyToDomElement], which is used for focused elements.
void applyTextToDomElement(DomHTMLElement? domElement) {
if (domInstanceOfString(domElement, 'HTMLInputElement')) {
final DomHTMLInputElement element = domElement! as DomHTMLInputElement;
if (domElement != null && domElement.isA<DomHTMLInputElement>()) {
final DomHTMLInputElement element = domElement as DomHTMLInputElement;
element.value = text;
} else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) {
final DomHTMLTextAreaElement element = domElement! as DomHTMLTextAreaElement;
} else if (domElement != null && domElement.isA<DomHTMLTextAreaElement>()) {
final DomHTMLTextAreaElement element = domElement as DomHTMLTextAreaElement;
element.value = text;
} else {
throw UnsupportedError('Unsupported DOM element type');
@ -1363,18 +1368,28 @@ abstract class DefaultTextEditingStrategy
}
// Subscribe to text and selection changes.
subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange));
subscriptions.add(
DomSubscription(activeDomElement, 'input', createDomEventListener(handleChange)),
);
subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction));
subscriptions.add(
DomSubscription(activeDomElement, 'keydown', createDomEventListener(maybeSendAction)),
);
subscriptions.add(DomSubscription(domDocument, 'selectionchange', handleChange));
subscriptions.add(
DomSubscription(domDocument, 'selectionchange', createDomEventListener(handleChange)),
);
subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', handleBeforeInput));
subscriptions.add(
DomSubscription(activeDomElement, 'beforeinput', createDomEventListener(handleBeforeInput)),
);
if (this is! SafariDesktopTextEditingStrategy) {
// handleBlur causes Safari to reopen autofill dialogs after autofill,
// so we don't attach the listener there.
subscriptions.add(DomSubscription(activeDomElement, 'blur', handleBlur));
subscriptions.add(
DomSubscription(activeDomElement, 'blur', createDomEventListener(handleBlur)),
);
}
addCompositionEventHandlers(activeDomElement);
@ -1529,7 +1544,7 @@ abstract class DefaultTextEditingStrategy
}
void maybeSendAction(DomEvent e) {
if (domInstanceOfString(e, 'KeyboardEvent')) {
if (e.isA<DomKeyboardEvent>()) {
final DomKeyboardEvent event = e as DomKeyboardEvent;
if (event.keyCode == _kReturnKeyCode) {
onAction!(inputConfiguration.inputAction);
@ -1580,21 +1595,33 @@ abstract class DefaultTextEditingStrategy
/// see: https://bugs.chromium.org/p/chromium/issues/detail?id=119216#c11.
void preventDefaultForMouseEvents() {
subscriptions.add(
DomSubscription(activeDomElement, 'mousedown', (DomEvent event) {
event.preventDefault();
}),
DomSubscription(
activeDomElement,
'mousedown',
createDomEventListener((DomEvent event) {
event.preventDefault();
}),
),
);
subscriptions.add(
DomSubscription(activeDomElement, 'mouseup', (DomEvent event) {
event.preventDefault();
}),
DomSubscription(
activeDomElement,
'mouseup',
createDomEventListener((DomEvent event) {
event.preventDefault();
}),
),
);
subscriptions.add(
DomSubscription(activeDomElement, 'mousemove', (DomEvent event) {
event.preventDefault();
}),
DomSubscription(
activeDomElement,
'mousemove',
createDomEventListener((DomEvent event) {
event.preventDefault();
}),
),
);
}
@ -1679,24 +1706,38 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
}
// Subscribe to text and selection changes.
subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange));
subscriptions.add(
DomSubscription(activeDomElement, 'input', createDomEventListener(handleChange)),
);
subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction));
subscriptions.add(
DomSubscription(activeDomElement, 'keydown', createDomEventListener(maybeSendAction)),
);
subscriptions.add(DomSubscription(domDocument, 'selectionchange', handleChange));
subscriptions.add(
DomSubscription(domDocument, 'selectionchange', createDomEventListener(handleChange)),
);
subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', handleBeforeInput));
subscriptions.add(
DomSubscription(activeDomElement, 'beforeinput', createDomEventListener(handleBeforeInput)),
);
subscriptions.add(DomSubscription(activeDomElement, 'blur', handleBlur));
subscriptions.add(
DomSubscription(activeDomElement, 'blur', createDomEventListener(handleBlur)),
);
addCompositionEventHandlers(activeDomElement);
// Position the DOM element after it is focused.
subscriptions.add(
DomSubscription(activeDomElement, 'focus', (_) {
// Cancel previous timer if exists.
_schedulePlacement();
}),
DomSubscription(
activeDomElement,
'focus',
createDomEventListener((DomEvent _) {
// Cancel previous timer if exists.
_schedulePlacement();
}),
),
);
_addTapListener();
@ -1736,18 +1777,22 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
/// placed to its correct position after [_delayBeforePlacement].
void _addTapListener() {
subscriptions.add(
DomSubscription(activeDomElement, 'click', (_) {
// Check if the element is already positioned. If not this does not fall
// under `The user was using the long press, now they want to enter text
// via keyboard` journey.
if (_canPosition) {
// Re-place the element somewhere outside of the screen.
initializeElementPlacement();
DomSubscription(
activeDomElement,
'click',
createDomEventListener((DomEvent _) {
// Check if the element is already positioned. If not this does not fall
// under `The user was using the long press, now they want to enter text
// via keyboard` journey.
if (_canPosition) {
// Re-place the element somewhere outside of the screen.
initializeElementPlacement();
// Re-configure the timer to place the element.
_schedulePlacement();
}
}),
// Re-configure the timer to place the element.
_schedulePlacement();
}
}),
),
);
}
@ -1799,15 +1844,25 @@ class AndroidTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
}
// Subscribe to text and selection changes.
subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange));
subscriptions.add(
DomSubscription(activeDomElement, 'input', createDomEventListener(handleChange)),
);
subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction));
subscriptions.add(
DomSubscription(activeDomElement, 'keydown', createDomEventListener(maybeSendAction)),
);
subscriptions.add(DomSubscription(domDocument, 'selectionchange', handleChange));
subscriptions.add(
DomSubscription(domDocument, 'selectionchange', createDomEventListener(handleChange)),
);
subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', handleBeforeInput));
subscriptions.add(
DomSubscription(activeDomElement, 'beforeinput', createDomEventListener(handleBeforeInput)),
);
subscriptions.add(DomSubscription(activeDomElement, 'blur', handleBlur));
subscriptions.add(
DomSubscription(activeDomElement, 'blur', createDomEventListener(handleBlur)),
);
addCompositionEventHandlers(activeDomElement);
@ -1847,11 +1902,17 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
}
// Subscribe to text and selection changes.
subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange));
subscriptions.add(
DomSubscription(activeDomElement, 'input', createDomEventListener(handleChange)),
);
subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction));
subscriptions.add(
DomSubscription(activeDomElement, 'keydown', createDomEventListener(maybeSendAction)),
);
subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', handleBeforeInput));
subscriptions.add(
DomSubscription(activeDomElement, 'beforeinput', createDomEventListener(handleBeforeInput)),
);
addCompositionEventHandlers(activeDomElement);
@ -1869,17 +1930,25 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
// After each keyup, the start/end values of the selection is compared to
// the previously saved editing state.
subscriptions.add(
DomSubscription(activeDomElement, 'keyup', (DomEvent event) {
handleChange(event);
}),
DomSubscription(
activeDomElement,
'keyup',
createDomEventListener((DomEvent event) {
handleChange(event);
}),
),
);
// In Firefox the context menu item "Select All" does not work without
// listening to onSelect. On the other browsers onSelectionChange is
// enough for covering "Select All" functionality.
subscriptions.add(DomSubscription(activeDomElement, 'select', handleChange));
subscriptions.add(
DomSubscription(activeDomElement, 'select', createDomEventListener(handleChange)),
);
subscriptions.add(DomSubscription(activeDomElement, 'blur', handleBlur));
subscriptions.add(
DomSubscription(activeDomElement, 'blur', createDomEventListener(handleBlur)),
);
preventDefaultForMouseEvents();
}

View File

@ -29,7 +29,11 @@ class FullPageDimensionsProvider extends DimensionsProvider {
final DomEventTarget resizeEventTarget = domWindow.visualViewport ?? domWindow;
// Subscribe to the 'resize' event, and convert it to a ui.Size stream.
_domResizeSubscription = DomSubscription(resizeEventTarget, 'resize', _onVisualViewportResize);
_domResizeSubscription = DomSubscription(
resizeEventTarget,
'resize',
createDomEventListener(_onVisualViewportResize),
);
}
late DomSubscription _domResizeSubscription;

View File

@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:js_interop';
import 'package:meta/meta.dart';
import 'package:ui/src/engine/display.dart';
@ -47,7 +46,7 @@ class DisplayDprStream {
} else {
_dprMediaQuery = domWindow.matchMedia('(resolution: ${_currentDpr}dppx)');
}
_dprMediaQuery.addEventListenerWithOptions(
_dprMediaQuery.addEventListener(
'change',
createDomEventListener(_onDprMediaQueryChange),
<String, Object>{
@ -59,14 +58,14 @@ class DisplayDprStream {
// listener from the old mediaQuery after we're done with it.
'once': true,
'passive': true,
},
}.toJSAnyDeep,
);
}
// Handler of the _dprMediaQuery 'change' event.
//
// This calls subscribe again because events are listened to with `once: true`.
JSVoid _onDprMediaQueryChange(DomEvent _) {
void _onDprMediaQueryChange(DomEvent _) {
_currentDpr = _display.devicePixelRatio;
_dprStreamController.add(_currentDpr);
// Re-subscribe...

View File

@ -53,7 +53,7 @@ class HotRestartCacheHandler {
/// Registers a [DomElement] to be removed after hot-restart.
@visibleForTesting
void registerElement(DomElement element) {
_jsHotRestartStore!.push(element as JSObject);
_jsHotRestartStore!.push(element);
}
}

View File

@ -4,7 +4,7 @@
import 'package:meta/meta.dart';
import 'package:ui/src/engine/dom.dart' show DomNavigatorExtension, DomWindowExtension, domWindow;
import 'package:ui/src/engine/dom.dart' show domWindow;
/// The HTML engine used by the current browser.
enum BrowserEngine {

View File

@ -28,7 +28,7 @@ Future<ui.Codec> createImageCodecFromUrl(Uri uri, {ImageCodecChunkCallback? chun
///
/// See https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap
FutureOr<ui.Image> createImageFromImageBitmap(JSAny imageSource) {
if (!domInstanceOfString(imageSource, 'ImageBitmap')) {
if (!imageSource.isA<DomImageBitmap>()) {
throw ArgumentError('Image source $imageSource is not an ImageBitmap.', 'imageSource');
}
return renderer.createImageFromImageBitmap(imageSource as DomImageBitmap);

View File

@ -89,7 +89,10 @@ class BrowserPlatformLocation implements PlatformLocation {
@visibleForTesting
DomEventListener getOrCreateDomEventListener(EventListener fn) {
return _popStateListenersCache.putIfAbsent(fn, () => createDomEventListener(fn));
final DomEventListener jsListener = createDomEventListener((DomEvent event) {
fn(event);
});
return _popStateListenersCache.putIfAbsent(fn, () => jsListener);
}
@override
@ -114,7 +117,7 @@ class BrowserPlatformLocation implements PlatformLocation {
String get search => _location.search!;
@override
String get hash => _location.locationHash;
String get hash => _location.hash;
@override
Object? get state => _history.state;

View File

@ -6,7 +6,6 @@ environment:
sdk: ^3.7.0-0
dependencies:
js: ^0.7.0
meta: ^1.14.0
web_locale_keymap:
path: ../../third_party/web_locale_keymap

View File

@ -477,7 +477,7 @@ void drawTestPicture(CkCanvas canvas) {
}
CkImage generateTestImage() {
final DomCanvasElement canvas = createDomCanvasElement(width: 20, height: 20);
final DomHTMLCanvasElement canvas = createDomCanvasElement(width: 20, height: 20);
final DomCanvasRenderingContext2D ctx = canvas.context2D;
ctx.fillStyle = '#FF0000';
ctx.fillRect(0, 0, 10, 10);

View File

@ -1573,7 +1573,7 @@ void _paragraphTests() {
final SkParagraph paragraph = builder.build();
paragraph.layout(500);
final DomCanvasElement canvas = createDomCanvasElement(width: 400, height: 160);
final DomHTMLCanvasElement canvas = createDomCanvasElement(width: 400, height: 160);
domDocument.body!.append(canvas);
// TODO(yjbanov): WebGL screenshot tests do not work on Firefox - https://github.com/flutter/flutter/issues/109265
@ -1721,7 +1721,7 @@ void _paragraphTests() {
test(
'MakeOnScreenGLSurface test',
() {
final DomCanvasElement canvas = createDomCanvasElement(width: 100, height: 100);
final DomHTMLCanvasElement canvas = createDomCanvasElement(width: 100, height: 100);
final WebGLContext gl = canvas.getGlContext(webGLVersion);
final int sampleCount = gl.getParameter(gl.samples);
final int stencilBits = gl.getParameter(gl.stencilBits);
@ -1748,7 +1748,7 @@ void _paragraphTests() {
test(
'MakeRenderTarget test',
() {
final DomCanvasElement canvas = createDomCanvasElement(width: 100, height: 100);
final DomHTMLCanvasElement canvas = createDomCanvasElement(width: 100, height: 100);
final int glContext =
canvasKit.GetWebGLContext(

View File

@ -100,7 +100,7 @@ class BitmapTestCodec extends TestFileCodec {
await imageElement.decode();
final DomImageBitmap bitmap = await createImageBitmap(imageElement as JSObject, (
final DomImageBitmap bitmap = await createImageBitmap(imageElement, (
x: 0,
y: 0,
width: imageElement.naturalWidth.toInt(),
@ -138,7 +138,8 @@ class BitmapSingleFrameCodec implements ui.Codec {
Future<void> testMain() async {
final HttpFetchResponse listingResponse = await httpFetch('/test_images/');
final List<String> testFiles = (await listingResponse.json() as List<dynamic>).cast<String>();
final List<String> testFiles =
((await listingResponse.json() as JSAny?).dartify()! as List<Object?>).cast<String>();
List<TestCodec> createTestCodecs({int testTargetWidth = 300, int testTargetHeight = 300}) {
// Sanity-check the test file list. If suddenly test files are moved or

View File

@ -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:js_interop';
import 'dart:js_interop_unsafe';
import 'package:test/bootstrap/browser.dart';
@ -22,16 +21,8 @@ void testMain() {
await bootstrapAndRunApp();
// window.exports and window.module should be undefined!
expect(
(domWindow as JSObject).has('exports'),
isFalse,
reason: '`window.exports` should not be defined.',
);
expect(
(domWindow as JSObject).has('module'),
isFalse,
reason: '`window.module` should not be defined.',
);
expect(domWindow.has('exports'), isFalse, reason: '`window.exports` should not be defined.');
expect(domWindow.has('module'), isFalse, reason: '`window.module` should not be defined.');
});
});
}

View File

@ -17,7 +17,7 @@ void testMain() {
group('initializeEngineServices', () {
test('stores user configuration', () async {
final JsFlutterConfiguration config = JsFlutterConfiguration();
(config as JSObject)
config
// `canvasKitBaseUrl` is required for the test to actually run.
..['canvasKitBaseUrl'] = '/canvaskit/'.toJS
// A property under test, that we'll try to read later.
@ -26,7 +26,7 @@ void testMain() {
..['nonexistentProperty'] = 32.0.toJS;
// Remove window.flutterConfiguration (if it's there)
(domWindow as JSObject)['flutterConfiguration'] = null;
config['flutterConfiguration'] = null;
// TODO(web): Replace the above nullification by the following assertion
// when wasm and JS tests initialize their config the same way:

View File

@ -252,10 +252,7 @@ class TestSkDeletableMock {
JsConstructor get constructor => TestJsConstructor(name: 'TestSkDeletable');
}
@JS()
@anonymous
@staticInterop
class TestSkDeletable implements SkDeletable {
extension type TestSkDeletable._primary(JSObject _) implements SkDeletable {
factory TestSkDeletable() {
final TestSkDeletableMock mock = TestSkDeletableMock();
return TestSkDeletable._(
@ -278,10 +275,7 @@ class TestSkDeletable implements SkDeletable {
});
}
@JS()
@anonymous
@staticInterop
class TestJsConstructor implements JsConstructor {
extension type TestJsConstructor._(JSObject _) implements JsConstructor {
external factory TestJsConstructor({String name});
}
@ -323,7 +317,7 @@ class _MockNativeMemoryFinalizationRegistry implements NativeMemoryFinalizationR
final List<_MockPair> registeredPairs = <_MockPair>[];
@override
void register(Object owner, UniqueRef<Object> ref) {
void register(Object owner, UniqueRef<JSObject> ref) {
registeredPairs.add(_MockPair(owner, ref));
}
}
@ -332,5 +326,5 @@ class _MockPair {
_MockPair(this.owner, this.ref);
Object owner;
UniqueRef<Object> ref;
UniqueRef<JSObject> ref;
}

View File

@ -112,7 +112,7 @@ void testMain() {
final Surface surface = Surface(isDisplayCanvas: true);
surface.createOrUpdateSurface(const BitmapSize(9, 19));
final DomCanvasElement original = getDisplayCanvas(surface);
final DomHTMLCanvasElement original = getDisplayCanvas(surface);
ui.Size canvasSize = getCssSize(surface);
// Expect exact requested dimensions.
@ -123,7 +123,7 @@ void testMain() {
// Shrinking causes us to resize the canvas.
surface.createOrUpdateSurface(const BitmapSize(5, 15));
final DomCanvasElement shrunk = getDisplayCanvas(surface);
final DomHTMLCanvasElement shrunk = getDisplayCanvas(surface);
canvasSize = getCssSize(surface);
expect(shrunk.width, 5);
expect(shrunk.height, 15);
@ -132,7 +132,7 @@ void testMain() {
// Increasing the size causes us to resize the canvas.
surface.createOrUpdateSurface(const BitmapSize(10, 20));
final DomCanvasElement firstIncrease = getDisplayCanvas(surface);
final DomHTMLCanvasElement firstIncrease = getDisplayCanvas(surface);
canvasSize = getCssSize(surface);
expect(firstIncrease, same(original));
@ -145,7 +145,7 @@ void testMain() {
// Subsequent increases also cause canvas resizing.
surface.createOrUpdateSurface(const BitmapSize(11, 22));
final DomCanvasElement secondIncrease = getDisplayCanvas(surface);
final DomHTMLCanvasElement secondIncrease = getDisplayCanvas(surface);
canvasSize = getCssSize(surface);
expect(secondIncrease, same(firstIncrease));
@ -156,7 +156,7 @@ void testMain() {
// Increases beyond the 40% limit will cause a canvas resize.
surface.createOrUpdateSurface(const BitmapSize(20, 40));
final DomCanvasElement huge = getDisplayCanvas(surface);
final DomHTMLCanvasElement huge = getDisplayCanvas(surface);
canvasSize = getCssSize(surface);
expect(huge, same(secondIncrease));
@ -169,7 +169,7 @@ void testMain() {
// Shrink again. Resize the canvas.
surface.createOrUpdateSurface(const BitmapSize(5, 15));
final DomCanvasElement shrunk2 = getDisplayCanvas(surface);
final DomHTMLCanvasElement shrunk2 = getDisplayCanvas(surface);
canvasSize = getCssSize(surface);
expect(shrunk2, same(huge));
@ -182,7 +182,7 @@ void testMain() {
// This tests https://github.com/flutter/flutter/issues/77084
EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(2.0);
surface.createOrUpdateSurface(const BitmapSize(5, 15));
final DomCanvasElement dpr2Canvas = getDisplayCanvas(surface);
final DomHTMLCanvasElement dpr2Canvas = getDisplayCanvas(surface);
canvasSize = getCssSize(surface);
expect(dpr2Canvas, same(huge));
@ -363,15 +363,15 @@ void testMain() {
});
}
DomCanvasElement getDisplayCanvas(Surface surface) {
DomHTMLCanvasElement getDisplayCanvas(Surface surface) {
assert(surface.isDisplayCanvas);
return surface.hostElement.children.first as DomCanvasElement;
return surface.hostElement.children.first as DomHTMLCanvasElement;
}
/// Extracts the CSS style values of 'width' and 'height' and returns them
/// as a [ui.Size].
ui.Size getCssSize(Surface surface) {
final DomCanvasElement canvas = getDisplayCanvas(surface);
final DomHTMLCanvasElement canvas = getDisplayCanvas(surface);
final String cssWidth = canvas.style.width;
final String cssHeight = canvas.style.height;
// CSS width and height should be in the form 'NNNpx'. So cut off the 'px' and

View File

@ -5,6 +5,7 @@
/// Provides utilities for testing engine code.
library matchers;
import 'dart:js_interop';
import 'dart:math' as math;
import 'package:html/dom.dart' as html;
@ -259,14 +260,19 @@ class HtmlPatternMatcher extends Matcher {
@override
bool matches(final Object? object, Map<Object?, Object?> matchState) {
if (object is! DomElement) {
// TODO(srujzs): Replace this with `!object.isJSAny` once we have that API
// in `dart:js_interop`.
// https://github.com/dart-lang/sdk/issues/56905
// ignore: invalid_runtime_check_with_js_interop_types
if (object is! JSAny || !object.isA<DomElement>()) {
return false;
}
final List<String> mismatches = <String>[];
matchState['mismatches'] = mismatches;
final html.Element element = html.parseFragment(object.outerHTML).children.single;
final html.Element element =
html.parseFragment((object as DomElement).outerHTML).children.single;
matchElements(_Breadcrumbs.root, mismatches, element, pattern);
return mismatches.isEmpty;
}

View File

@ -5,7 +5,7 @@
@TestOn('browser')
library;
import 'package:js/js_util.dart' as js_util;
import 'dart:js_interop';
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
@ -27,7 +27,7 @@ void testMain() {
test('legacy constructor initializes with a Js Object', () async {
final FlutterConfiguration config = FlutterConfiguration.legacy(
js_util.jsify(<String, Object?>{'canvasKitBaseUrl': '/some_other_url/'})
<String, Object?>{'canvasKitBaseUrl': '/some_other_url/'}.jsify()!
as JsFlutterConfiguration,
);
@ -38,13 +38,13 @@ void testMain() {
group('setUserConfiguration', () {
test('throws assertion error if already initialized from JS', () async {
final FlutterConfiguration config = FlutterConfiguration.legacy(
js_util.jsify(<String, Object?>{'canvasKitBaseUrl': '/some_other_url/'})
<String, Object?>{'canvasKitBaseUrl': '/some_other_url/'}.jsify()!
as JsFlutterConfiguration,
);
expect(() {
config.setUserConfiguration(
js_util.jsify(<String, Object?>{'canvasKitBaseUrl': '/yet_another_url/'})
<String, Object?>{'canvasKitBaseUrl': '/yet_another_url/'}.jsify()!
as JsFlutterConfiguration,
);
}, throwsAssertionError);
@ -54,8 +54,7 @@ void testMain() {
final FlutterConfiguration config = FlutterConfiguration.legacy(null);
config.setUserConfiguration(
js_util.jsify(<String, Object?>{'canvasKitBaseUrl': '/one_more_url/'})
as JsFlutterConfiguration,
<String, Object?>{'canvasKitBaseUrl': '/one_more_url/'}.jsify()! as JsFlutterConfiguration,
);
expect(config.canvasKitBaseUrl, '/one_more_url/');
@ -66,7 +65,7 @@ void testMain() {
expect(() {
config.setUserConfiguration(
js_util.jsify(<String, Object?>{'nonexistentProperty': 32.0}) as JsFlutterConfiguration,
<String, Object?>{'nonexistentProperty': 32.0}.jsify()! as JsFlutterConfiguration,
);
}, returnsNormally);
});
@ -76,9 +75,7 @@ void testMain() {
late FlutterConfiguration defaultConfig;
setUp(() {
defaultConfig = FlutterConfiguration();
defaultConfig.setUserConfiguration(
js_util.jsify(<String, Object?>{}) as JsFlutterConfiguration,
);
defaultConfig.setUserConfiguration(<String, Object?>{}.jsify()! as JsFlutterConfiguration);
});
test('canvasKitVariant', () {
@ -96,7 +93,7 @@ void testMain() {
final FlutterConfiguration config = FlutterConfiguration();
config.setUserConfiguration(
// With an empty map, the canvasKitVariant is undefined in JS.
js_util.jsify(<String, Object?>{}) as JsFlutterConfiguration,
<String, Object?>{}.jsify()! as JsFlutterConfiguration,
);
expect(config.canvasKitVariant, CanvasKitVariant.auto);
@ -106,23 +103,22 @@ void testMain() {
final FlutterConfiguration config = FlutterConfiguration();
config.setUserConfiguration(
js_util.jsify(<String, Object?>{'canvasKitVariant': 'foo'}) as JsFlutterConfiguration,
<String, Object?>{'canvasKitVariant': 'foo'}.jsify()! as JsFlutterConfiguration,
);
expect(() => config.canvasKitVariant, throwsArgumentError);
config.setUserConfiguration(
js_util.jsify(<String, Object?>{'canvasKitVariant': 'auto'}) as JsFlutterConfiguration,
<String, Object?>{'canvasKitVariant': 'auto'}.jsify()! as JsFlutterConfiguration,
);
expect(config.canvasKitVariant, CanvasKitVariant.auto);
config.setUserConfiguration(
js_util.jsify(<String, Object?>{'canvasKitVariant': 'full'}) as JsFlutterConfiguration,
<String, Object?>{'canvasKitVariant': 'full'}.jsify()! as JsFlutterConfiguration,
);
expect(config.canvasKitVariant, CanvasKitVariant.full);
config.setUserConfiguration(
js_util.jsify(<String, Object?>{'canvasKitVariant': 'chromium'})
as JsFlutterConfiguration,
<String, Object?>{'canvasKitVariant': 'chromium'}.jsify()! as JsFlutterConfiguration,
);
expect(config.canvasKitVariant, CanvasKitVariant.chromium);
});
@ -131,7 +127,7 @@ void testMain() {
test('multiViewEnabled', () {
final FlutterConfiguration config = FlutterConfiguration();
config.setUserConfiguration(
js_util.jsify(<String, Object?>{'multiViewEnabled': true}) as JsFlutterConfiguration,
<String, Object?>{'multiViewEnabled': true}.jsify()! as JsFlutterConfiguration,
);
expect(config.multiViewEnabled, isTrue);
});

View File

@ -93,7 +93,7 @@ Future<void> _testSuccessfulPayloads() async {
expect(response.hasPayload, isTrue);
expect(response.payload, isNotNull);
expect(response.url, '/test_images/');
expect(await response.json(), isA<List<Object?>>());
expect(((await response.json()) as JSAny?).isA<JSArray>(), isTrue);
});
test('httpFetch reads data in chunks', () async {
@ -119,9 +119,7 @@ Future<void> _testSuccessfulPayloads() async {
expect(response.url, url);
final List<int> result = <int>[];
await response.payload.read<JSUint8Array>(
(JSUint8Array chunk) => result.addAll(chunk.toDart),
);
await response.payload.read((JSUint8Array chunk) => result.addAll(chunk.toDart));
expect(result, hasLength(length));
expect(result, List<int>.generate(length, (int i) => i & 0xFF));
}
@ -154,7 +152,7 @@ Future<void> _testSuccessfulPayloads() async {
test('httpFetchJson fetches json', () async {
final Object? json = await httpFetchJson('/test_images/');
expect(json, isA<List<Object?>>());
expect((json as JSAny?).isA<JSArray>(), isTrue);
});
}

View File

@ -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 'dart:js_interop';
import 'dart:typed_data';
import 'package:test/bootstrap/browser.dart';
@ -21,7 +22,8 @@ Future<void> testMain() async {
Future<List<String>> createTestFiles() async {
final HttpFetchResponse listingResponse = await httpFetch('/test_images/');
List<String> testFiles = (await listingResponse.json() as List<dynamic>).cast<String>();
List<String> testFiles =
((await listingResponse.json() as JSAny?).dartify()! as List<Object?>).cast<String>();
testFiles = testFiles.map((String baseName) => '/test_images/$baseName').toList();
// Sanity-check the test file list. If suddenly test files are moved or

View File

@ -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 'dart:js_interop';
import 'dart:js_util' as js_util;
import 'package:meta/meta.dart';
@ -83,7 +84,7 @@ void testMain() {
test('_PointerEventContext generates expected events', () {
DomPointerEvent expectCorrectType(DomEvent e) {
expect(domInstanceOfString(e, 'PointerEvent'), isTrue);
expect(e.isA<DomPointerEvent>(), isTrue);
return e as DomPointerEvent;
}
@ -2526,7 +2527,7 @@ void testMain() {
Listener.register(
event: 'custom-event',
target: eventTarget,
handler: (event) {
handler: (DomEvent event) {
expect(event, expected);
handled = true;
},
@ -2541,7 +2542,7 @@ void testMain() {
final Listener listener = Listener.register(
event: 'custom-event',
target: eventTarget,
handler: (event) {
handler: (DomEvent event) {
handled = true;
},
);

View File

@ -9,6 +9,7 @@ import 'package:test/test.dart';
import 'package:ui/src/engine.dart' hide window;
import 'package:ui/ui.dart' as ui;
import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
import 'package:ui/ui_web/src/ui_web/navigation/url_strategy.dart';
import '../common/matchers.dart';
import '../common/test_initialization.dart';
@ -507,7 +508,7 @@ class _SampleUrlStrategy implements ui_web.UrlStrategy {
Object? getState() => _state;
@override
ui.VoidCallback addPopStateListener(DartDomEventListener listener) {
ui.VoidCallback addPopStateListener(PopStateListener listener) {
wasUsed = true;
return () {};
}

View File

@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:js_interop';
import 'dart:typed_data';
import 'package:test/bootstrap/browser.dart';
@ -21,7 +20,7 @@ void main() {
}
class StubPictureRenderer implements PictureRenderer {
final DomCanvasElement scratchCanvasElement = createDomCanvasElement(width: 500, height: 500);
final DomHTMLCanvasElement scratchCanvasElement = createDomCanvasElement(width: 500, height: 500);
@override
Future<RenderResult> renderPictures(List<ScenePicture> pictures) async {
@ -29,7 +28,7 @@ class StubPictureRenderer implements PictureRenderer {
final List<DomImageBitmap> bitmaps = await Future.wait(
pictures.map((ScenePicture picture) {
final ui.Rect cullRect = picture.cullRect;
final Future<DomImageBitmap> bitmap = createImageBitmap(scratchCanvasElement as JSObject, (
final Future<DomImageBitmap> bitmap = createImageBitmap(scratchCanvasElement, (
x: 0,
y: 0,
width: cullRect.width.toInt(),

View File

@ -1582,7 +1582,7 @@ void _testVerticalScrolling() {
final scrollable = owner().debugSemanticsTree![0]!.semanticRole! as SemanticScrollable;
final scrollEvent = createDomEvent('Event', 'scroll') as JSAny;
final listener = scrollable.scrollListener! as JSFunction;
final listener = scrollable.scrollListener!;
expect(() => listener.callAsFunction(null, scrollEvent), returnsNormally);
semantics().semanticsEnabled = false;

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:js_interop';
import 'dart:js_util' as js_util;
import 'dart:typed_data';
@ -1151,7 +1152,9 @@ Future<void> testMain() async {
test('Moves the focus across input elements', () async {
final List<DomEvent> focusinEvents = <DomEvent>[];
final DomEventListener handleFocusIn = createDomEventListener(focusinEvents.add);
final DomEventListener handleFocusIn = createDomEventListener((DomEvent event) {
focusinEvents.add(event);
});
final MethodCall setClient1 = MethodCall('TextInput.setClient', <dynamic>[
123,
@ -3640,8 +3643,8 @@ void cleanTestFlags() {
void checkInputEditingState(DomElement? element, String text, int start, int end) {
expect(element, isNotNull);
expect(domInstanceOfString(element, 'HTMLInputElement'), true);
final DomHTMLInputElement input = element! as DomHTMLInputElement;
expect(element!.isA<DomHTMLInputElement>(), true);
final DomHTMLInputElement input = element as DomHTMLInputElement;
expect(defaultTextEditingRoot.ownerDocument?.activeElement, input);
expect(input.value, text);
expect(input.selectionStart, start);

View File

@ -103,7 +103,7 @@ void doTests() {
test('Initializes and attaches a shadow root', () {
final DomManager domManager = DomManager(devicePixelRatio: 3.0);
expect(domInstanceOfString(domManager.renderingHost, 'ShadowRoot'), isTrue);
expect(domManager.renderingHost.isA<DomShadowRoot>(), isTrue);
expect(domManager.renderingHost.host, domManager.platformViewsHost);
expect(domManager.renderingHost, domManager.platformViewsHost.shadowRoot);

View File

@ -106,12 +106,7 @@ Future<void> doTests() async {
}
// The JS-interop definition of the `initialData` object passed to the views of this app.
@JS()
@staticInterop
class InitialData {}
/// The attributes of the [InitialData] object.
extension InitialDataExtension on InitialData {
extension type InitialData._(JSObject _) implements JSObject {
external int get someInt;
external String? get someString;

View File

@ -17,7 +17,7 @@ void main() {
}
@JS()
external JSBoolean get crossOriginIsolated;
external bool get crossOriginIsolated;
Future<void> testMain() async {
setUpUnitTests(setUpTestViewDimensions: false);
@ -27,7 +27,7 @@ Future<void> testMain() async {
expect(isWasm, isTrue);
expect(isSkwasm, isTrue);
final bool shouldBeMultiThreaded =
crossOriginIsolated.toDart && !configuration.forceSingleThreadedSkwasm;
crossOriginIsolated && !configuration.forceSingleThreadedSkwasm;
expect(isMultiThreaded, shouldBeMultiThreaded);
} else {
expect(isWasm, isFalse);

View File

@ -8,7 +8,6 @@ import 'dart:typed_data';
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine/canvaskit/image.dart';
import 'package:ui/src/engine/dom.dart';
import 'package:ui/src/engine/html_image_element_codec.dart';
import 'package:ui/ui.dart' as ui;
import 'package:ui/ui_web/src/ui_web.dart' as ui_web;

View File

@ -4,7 +4,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:js_interop';
import 'dart:math';
import 'dart:typed_data';
@ -422,18 +421,18 @@ Future<void> testMain() async {
image.src = url;
await completer.future;
final DomImageBitmap bitmap = await createImageBitmap(image as JSObject);
final DomImageBitmap bitmap = await createImageBitmap(image);
domWindow.URL.revokeObjectURL(url);
expect(bitmap.width.toDartInt, 150);
expect(bitmap.height.toDartInt, 150);
expect(bitmap.width, 150);
expect(bitmap.height, 150);
final ui.Image uiImage = await renderer.createImageFromImageBitmap(bitmap);
if (isSkwasm && isMultiThreaded) {
// Multi-threaded skwasm transfers the bitmap to the web worker, so it should be
// disposed/consumed.
expect(bitmap.width.toDartInt, 0);
expect(bitmap.height.toDartInt, 0);
expect(bitmap.width, 0);
expect(bitmap.height, 0);
}
return uiImage;
});

View File

@ -62,8 +62,7 @@ import 'dart:collection';
import 'dart:convert' hide Codec;
import 'dart:developer' as developer;
import 'dart:js_util' as js_util;
import 'dart:_js_annotations';
import 'dart:js_interop' hide JS;
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'dart:math' as math;
import 'dart:typed_data';

View File

@ -17,7 +17,7 @@ import 'dart:io';
import 'package:test/test.dart';
// Libraries that allow making arbitrary calls to JavaScript.
const List<String> _jsAccessLibraries = <String>['dart:js_util', 'package:js'];
const List<String> _jsAccessLibraries = <String>['dart:js_util'];
// Libraries that are allowed to make direct calls to JavaScript. These
// libraries must be reviewed carefully to make sure JavaScript APIs are used

View File

@ -31,8 +31,7 @@ import 'dart:collection';
import 'dart:convert' hide Codec;
import 'dart:developer' as developer;
import 'dart:js_util' as js_util;
import 'dart:_js_annotations';
import 'dart:js_interop' hide JS;
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'dart:math' as math;
import 'dart:typed_data';
@ -70,8 +69,7 @@ import 'dart:collection';
import 'dart:convert' hide Codec;
import 'dart:developer' as developer;
import 'dart:js_util' as js_util;
import 'dart:_js_annotations';
import 'dart:js_interop' hide JS;
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'dart:math' as math;
import 'dart:typed_data';

View File

@ -4,9 +4,6 @@
// ignore_for_file: avoid_dynamic_calls
@JS()
library test.host;
import 'dart:async';
import 'dart:convert';
import 'dart:js_interop';
@ -17,30 +14,21 @@ import 'package:stream_channel/stream_channel.dart';
import 'package:ui/src/engine/dom.dart';
/// A class defined in content shell, used to control its behavior.
@JS()
@staticInterop
class _TestRunner {}
extension _TestRunnerExtension on _TestRunner {
external JSVoid waitUntilDone();
extension type _TestRunner(JSObject _) implements JSObject {
external void waitUntilDone();
}
/// Returns the current content shell runner, or `null` if none exists.
@JS()
@JS('testRunner')
external _TestRunner? get testRunner; // ignore: library_private_types_in_public_api
/// A class that exposes the test API to JS.
///
/// These are exposed so that tools like IDEs can interact with them via remote
/// debugging.
@JS()
@anonymous
@staticInterop
class _JSApi {
external factory _JSApi({JSFunction resume, JSFunction restartCurrent});
}
extension type _JSApi._(JSObject _) implements JSObject {
external _JSApi({JSFunction resume, JSFunction restartCurrent});
extension _JSApiExtension on _JSApi {
/// Causes the test runner to resume running, as though the user had clicked
/// the "play" button.
// ignore: unused_element
@ -225,52 +213,68 @@ StreamChannel<dynamic> _connectToIframe(String url, int id) {
_domSubscriptions[id] = domSubscriptions;
_streamSubscriptions[id] = streamSubscriptions;
domSubscriptions.add(
DomSubscription(domWindow, 'message', (DomEvent event) {
final DomMessageEvent message = event as DomMessageEvent;
// A message on the Window can theoretically come from any website. It's
// very unlikely that a malicious site would care about hacking someone's
// unit tests, let alone be able to find the test server while it's
// running, but it's good practice to check the origin anyway.
if (message.origin != domWindow.location.origin) {
return;
}
if (message.source.location?.href != iframe.src) {
return;
}
DomSubscription(
domWindow,
'message',
createDomEventListener((DomEvent event) {
final DomMessageEvent message = event as DomMessageEvent;
// A message on the Window can theoretically come from any website. It's
// very unlikely that a malicious site would care about hacking someone's
// unit tests, let alone be able to find the test server while it's
// running, but it's good practice to check the origin anyway.
if (message.origin != domWindow.location.origin) {
return;
}
// We have to do these ugly casts because the message is cross-origin
// which isn't handled cleanly by dart:js_interop.
if (((message.source as DomMessageEventSource?)?.location as DomMessageEventLocation?)
?.href !=
iframe.src) {
return;
}
message.stopPropagation();
message.stopPropagation();
if (message.data == 'port') {
final DomMessagePort port = message.ports.first;
domSubscriptions.add(
DomSubscription(port, 'message', (DomEvent event) {
controller.local.sink.add((event as DomMessageEvent).data);
}),
);
port.start();
streamSubscriptions.add(controller.local.stream.listen(port.postMessage));
} else if (message.data['ready'] == true) {
// This message indicates that the iframe is actively listening for
// events, so the message channel's second port can now be transferred.
final DomMessageChannel channel = createDomMessageChannel();
channel.port1.start();
channel.port2.start();
iframe.contentWindow.postMessage('port', domWindow.location.origin, <DomMessagePort>[
channel.port2,
]);
domSubscriptions.add(
DomSubscription(channel.port1, 'message', (DomEvent message) {
controller.local.sink.add((message as DomMessageEvent).data['data']);
}),
);
if (message.data == 'port') {
final DomMessagePort port = message.ports[0];
domSubscriptions.add(
DomSubscription(
port,
'message',
createDomEventListener((DomEvent event) {
controller.local.sink.add((event as DomMessageEvent).data);
}),
),
);
port.start();
streamSubscriptions.add(controller.local.stream.listen(port.postMessage));
} else if (message.data['ready'] == true) {
// This message indicates that the iframe is actively listening for
// events, so the message channel's second port can now be transferred.
final DomMessageChannel channel = createDomMessageChannel();
channel.port1.start();
channel.port2.start();
iframe.contentWindow.postMessage('port', domWindow.location.origin, <DomMessagePort>[
channel.port2,
]);
domSubscriptions.add(
DomSubscription(
channel.port1,
'message',
createDomEventListener((DomEvent message) {
controller.local.sink.add((message as DomMessageEvent).data['data']);
}),
),
);
streamSubscriptions.add(controller.local.stream.listen(channel.port1.postMessage));
} else if (message.data['exception'] == true) {
// This message from `dart.js` indicates that an exception occurred
// loading the test.
controller.local.sink.add(message.data['data']);
}
}),
streamSubscriptions.add(controller.local.stream.listen(channel.port1.postMessage));
} else if (message.data['exception'] == true) {
// This message from `dart.js` indicates that an exception occurred
// loading the test.
controller.local.sink.add(message.data['data']);
}
}),
),
);
return controller.foreign;