[web] Migrate Flutter Web DOM usage to JS static interop - 5 (flutter/engine#33020)

This commit is contained in:
joshualitt 2022-05-06 09:09:37 -07:00 committed by GitHub
parent 7585f8938d
commit d493db8aac
6 changed files with 77 additions and 62 deletions

View File

@ -10,6 +10,7 @@ import 'package:ui/ui.dart' as ui;
import '../../engine.dart' show platformViewManager;
import '../configuration.dart';
import '../dom.dart';
import '../html/path_to_svg_clip.dart';
import '../platform_views/slots.dart';
import '../util.dart';
@ -212,10 +213,10 @@ class HtmlViewEmbedder {
void _compositeWithParams(int viewId, EmbeddedViewParams params) {
// If we haven't seen this viewId yet, cache it for clips/transforms.
final ViewClipChain clipChain = _viewClipChains.putIfAbsent(viewId, () {
return ViewClipChain(view: createPlatformViewSlot(viewId));
return ViewClipChain(view: createPlatformViewSlot(viewId) as DomElement);
});
final html.Element slot = clipChain.slot;
final DomElement slot = clipChain.slot;
// See `apply()` in the PersistedPlatformView class for the HTML version
// of this code.
@ -228,8 +229,8 @@ class HtmlViewEmbedder {
final int currentClippingCount = _countClips(params.mutators);
final int previousClippingCount = clipChain.clipCount;
if (currentClippingCount != previousClippingCount) {
final html.Element oldPlatformViewRoot = clipChain.root;
final html.Element newPlatformViewRoot = _reconstructClipViewsChain(
final DomElement oldPlatformViewRoot = clipChain.root;
final DomElement newPlatformViewRoot = _reconstructClipViewsChain(
currentClippingCount,
slot,
oldPlatformViewRoot,
@ -255,26 +256,26 @@ class HtmlViewEmbedder {
return clipCount;
}
html.Element _reconstructClipViewsChain(
DomElement _reconstructClipViewsChain(
int numClips,
html.Element platformView,
html.Element headClipView,
DomElement platformView,
DomElement headClipView,
) {
int indexInFlutterView = -1;
if (headClipView.parent != null) {
if (headClipView.parentElement != null) {
indexInFlutterView = skiaSceneHost!.children.indexOf(headClipView);
headClipView.remove();
}
html.Element head = platformView;
DomElement head = platformView;
int clipIndex = 0;
// Re-use as much existing clip views as needed.
while (head != headClipView && clipIndex < numClips) {
head = head.parent!;
head = head.parentElement!;
clipIndex++;
}
// If there weren't enough existing clip views, add more.
while (clipIndex < numClips) {
final html.Element clippingView = html.Element.tag('flt-clip');
final DomElement clippingView = createDomElement('flt-clip');
clippingView.append(head);
head = clippingView;
clipIndex++;
@ -309,9 +310,9 @@ class HtmlViewEmbedder {
}
void _applyMutators(
EmbeddedViewParams params, html.Element embeddedView, int viewId) {
EmbeddedViewParams params, DomElement embeddedView, int viewId) {
final MutatorsStack mutators = params.mutators;
html.Element head = embeddedView;
DomElement head = embeddedView;
Matrix4 headTransform = params.offset == ui.Offset.zero
? Matrix4.identity()
: Matrix4.translationValues(params.offset.dx, params.offset.dy, 0);
@ -329,7 +330,7 @@ class HtmlViewEmbedder {
case MutatorType.clipRect:
case MutatorType.clipRRect:
case MutatorType.clipPath:
final html.Element clipView = head.parent!;
final DomElement clipView = head.parentElement!;
clipView.style.clip = '';
clipView.style.clipPath = '';
headTransform = Matrix4.identity();
@ -397,14 +398,15 @@ class HtmlViewEmbedder {
final Matrix4 scaleMatrix =
Matrix4.diagonal3Values(inverseScale, inverseScale, 1);
headTransform = scaleMatrix.multiplied(headTransform);
head.style.transform = float64ListToCssTransform(headTransform.storage);
head.style.transform =
float64ListToCssTransform(headTransform.storage);
}
/// Sets the transform origin to the top-left corner of the element.
///
/// By default, the transform origin is the center of the element, but
/// Flutter assumes the transform origin is the top-left point.
void _resetAnchor(html.Element element) {
void _resetAnchor(DomElement element) {
element.style.transformOrigin = '0 0 0';
element.style.position = 'absolute';
}
@ -424,7 +426,7 @@ class HtmlViewEmbedder {
}
_svgPathDefs = kSvgResourceHeader.clone(false) as svg.SvgSvgElement;
_svgPathDefs!.append(svg.DefsElement()..id = 'sk_path_defs');
skiaSceneHost!.append(_svgPathDefs!);
skiaSceneHost!.append(_svgPathDefs! as DomElement);
}
void submitFrame() {
@ -489,7 +491,7 @@ class HtmlViewEmbedder {
_activeCompositionOrder.addAll(_compositionOrder);
unusedViews.removeAll(_compositionOrder);
html.Element? elementToInsertBefore;
DomElement? elementToInsertBefore;
if (diffResult.addToBeginning) {
elementToInsertBefore =
_viewClipChains[diffResult.viewToInsertBefore!]!.root;
@ -504,27 +506,26 @@ class HtmlViewEmbedder {
}
}
if (diffResult.addToBeginning) {
final html.Element platformViewRoot = _viewClipChains[viewId]!.root;
final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
skiaSceneHost!.insertBefore(platformViewRoot, elementToInsertBefore);
final Surface? overlay = _overlays[viewId];
if (overlay != null) {
skiaSceneHost!
.insertBefore(overlay.htmlElement as html.Element, elementToInsertBefore);
.insertBefore(overlay.htmlElement, elementToInsertBefore);
}
} else {
final html.Element platformViewRoot = _viewClipChains[viewId]!.root;
final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
skiaSceneHost!.append(platformViewRoot);
final Surface? overlay = _overlays[viewId];
if (overlay != null) {
skiaSceneHost!.append(overlay.htmlElement as html.Element);
skiaSceneHost!.append(overlay.htmlElement);
}
}
}
insertBeforeMap?.forEach((int viewId, int viewIdToInsertBefore) {
final html.Element overlay = _overlays[viewId]!.htmlElement as
html.Element;
final DomElement overlay = _overlays[viewId]!.htmlElement;
if (viewIdToInsertBefore != -1) {
final html.Element nextSibling =
final DomElement nextSibling =
_viewClipChains[viewIdToInsertBefore]!.root;
skiaSceneHost!.insertBefore(overlay, nextSibling);
} else {
@ -533,8 +534,7 @@ class HtmlViewEmbedder {
});
if (_didPaintBackupSurface) {
skiaSceneHost!
.append(SurfaceFactory.instance.backupSurface.htmlElement as
html.Element);
.append(SurfaceFactory.instance.backupSurface.htmlElement);
}
} else {
SurfaceFactory.instance.removeSurfacesFromDom();
@ -549,19 +549,18 @@ class HtmlViewEmbedder {
}
}
final html.Element platformViewRoot = _viewClipChains[viewId]!.root;
final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
final Surface? overlay = _overlays[viewId];
skiaSceneHost!.append(platformViewRoot);
if (overlay != null) {
skiaSceneHost!.append(overlay.htmlElement as html.Element);
skiaSceneHost!.append(overlay.htmlElement);
}
_activeCompositionOrder.add(viewId);
unusedViews.remove(viewId);
}
if (_didPaintBackupSurface) {
skiaSceneHost!
.append(SurfaceFactory.instance.backupSurface.htmlElement as
html.Element);
.append(SurfaceFactory.instance.backupSurface.htmlElement);
}
}
@ -770,19 +769,19 @@ class HtmlViewEmbedder {
/// * The slot view in the stack (the actual contents of the platform view).
/// * The number of clipping elements used last time the view was composited.
class ViewClipChain {
html.Element _root;
html.Element _slot;
DomElement _root;
DomElement _slot;
int _clipCount = -1;
ViewClipChain({required html.Element view})
ViewClipChain({required DomElement view})
: _root = view,
_slot = view;
html.Element get root => _root;
html.Element get slot => _slot;
DomElement get root => _root;
DomElement get slot => _slot;
int get clipCount => _clipCount;
void updateClipChain({required html.Element root, required int clipCount}) {
void updateClipChain({required DomElement root, required int clipCount}) {
_root = root;
_clipCount = clipCount;
}

View File

@ -4,7 +4,6 @@
library canvaskit_initialization;
import 'dart:async';
import 'dart:html' as html;
import '../../engine.dart' show kProfileMode;
import '../browser_detection.dart';
@ -119,4 +118,4 @@ void ensureSkiaFontCollectionInitialized() {
}
/// The scene host, where the root canvas and overlay canvases are added to.
html.Element? skiaSceneHost;
DomElement? skiaSceneHost;

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:html' as html;
import 'package:ui/ui.dart' as ui;
import '../browser_detection.dart';
@ -133,7 +131,7 @@ class Surface {
void addToScene() {
if (!_addedToScene) {
skiaSceneHost!.children.insert(0, htmlElement as html.Element);
skiaSceneHost!.prepend(htmlElement);
}
_addedToScene = true;
}
@ -210,8 +208,8 @@ class Surface {
final double logicalWidth = _pixelWidth / window.devicePixelRatio;
final double logicalHeight = _pixelHeight / window.devicePixelRatio;
final DomCSSStyleDeclaration style = htmlCanvas!.style;
style.setProperty('width', '${logicalWidth}px');
style.setProperty('height', '${logicalHeight}px');
style.width = '${logicalWidth}px';
style.height = '${logicalHeight}px';
}
/// Translate the canvas so the surface covers the visible portion of the
@ -226,7 +224,7 @@ class Surface {
final int surfaceHeight = _currentSurfaceSize!.height.ceil();
final double offset =
(_pixelHeight - surfaceHeight) / window.devicePixelRatio;
htmlCanvas!.style.setProperty('transform', 'translate(0, -${offset}px)');
htmlCanvas!.style.transform = 'translate(0, -${offset}px)';
}
void _contextRestoredListener(DomEvent event) {
@ -296,7 +294,7 @@ class Surface {
// accessible.
htmlCanvas.setAttribute('aria-hidden', 'true');
htmlCanvas.style.setProperty('position', 'absolute');
htmlCanvas.style.position = 'absolute';
_updateLogicalHtmlCanvasSize();
// When the browser tab using WebGL goes dormant the browser and/or OS may

View File

@ -112,6 +112,7 @@ extension DomElementExtension on DomElement {
external /* List<DomElement> */ List<Object?> get children;
external DomCSSStyleDeclaration get style;
external void append(DomNode node);
external void prepend(DomNode node);
external void setAttribute(String name, Object value);
}
@ -120,6 +121,23 @@ 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 transformOrigin(String value) => setProperty('transform-origin', value);
set opacity(String value) => setProperty('opacity', value);
String get width => getPropertyValue('width');
String get height => getPropertyValue('height');
String get position => getPropertyValue('position');
String get clip => getPropertyValue('clip');
String get clipPath => getPropertyValue('clip-path');
String get transform => getPropertyValue('transform');
String get transformOrigin => getPropertyValue('transform-origin');
String get opacity => getPropertyValue('opacity');
external String getPropertyValue(String property);
external void setProperty(String propertyName, String value, [String
priority]);

View File

@ -11,6 +11,7 @@ import '../engine.dart' show buildMode, registerHotRestartListener;
import 'browser_detection.dart';
import 'canvaskit/initialization.dart';
import 'configuration.dart';
import 'dom.dart';
import 'host_node.dart';
import 'keyboard_binding.dart';
import 'platform_dispatcher.dart';
@ -295,8 +296,8 @@ class FlutterViewEmbedder {
/// added eagerly during initialization here and never touched, unless the
/// system is reset due to hot restart or in a test.
if (useCanvasKit) {
skiaSceneHost = html.Element.tag('flt-scene');
addSceneToSceneHost(skiaSceneHost);
skiaSceneHost = createDomElement('flt-scene');
addSceneToSceneHost(skiaSceneHost as html.Element?);
}
final html.Element semanticsHostElement =

View File

@ -29,8 +29,8 @@ void testMain() {
// Expect exact requested dimensions.
expect(original.width, 9);
expect(original.height, 19);
expect(original.style.getPropertyValue('width'), '9px');
expect(original.style.getPropertyValue('height'), '19px');
expect(original.style.width, '9px');
expect(original.style.height, '19px');
expect(originalSurface.width(), 9);
expect(originalSurface.height(), 19);
@ -39,8 +39,8 @@ void testMain() {
surface.acquireFrame(const ui.Size(5, 15)).skiaSurface;
final DomCanvasElement shrunk = surface.htmlCanvas!;
expect(shrunk, same(original));
expect(shrunk.style.getPropertyValue('width'), '9px');
expect(shrunk.style.getPropertyValue('height'), '19px');
expect(shrunk.style.width, '9px');
expect(shrunk.style.height, '19px');
expect(shrunkSurface, isNot(same(original)));
expect(shrunkSurface.width(), 5);
expect(shrunkSurface.height(), 15);
@ -56,8 +56,8 @@ void testMain() {
// Expect overallocated dimensions
expect(firstIncrease.width, 14);
expect(firstIncrease.height, 28);
expect(firstIncrease.style.getPropertyValue('width'), '14px');
expect(firstIncrease.style.getPropertyValue('height'), '28px');
expect(firstIncrease.style.width, '14px');
expect(firstIncrease.style.height, '28px');
expect(firstIncreaseSurface.width(), 10);
expect(firstIncreaseSurface.height(), 20);
@ -79,8 +79,8 @@ void testMain() {
// Also over-allocated
expect(huge.width, 28);
expect(huge.height, 56);
expect(huge.style.getPropertyValue('width'), '28px');
expect(huge.style.getPropertyValue('height'), '56px');
expect(huge.style.width, '28px');
expect(huge.style.height, '56px');
expect(hugeSurface.width(), 20);
expect(hugeSurface.height(), 40);
@ -154,8 +154,8 @@ void testMain() {
expect(original.width(), 10);
expect(original.height(), 16);
expect(surface.htmlCanvas!.style.getPropertyValue('width'), '10px');
expect(surface.htmlCanvas!.style.getPropertyValue('height'), '16px');
expect(surface.htmlCanvas!.style.width, '10px');
expect(surface.htmlCanvas!.style.height, '16px');
// Increase device-pixel ratio: this makes CSS pixels bigger, so we need
// fewer of them to cover the browser window.
@ -164,8 +164,8 @@ void testMain() {
surface.acquireFrame(const ui.Size(10, 16)).skiaSurface;
expect(highDpr.width(), 10);
expect(highDpr.height(), 16);
expect(surface.htmlCanvas!.style.getPropertyValue('width'), '5px');
expect(surface.htmlCanvas!.style.getPropertyValue('height'), '8px');
expect(surface.htmlCanvas!.style.width, '5px');
expect(surface.htmlCanvas!.style.height, '8px');
// Decrease device-pixel ratio: this makes CSS pixels smaller, so we need
// more of them to cover the browser window.
@ -174,8 +174,8 @@ void testMain() {
surface.acquireFrame(const ui.Size(10, 16)).skiaSurface;
expect(lowDpr.width(), 10);
expect(lowDpr.height(), 16);
expect(surface.htmlCanvas!.style.getPropertyValue('width'), '20px');
expect(surface.htmlCanvas!.style.getPropertyValue('height'), '32px');
expect(surface.htmlCanvas!.style.width, '20px');
expect(surface.htmlCanvas!.style.height, '32px');
});
}, skip: isIosSafari);
}