diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index 80e998f3bdbcc..d3846bc57933e 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart @@ -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>(this, 'children').cast(); 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 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(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 { [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? descriptors]) => + domCallConstructorString('FontFace', [ + family, + source, + if (descriptors != null) js_util.jsify(descriptors) + ])! as DomFontFace; + +extension DomFontFaceExtension on DomFontFace { + Future load() => + js_util.promiseToFuture(js_util.callMethod(this, 'load', [])); +} + +@JS() +@staticInterop +class DomFontFaceSet extends DomEventTarget {} + +extension DomFontFaceSetExtension on DomFontFaceSet { + external DomFontFaceSet? add(DomFontFace font); + external void clear(); +} + extension DomResponseExtension on DomResponse { Future arrayBuffer() => js_util .promiseToFuture(js_util.callMethod(this, 'arrayBuffer', [])); diff --git a/lib/web_ui/lib/src/engine/svg.dart b/lib/web_ui/lib/src/engine/svg.dart index 4c5c77d339fb6..791fffcd04f34 100644 --- a/lib/web_ui/lib/src/engine/svg.dart +++ b/lib/web_ui/lib/src/engine/svg.dart @@ -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 diff --git a/lib/web_ui/lib/src/engine/text/font_collection.dart b/lib/web_ui/lib/src/engine/text/font_collection.dart index 2f5fa237c00bf..bfb969b1f764c 100644 --- a/lib/web_ui/lib/src/engine/text/font_collection.dart +++ b/lib/web_ui/lib/src/engine/text/font_collection.dart @@ -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 '

' 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 _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 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 diff --git a/lib/web_ui/lib/src/engine/text/layout_service.dart b/lib/web_ui/lib/src/engine/text/layout_service.dart index e4a02017874b3..71274024819cc 100644 --- a/lib/web_ui/lib/src/engine/text/layout_service.dart +++ b/lib/web_ui/lib/src/engine/text/layout_service.dart @@ -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(); diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index 0d9adc807ebe2..6eac6daa733f9 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -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, { diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index 668848581c198..5079b3589f5df 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -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? _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 _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; }