[web] Migrate Flutter Web DOM usage to JS static interop - 21. (flutter/engine#33349)

This commit is contained in:
joshualitt 2022-05-31 11:15:55 -07:00 committed by GitHub
parent 8d7d82192e
commit 4617fb4bb2
6 changed files with 176 additions and 86 deletions

View File

@ -80,6 +80,7 @@ extension DomDocumentExtension on DomDocument {
class DomHTMLDocument extends DomDocument {}
extension DomHTMLDocumentExtension on DomHTMLDocument {
external DomFontFaceSet? get fonts;
external DomHTMLHeadElement? get head;
external DomHTMLBodyElement? get body;
}
@ -168,12 +169,14 @@ extension DomElementExtension on DomElement {
js_util.getProperty<List<Object?>>(this, 'children').cast<DomElement>();
external String get id;
external set id(String id);
external set innerHtml(String? html);
external String? get outerHTML;
external set spellcheck(bool? value);
external String get tagName;
external DomCSSStyleDeclaration get style;
external void append(DomNode node);
external String? getAttribute(String attributeName);
external DomRect getBoundingClientRect();
external void prepend(DomNode node);
external DomElement? querySelector(String selectors);
List<DomElement> querySelectorAll(String selectors) =>
@ -189,65 +192,68 @@ extension DomElementExtension on DomElement {
class DomCSSStyleDeclaration {}
extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
set width(String value) => setProperty('width', value, '');
set height(String value) => setProperty('height', value, '');
set position(String value) => setProperty('position', value, '');
set clip(String value) => setProperty('clip', value, '');
set clipPath(String value) => setProperty('clip-path', value, '');
set transform(String value) => setProperty('transform', value, '');
set width(String value) => setProperty('width', value);
set height(String value) => setProperty('height', value);
set position(String value) => setProperty('position', value);
set clip(String value) => setProperty('clip', value);
set clipPath(String value) => setProperty('clip-path', value);
set transform(String value) => setProperty('transform', value);
set transformOrigin(String value) =>
setProperty('transform-origin', value, '');
set opacity(String value) => setProperty('opacity', value, '');
set color(String value) => setProperty('color', value, '');
set top(String value) => setProperty('top', value, '');
set left(String value) => setProperty('left', value, '');
set right(String value) => setProperty('right', value, '');
set bottom(String value) => setProperty('bottom', value, '');
setProperty('transform-origin', value);
set opacity(String value) => setProperty('opacity', value);
set color(String value) => setProperty('color', value);
set top(String value) => setProperty('top', value);
set left(String value) => setProperty('left', value);
set right(String value) => setProperty('right', value);
set bottom(String value) => setProperty('bottom', value);
set backgroundColor(String value) =>
setProperty('background-color', value, '');
set pointerEvents(String value) => setProperty('pointer-events', value, '');
set filter(String value) => setProperty('filter', value, '');
set zIndex(String value) => setProperty('z-index', value, '');
set whiteSpace(String value) => setProperty('white-space', value, '');
set lineHeight(String value) => setProperty('line-height', value, '');
set textStroke(String value) => setProperty('-webkit-text-stroke', value, '');
set fontSize(String value) => setProperty('font-size', value, '');
set fontWeight(String value) => setProperty('font-weight', value, '');
set fontStyle(String value) => setProperty('font-style', value, '');
set fontFamily(String value) => setProperty('font-family', value, '');
set letterSpacing(String value) => setProperty('letter-spacing', value, '');
set wordSpacing(String value) => setProperty('word-spacing', value, '');
set textShadow(String value) => setProperty('text-shadow', value, '');
set textDecoration(String value) => setProperty('text-decoration', value, '');
setProperty('background-color', value);
set pointerEvents(String value) => setProperty('pointer-events', value);
set filter(String value) => setProperty('filter', value);
set zIndex(String value) => setProperty('z-index', value);
set whiteSpace(String value) => setProperty('white-space', value);
set lineHeight(String value) => setProperty('line-height', value);
set textStroke(String value) => setProperty('-webkit-text-stroke', value);
set fontSize(String value) => setProperty('font-size', value);
set fontWeight(String value) => setProperty('font-weight', value);
set fontStyle(String value) => setProperty('font-style', value);
set fontFamily(String value) => setProperty('font-family', value);
set letterSpacing(String value) => setProperty('letter-spacing', value);
set wordSpacing(String value) => setProperty('word-spacing', value);
set textShadow(String value) => setProperty('text-shadow', value);
set textDecoration(String value) => setProperty('text-decoration', value);
set textDecorationColor(String value) =>
setProperty('text-decoration-color', value, '');
setProperty('text-decoration-color', value);
set fontFeatureSettings(String value) =>
setProperty('font-feature-settings', value, '');
setProperty('font-feature-settings', value);
set fontVariationSettings(String value) =>
setProperty('font-variation-settings', value, '');
set visibility(String value) => setProperty('visibility', value, '');
set overflow(String value) => setProperty('overflow', value, '');
set boxShadow(String value) => setProperty('box-shadow', value, '');
setProperty('font-variation-settings', value);
set visibility(String value) => setProperty('visibility', value);
set overflow(String value) => setProperty('overflow', value);
set boxShadow(String value) => setProperty('box-shadow', value);
set borderTopLeftRadius(String value) =>
setProperty('border-top-left-radius', value, '');
setProperty('border-top-left-radius', value);
set borderTopRightRadius(String value) =>
setProperty('border-top-right-radius', value, '');
setProperty('border-top-right-radius', value);
set borderBottomLeftRadius(String value) =>
setProperty('border-bottom-left-radius', value, '');
setProperty('border-bottom-left-radius', value);
set borderBottomRightRadius(String value) =>
setProperty('border-bottom-right-radius', value, '');
set borderRadius(String value) => setProperty('border-radius', value, '');
set perspective(String value) => setProperty('perspective', value, '');
set padding(String value) => setProperty('padding', value, '');
setProperty('border-bottom-right-radius', value);
set borderRadius(String value) => setProperty('border-radius', value);
set perspective(String value) => setProperty('perspective', value);
set padding(String value) => setProperty('padding', value);
set backgroundImage(String value) =>
setProperty('background-image', value, '');
set border(String value) => setProperty('border', value, '');
set mixBlendMode(String value) => setProperty('mix-blend-mode', value, '');
set backgroundSize(String value) =>
setProperty('background-size', value, '');
setProperty('background-image', value);
set border(String value) => setProperty('border', value);
set mixBlendMode(String value) => setProperty('mix-blend-mode', value);
set backgroundSize(String value) => setProperty('background-size', value);
set backgroundBlendMode(String value) =>
setProperty('background-blend-mode', value, '');
set transformStyle(String value) => setProperty('transform-style', value, '');
setProperty('background-blend-mode', value);
set transformStyle(String value) => setProperty('transform-style', value);
set display(String value) => setProperty('display', value);
set flexDirection(String value) => setProperty('flex-direction', value);
set alignItems(String value) => setProperty('align-items', value);
set margin(String value) => setProperty('margin', value);
String get width => getPropertyValue('width');
String get height => getPropertyValue('height');
String get position => getPropertyValue('position');
@ -298,6 +304,10 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
String get backgroundSize => getPropertyValue('background-size');
String get backgroundBlendMode => getPropertyValue('background-blend-mode');
String get transformStyle => getPropertyValue('transform-style');
String get display => getPropertyValue('display');
String get flexDirection => getPropertyValue('flex-direction');
String get alignItems => getPropertyValue('align-items');
String get margin => getPropertyValue('margin');
external String getPropertyValue(String property);
void setProperty(String propertyName, String value, [String? priority]) {
@ -313,6 +323,10 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
@staticInterop
class DomHTMLElement extends DomElement {}
extension DomHTMLElementExtension on DomHTMLElement {
int get offsetWidth => js_util.getProperty<num>(this, 'offsetWidth') as int;
}
@JS()
@staticInterop
class DomHTMLMetaElement extends DomHTMLElement {}
@ -369,6 +383,21 @@ class DomHTMLButtonElement extends DomHTMLElement {}
DomHTMLButtonElement createDomHTMLButtonElement() =>
domDocument.createElement('button') as DomHTMLButtonElement;
@JS()
@staticInterop
class DomHTMLParagraphElement extends DomHTMLElement {}
DomHTMLParagraphElement createDomHTMLParagraphElement() =>
domDocument.createElement('p') as DomHTMLParagraphElement;
@JS()
@staticInterop
class DomHTMLStyleElement extends DomHTMLElement {}
extension DomHTMLStyleElementExtension on DomHTMLStyleElement {
external set type(String? value);
}
@JS()
@staticInterop
class DomPerformance extends DomEventTarget {}
@ -434,6 +463,7 @@ class DomCanvasRenderingContext2D {}
extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D {
external Object? get fillStyle;
external set fillStyle(Object? style);
external String get font;
external set font(String value);
external set lineWidth(num? value);
external set strokeStyle(Object? value);
@ -452,6 +482,7 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D {
<Object>[text, x, y, if (maxWidth != null) maxWidth]);
external DomImageData getImageData(int x, int y, int sw, int sh);
external void lineTo(num x, num y);
external DomTextMetrics measureText(String text);
external void moveTo(num x, num y);
external void save();
external void stroke();
@ -516,6 +547,14 @@ class DomText extends DomCharacterData {}
DomText createDomText(String data) => domDocument.createTextNode(data);
@JS()
@staticInterop
class DomTextMetrics {}
extension DomTextMetricsExtension on DomTextMetrics {
external num? get width;
}
@JS()
@staticInterop
class DomException {
@ -526,6 +565,51 @@ extension DomExceptionExtension on DomException {
external String get name;
}
@JS()
@staticInterop
class DomRectReadOnly {}
extension DomRectReadOnlyExtension on DomRectReadOnly {
external num get x;
external num get y;
external num get width;
external num get height;
external num get top;
external num get right;
external num get bottom;
external num get left;
}
@JS()
@staticInterop
class DomRect extends DomRectReadOnly {}
@JS()
@staticInterop
class DomFontFace {}
DomFontFace createDomFontFace(String family, Object source,
[Map<Object?, Object?>? descriptors]) =>
domCallConstructorString('FontFace', <Object>[
family,
source,
if (descriptors != null) js_util.jsify(descriptors)
])! as DomFontFace;
extension DomFontFaceExtension on DomFontFace {
Future<DomFontFace> load() =>
js_util.promiseToFuture(js_util.callMethod(this, 'load', <Object>[]));
}
@JS()
@staticInterop
class DomFontFaceSet extends DomEventTarget {}
extension DomFontFaceSetExtension on DomFontFaceSet {
external DomFontFaceSet? add(DomFontFace font);
external void clear();
}
extension DomResponseExtension on DomResponse {
Future<dynamic> arrayBuffer() => js_util
.promiseToFuture(js_util.callMethod(this, 'arrayBuffer', <Object>[]));

View File

@ -11,7 +11,8 @@ import 'dom.dart';
class SVGElement extends DomElement {}
SVGElement createSVGElement(String tag) =>
domDocument.createElementNS('http://www.w3.org/2000/svg', tag) as SVGElement;
domDocument.createElementNS('http://www.w3.org/2000/svg', tag)
as SVGElement;
@JS()
@staticInterop

View File

@ -4,11 +4,11 @@
import 'dart:async';
import 'dart:convert';
import 'dart:html' as html;
import 'dart:typed_data';
import '../assets.dart';
import '../browser_detection.dart';
import '../dom.dart';
import '../safe_browser_api.dart';
import '../util.dart';
import 'layout_service.dart';
@ -105,7 +105,7 @@ class FontCollection {
_assetFontManager = null;
_testFontManager = null;
if (supportsFontsClearApi) {
html.document.fonts!.clear();
domDocument.fonts!.clear();
}
}
}
@ -139,27 +139,27 @@ class FontManager {
/// Browsers and browsers versions differ significantly on how a valid font
/// family name should be formatted. Notable issues are:
///
/// Safari 12 and Firefox crash if you create a [html.FontFace] with a font
/// Safari 12 and Firefox crash if you create a [DomFontFace] with a font
/// family that is not correct CSS syntax. Font family names with invalid
/// characters are accepted accepted on these browsers, when wrapped it in
/// quotes.
///
/// Additionally, for Safari 12 to work [html.FontFace] name should be
/// Additionally, for Safari 12 to work [DomFontFace] name should be
/// loaded correctly on the first try.
///
/// A font in Chrome is not usable other than inside a '<p>' tag, if a
/// [html.FontFace] is loaded wrapped with quotes. Unlike Safari 12 if a
/// [DomFontFace] is loaded wrapped with quotes. Unlike Safari 12 if a
/// valid version of the font is also loaded afterwards it will show
/// that font normally.
///
/// In Safari 13 the [html.FontFace] should be loaded with unquoted family
/// In Safari 13 the [DomFontFace] should be loaded with unquoted family
/// names.
///
/// In order to avoid all these browser compatibility issues this method:
/// * Detects the family names that might cause a conflict.
/// * Loads it with the quotes.
/// * Loads it again without the quotes.
/// * For all the other family names [html.FontFace] is loaded only once.
/// * For all the other family names [DomFontFace] is loaded only once.
///
/// See also:
///
@ -187,9 +187,9 @@ class FontManager {
) {
// try/catch because `new FontFace` can crash with an improper font family.
try {
final html.FontFace fontFace = html.FontFace(family, asset, descriptors);
final DomFontFace fontFace = createDomFontFace(family, asset, descriptors);
_fontLoadingFutures.add(fontFace.load().then((_) {
html.document.fonts!.add(fontFace);
domDocument.fonts!.add(fontFace);
}, onError: (dynamic e) {
printWarning('Error while trying to load font family "$family":\n$e');
}));
@ -202,15 +202,15 @@ class FontManager {
Future<void> _loadFontFaceBytes(String family, Uint8List list) {
// Since these fonts are loaded by user code, surface the error
// through the returned future.
final html.FontFace fontFace = html.FontFace(family, list);
final DomFontFace fontFace = createDomFontFace(family, list);
return fontFace.load().then((_) {
html.document.fonts!.add(fontFace);
domDocument.fonts!.add(fontFace);
// There might be paragraph measurements for this new font before it is
// loaded. They were measured using fallback font, so we should clear the
// cache.
Spanometer.clearRulersCache();
}, onError: (dynamic exception) {
// Failures here will throw an html.DomException which confusingly
// Failures here will throw an DomException which confusingly
// does not implement Exception or Error. Rethrow an Exception so it can
// be caught in user code without depending on dart:html or requiring a
// catch block without "on".
@ -245,7 +245,7 @@ class _PolyfillFontManager extends FontManager {
String asset,
Map<String, String> descriptors,
) {
final html.ParagraphElement paragraph = html.ParagraphElement();
final DomHTMLParagraphElement paragraph = createDomHTMLParagraphElement();
paragraph.style.position = 'absolute';
paragraph.style.visibility = 'hidden';
paragraph.style.fontSize = '72px';
@ -253,14 +253,14 @@ class _PolyfillFontManager extends FontManager {
browserEngine == BrowserEngine.ie11 ? 'Times New Roman' : 'sans-serif';
paragraph.style.fontFamily = fallbackFontName;
if (descriptors['style'] != null) {
paragraph.style.fontStyle = descriptors['style'];
paragraph.style.fontStyle = descriptors['style']!;
}
if (descriptors['weight'] != null) {
paragraph.style.fontWeight = descriptors['weight'];
paragraph.style.fontWeight = descriptors['weight']!;
}
paragraph.text = _testString;
html.document.body!.append(paragraph);
domDocument.body!.append(paragraph);
final int sansSerifWidth = paragraph.offsetWidth;
paragraph.style.fontFamily = "'$family', $fallbackFontName";
@ -297,10 +297,10 @@ class _PolyfillFontManager extends FontManager {
final String fontFaceDeclaration = fontStyleMap.keys
.map((String name) => '$name: ${fontStyleMap[name]};')
.join(' ');
final html.StyleElement fontLoadStyle = html.StyleElement();
final DomHTMLStyleElement fontLoadStyle = DomHTMLStyleElement();
fontLoadStyle.type = 'text/css';
fontLoadStyle.innerHtml = '@font-face { $fontFaceDeclaration }';
html.document.head!.append(fontLoadStyle);
domDocument.head!.append(fontLoadStyle);
// HACK: If this is an icon font, then when it loads it won't change the
// width of our test string. So we just have to hope it loads before the

View File

@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:html' as html;
import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'package:ui/ui.dart' as ui;
import '../dom.dart';
import 'canvas_paragraph.dart';
import 'line_breaker.dart';
import 'measurement.dart';
@ -17,13 +17,13 @@ import 'text_direction.dart';
/// Performs layout on a [CanvasParagraph].
///
/// It uses a [html.CanvasElement] to measure text.
/// It uses a [DomCanvasElement] to measure text.
class TextLayoutService {
TextLayoutService(this.paragraph);
final CanvasParagraph paragraph;
final html.CanvasRenderingContext2D context = html.CanvasElement().context2D;
final DomCanvasRenderingContext2D context = createDomCanvasElement().context2D;
// *** Results of layout *** //
@ -1582,7 +1582,7 @@ class Spanometer {
Spanometer(this.paragraph, this.context);
final CanvasParagraph paragraph;
final html.CanvasRenderingContext2D context;
final DomCanvasRenderingContext2D context;
static RulerHost _rulerHost = RulerHost();

View File

@ -5,6 +5,7 @@
import 'dart:html' as html;
import '../../engine.dart' show registerHotRestartListener;
import '../dom.dart';
import '../embedder.dart';
// TODO(yjbanov): this is a hack we use to compute ideographic baseline; this
@ -70,7 +71,7 @@ double _lastWidth = -1;
/// This method assumes that the correct font has already been set on
/// [_canvasContext].
double measureSubstring(
html.CanvasRenderingContext2D _canvasContext,
DomCanvasRenderingContext2D _canvasContext,
String text,
int start,
int end, {

View File

@ -7,6 +7,7 @@ import 'dart:html' as html;
import 'package:ui/ui.dart' as ui;
import '../browser_detection.dart';
import '../dom.dart';
import '../embedder.dart';
import '../util.dart';
import 'measurement.dart';
@ -98,8 +99,8 @@ class TextHeightStyle {
class TextDimensions {
TextDimensions(this._element);
final html.Element _element;
html.Rectangle<num>? _cachedBoundingClientRect;
final DomElement _element;
DomRect? _cachedBoundingClientRect;
void _invalidateBoundsCache() {
_cachedBoundingClientRect = null;
@ -114,10 +115,10 @@ class TextDimensions {
void applyHeightStyle(TextHeightStyle textHeightStyle) {
final String fontFamily = textHeightStyle.fontFamily;
final double fontSize = textHeightStyle.fontSize;
final html.CssStyleDeclaration style = _element.style;
final DomCSSStyleDeclaration style = _element.style;
style
..fontSize = '${fontSize.floor()}px'
..fontFamily = canonicalizeFontFamily(fontFamily);
..fontFamily = canonicalizeFontFamily(fontFamily)!;
final double? height = textHeightStyle.height;
if (height != null) {
@ -128,12 +129,12 @@ class TextDimensions {
/// Appends element and probe to hostElement that is set up for a specific
/// TextStyle.
void appendToHost(html.HtmlElement hostElement) {
void appendToHost(DomHTMLElement hostElement) {
hostElement.append(_element);
_invalidateBoundsCache();
}
html.Rectangle<num> _readAndCacheMetrics() =>
DomRect _readAndCacheMetrics() =>
_cachedBoundingClientRect ??= _element.getBoundingClientRect();
/// The height of the paragraph being measured.
@ -166,9 +167,9 @@ class TextHeightRuler {
final RulerHost rulerHost;
// Elements used to measure the line-height metric.
late final html.HtmlElement _probe = _createProbe();
late final html.HtmlElement _host = _createHost();
final TextDimensions _dimensions = TextDimensions(html.document.createElement('flt-paragraph'));
late final DomHTMLElement _probe = _createProbe();
late final DomHTMLElement _host = _createHost();
final TextDimensions _dimensions = TextDimensions(domDocument.createElement('flt-paragraph'));
/// The alphabetic baseline for this ruler's [textHeightStyle].
late final double alphabeticBaseline = _probe.getBoundingClientRect().bottom.toDouble();
@ -181,8 +182,8 @@ class TextHeightRuler {
_host.remove();
}
html.HtmlElement _createHost() {
final html.DivElement host = html.DivElement();
DomHTMLElement _createHost() {
final DomHTMLDivElement host = createDomHTMLDivElement();
host.style
..visibility = 'hidden'
..position = 'absolute'
@ -208,12 +209,15 @@ class TextHeightRuler {
_dimensions.updateTextToSpace();
_dimensions.appendToHost(host);
rulerHost.addElement(host);
// [rulerHost] is not migrated yet so add a cast to [html.HtmlElement].
// This cast will be removed after the migration is complete.
rulerHost.addElement(host as html.HtmlElement);
return host;
}
html.HtmlElement _createProbe() {
final html.HtmlElement probe = html.DivElement();
DomHTMLElement _createProbe() {
final DomHTMLElement probe = createDomHTMLDivElement();
_host.append(probe);
return probe;
}