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