mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[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:
parent
943dd30bfd
commit
7cfaf04fa9
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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});
|
||||
}
|
||||
|
||||
|
||||
@ -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?]);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -36,7 +36,7 @@ class SemanticIncrementable extends SemanticRole {
|
||||
|
||||
_element.addEventListener(
|
||||
'change',
|
||||
createDomEventListener((_) {
|
||||
createDomEventListener((DomEvent _) {
|
||||
if (_element.disabled!) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -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';
|
||||
|
||||
|
||||
@ -163,7 +163,7 @@ class SemanticScrollable extends SemanticRole {
|
||||
};
|
||||
EngineSemantics.instance.addGestureModeListener(_gestureModeListener!);
|
||||
|
||||
scrollListener = createDomEventListener((_) {
|
||||
scrollListener = createDomEventListener((DomEvent _) {
|
||||
if (!_canScroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,5 +369,7 @@ class SkwasmMaskFilter extends SkwasmObjectWrapper<RawMaskFilter> {
|
||||
);
|
||||
|
||||
static final SkwasmFinalizationRegistry<RawMaskFilter> _registry =
|
||||
SkwasmFinalizationRegistry<RawMaskFilter>(maskFilterDispose);
|
||||
SkwasmFinalizationRegistry<RawMaskFilter>(
|
||||
(MaskFilterHandle handle) => maskFilterDispose(handle),
|
||||
);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
),
|
||||
);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
});
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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...
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
});
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
},
|
||||
);
|
||||
|
||||
@ -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 () {};
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
});
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user