Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[web] Migrate Flutter Web DOM usage to JS static interop - 22. #33350

Merged
merged 1 commit into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions lib/web_ui/lib/src/engine/clipboard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
// 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';
import 'dom.dart';
import 'services.dart';
import 'util.dart';

Expand Down Expand Up @@ -90,7 +89,7 @@ class ClipboardMessageHandler {
/// APIs and the browser.
abstract class CopyToClipboardStrategy {
factory CopyToClipboardStrategy() {
return !unsafeIsNull(html.window.navigator.clipboard)
return !unsafeIsNull(domWindow.navigator.clipboard)
? ClipboardAPICopyStrategy()
: ExecCommandCopyStrategy();
}
Expand All @@ -110,7 +109,7 @@ abstract class CopyToClipboardStrategy {
abstract class PasteFromClipboardStrategy {
factory PasteFromClipboardStrategy() {
return (browserEngine == BrowserEngine.firefox ||
unsafeIsNull(html.window.navigator.clipboard))
unsafeIsNull(domWindow.navigator.clipboard))
? ExecCommandPasteStrategy()
: ClipboardAPIPasteStrategy();
}
Expand All @@ -127,7 +126,7 @@ class ClipboardAPICopyStrategy implements CopyToClipboardStrategy {
@override
Future<bool> setData(String? text) async {
try {
await html.window.navigator.clipboard!.writeText(text!);
await domWindow.navigator.clipboard!.writeText(text!);
} catch (error) {
print('copy is not successful $error');
return Future<bool>.value(false);
Expand All @@ -145,7 +144,7 @@ class ClipboardAPICopyStrategy implements CopyToClipboardStrategy {
class ClipboardAPIPasteStrategy implements PasteFromClipboardStrategy {
@override
Future<String> getData() async {
return html.window.navigator.clipboard!.readText();
return domWindow.navigator.clipboard!.readText();
}
}

Expand All @@ -159,13 +158,13 @@ class ExecCommandCopyStrategy implements CopyToClipboardStrategy {
bool _setDataSync(String? text) {
// Copy content to clipboard with execCommand.
// See: https://developers.google.com/web/updates/2015/04/cut-and-copy-commands
final html.TextAreaElement tempTextArea = _appendTemporaryTextArea();
final DomHTMLTextAreaElement tempTextArea = _appendTemporaryTextArea();
tempTextArea.value = text;
tempTextArea.focus();
tempTextArea.select();
bool result = false;
try {
result = html.document.execCommand('copy');
result = domDocument.execCommand('copy');
if (!result) {
print('copy is not successful');
}
Expand All @@ -177,9 +176,9 @@ class ExecCommandCopyStrategy implements CopyToClipboardStrategy {
return result;
}

html.TextAreaElement _appendTemporaryTextArea() {
final html.TextAreaElement tempElement = html.TextAreaElement();
final html.CssStyleDeclaration elementStyle = tempElement.style;
DomHTMLTextAreaElement _appendTemporaryTextArea() {
final DomHTMLTextAreaElement tempElement = createDomHTMLTextAreaElement();
final DomCSSStyleDeclaration elementStyle = tempElement.style;
elementStyle
..position = 'absolute'
..top = '-99999px'
Expand All @@ -189,12 +188,12 @@ class ExecCommandCopyStrategy implements CopyToClipboardStrategy {
..backgroundColor = 'transparent'
..background = 'transparent';

html.document.body!.append(tempElement);
domDocument.body!.append(tempElement);

return tempElement;
}

void _removeTemporaryTextArea(html.HtmlElement element) {
void _removeTemporaryTextArea(DomHTMLElement element) {
element.remove();
}
}
Expand Down
49 changes: 49 additions & 0 deletions lib/web_ui/lib/src/engine/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extension DomWindowExtension on DomWindow {
external int? get innerHeight;
external int? get innerWidth;
external DomNavigator get navigator;
external DomVisualViewport? get visualViewport;
external DomPerformance get performance;
Future<Object?> fetch(String url) =>
js_util.promiseToFuture(js_util.callMethod(this, 'fetch', <String>[url]));
Expand All @@ -50,6 +51,7 @@ external DomWindow get domWindow;
class DomNavigator {}

extension DomNavigatorExtension on DomNavigator {
external DomClipboard? get clipboard;
external int? get maxTouchPoints;
external String get vendor;
external String get language;
Expand All @@ -62,13 +64,15 @@ extension DomNavigatorExtension on DomNavigator {
class DomDocument {}

extension DomDocumentExtension on DomDocument {
external DomElement? get documentElement;
external DomElement? querySelector(String selectors);
List<DomElement> querySelectorAll(String selectors) =>
js_util.callMethod<List<Object?>>(
this, 'querySelectorAll', <Object>[selectors]).cast<DomElement>();
DomElement createElement(String name, [Object? options]) =>
js_util.callMethod(this, 'createElement',
<Object>[name, if (options != null) options]) as DomElement;
external bool execCommand(String commandId);
external DomHTMLScriptElement? get currentScript;
external DomElement createElementNS(
String namespaceURI, String qualifiedName);
Expand Down Expand Up @@ -167,6 +171,8 @@ DomElement createDomElement(String tag) => domDocument.createElement(tag);
extension DomElementExtension on DomElement {
List<DomElement> get children =>
js_util.getProperty<List<Object?>>(this, 'children').cast<DomElement>();
external int get clientHeight;
external int get clientWidth;
external String get id;
external set id(String id);
external set innerHtml(String? html);
Expand Down Expand Up @@ -254,6 +260,7 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
set flexDirection(String value) => setProperty('flex-direction', value);
set alignItems(String value) => setProperty('align-items', value);
set margin(String value) => setProperty('margin', value);
set background(String value) => setProperty('background', value);
String get width => getPropertyValue('width');
String get height => getPropertyValue('height');
String get position => getPropertyValue('position');
Expand Down Expand Up @@ -308,6 +315,7 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
String get flexDirection => getPropertyValue('flex-direction');
String get alignItems => getPropertyValue('align-items');
String get margin => getPropertyValue('margin');
String get background=> getPropertyValue('background');

external String getPropertyValue(String property);
void setProperty(String propertyName, String value, [String? priority]) {
Expand All @@ -325,6 +333,7 @@ class DomHTMLElement extends DomElement {}

extension DomHTMLElementExtension on DomHTMLElement {
int get offsetWidth => js_util.getProperty<num>(this, 'offsetWidth') as int;
external void focus();
}

@JS()
Expand Down Expand Up @@ -376,6 +385,13 @@ class DomHTMLDivElement extends DomHTMLElement {}
DomHTMLDivElement createDomHTMLDivElement() =>
domDocument.createElement('div') as DomHTMLDivElement;

@JS()
@staticInterop
class DomHTMLSpanElement extends DomHTMLElement {}

DomHTMLSpanElement createDomHTMLSpanElement() =>
domDocument.createElement('span') as DomHTMLSpanElement;

@JS()
@staticInterop
class DomHTMLButtonElement extends DomHTMLElement {}
Expand Down Expand Up @@ -610,6 +626,39 @@ extension DomFontFaceSetExtension on DomFontFaceSet {
external void clear();
}

@JS()
@staticInterop
class DomVisualViewport extends DomEventTarget {}

extension DomVisualViewportExtension on DomVisualViewport {
external num? get height;
external num? get width;
}

@JS()
@staticInterop
class DomHTMLTextAreaElement extends DomHTMLElement {}

DomHTMLTextAreaElement createDomHTMLTextAreaElement() =>
domDocument.createElement('textarea') as DomHTMLTextAreaElement;

extension DomHTMLTextAreaElementExtension on DomHTMLTextAreaElement {
external set value(String? value);
external void select();
}

@JS()
@staticInterop
class DomClipboard extends DomEventTarget {}

extension DomClipboardExtension on DomClipboard {
Future<String> readText() =>
js_util.promiseToFuture<String>(js_util.callMethod(this, 'readText', <Object>[]));

Future<dynamic> writeText(String data) =>
js_util.promiseToFuture(js_util.callMethod(this, 'readText', <Object>[data]));
}

extension DomResponseExtension on DomResponse {
Future<dynamic> arrayBuffer() => js_util
.promiseToFuture(js_util.callMethod(this, 'arrayBuffer', <Object>[]));
Expand Down
6 changes: 4 additions & 2 deletions lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'canvaskit/initialization.dart';
import 'canvaskit/layer_scene_builder.dart';
import 'canvaskit/rasterizer.dart';
import 'clipboard.dart';
import 'dom.dart';
import 'embedder.dart';
import 'html/scene.dart';
import 'mouse_cursor.dart';
Expand Down Expand Up @@ -482,8 +483,9 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
case 'flutter/platform_views':
_platformViewMessageHandler ??= PlatformViewMessageHandler(
contentManager: platformViewManager,
contentHandler: (html.Element content) {
flutterViewEmbedder.glassPaneElement!.append(content);
contentHandler: (DomElement content) {
// Remove cast to [html.Element] after migration.
flutterViewEmbedder.glassPaneElement!.append(content as html.Element);
},
);
_platformViewMessageHandler!.handlePlatformViewCall(data, callback!);
Expand Down
26 changes: 14 additions & 12 deletions lib/web_ui/lib/src/engine/platform_views/content_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@
import 'dart:html' as html;

import '../browser_detection.dart';
import '../dom.dart';
import '../embedder.dart';
import '../util.dart';
import 'slots.dart';

/// A function which takes a unique `id` and some `params` and creates an HTML element.
///
/// This is made available to end-users through dart:ui in web.
typedef ParameterizedPlatformViewFactory = html.Element Function(
typedef ParameterizedPlatformViewFactory = DomElement Function(
int viewId, {
Object? params,
});

/// A function which takes a unique `id` and creates an HTML element.
///
/// This is made available to end-users through dart:ui in web.
typedef PlatformViewFactory = html.Element Function(int viewId);
typedef PlatformViewFactory = DomElement Function(int viewId);

/// This class handles the lifecycle of Platform Views in the DOM of a Flutter Web App.
///
Expand All @@ -29,7 +30,7 @@ typedef PlatformViewFactory = html.Element Function(int viewId);
///
/// * `factories`: The functions used to render the contents of any given Platform
/// View by its `viewType`.
/// * `contents`: The result [html.Element] of calling a `factory` function.
/// * `contents`: The result [DomElement] of calling a `factory` function.
///
/// The third part is `slots`, which are created on demand by the
/// [createPlatformViewSlot] function.
Expand All @@ -41,7 +42,7 @@ class PlatformViewManager {
final Map<String, Function> _factories = <String, Function>{};

// The references to content tags, indexed by their framework-given ID.
final Map<int, html.Element> _contents = <int, html.Element>{};
final Map<int, DomElement> _contents = <int, DomElement>{};

final Set<String> _invisibleViews = <String>{};
final Map<int, String> _viewIdToType = <int, String>{};
Expand Down Expand Up @@ -103,7 +104,7 @@ class PlatformViewManager {
/// a place where to attach the `slot` property, that will tell the browser
/// what `slot` tag will reveal this `contents`, **without modifying the returned
/// html from the `factory` function**.
html.Element renderContent(
DomElement renderContent(
String viewType,
int viewId,
Object? params,
Expand All @@ -115,12 +116,12 @@ class PlatformViewManager {
_viewIdToType[viewId] = viewType;

return _contents.putIfAbsent(viewId, () {
final html.Element wrapper = html.document
final DomElement wrapper = domDocument
.createElement('flt-platform-view')
..setAttribute('slot', slotName);

final Function factoryFunction = _factories[viewType]!;
late html.Element content;
late DomElement content;

if (factoryFunction is ParameterizedPlatformViewFactory) {
content = factoryFunction(viewId, params: params);
Expand All @@ -140,7 +141,7 @@ class PlatformViewManager {
/// never been rendered before.
void clearPlatformView(int viewId) {
// Remove from our cache, and then from the DOM...
final html.Element? element = _contents.remove(viewId);
final DomElement? element = _contents.remove(viewId);
_safelyRemoveSlottedElement(element);
}

Expand All @@ -149,7 +150,7 @@ class PlatformViewManager {
// than its slot (after the slot is removed).
//
// TODO(web): Cleanup https://github.com/flutter/flutter/issues/85816
void _safelyRemoveSlottedElement(html.Element? element) {
void _safelyRemoveSlottedElement(DomElement? element) {
if (element == null) {
return;
}
Expand All @@ -159,10 +160,11 @@ class PlatformViewManager {
}
final String tombstoneName = "tombstone-${element.getAttribute('slot')}";
// Create and inject a new slot in the shadow root
final html.Element slot = html.document.createElement('slot')
final DomElement slot = domDocument.createElement('slot')
..style.display = 'none'
..setAttribute('name', tombstoneName);
flutterViewEmbedder.glassPaneShadow!.append(slot);
// Remove cast to [html.Node] after migration.
flutterViewEmbedder.glassPaneShadow!.append(slot as html.Node);
// Link the element to the new slot
element.setAttribute('slot', tombstoneName);
// Delete both the element, and the new slot
Expand All @@ -172,7 +174,7 @@ class PlatformViewManager {

/// Attempt to ensure that the contents of the user-supplied DOM element will
/// fill the space allocated for this platform view by the framework.
void _ensureContentCorrectlySized(html.Element content, String viewType) {
void _ensureContentCorrectlySized(DomElement content, String viewType) {
// Scrutinize closely any other modifications to `content`.
// We shouldn't modify users' returned `content` if at all possible.
// Note there's also no getContent(viewId) function anymore, to prevent
Expand Down
8 changes: 4 additions & 4 deletions lib/web_ui/lib/src/engine/platform_views/message_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// 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:typed_data';

import '../dom.dart';
import '../services.dart';
import '../util.dart';
import 'content_manager.dart';
Expand All @@ -13,9 +13,9 @@ import 'content_manager.dart';
/// Copied here so there's no circular dependencies.
typedef _PlatformMessageResponseCallback = void Function(ByteData? data);

/// A function that handle a newly created [html.Element] with the contents of a
/// A function that handle a newly created [DomElement] with the contents of a
/// platform view with a unique [int] id.
typedef PlatformViewContentHandler = void Function(html.Element);
typedef PlatformViewContentHandler = void Function(DomElement);

/// This class handles incoming framework messages to create/dispose Platform Views.
///
Expand Down Expand Up @@ -91,7 +91,7 @@ class PlatformViewMessageHandler {
}

// TODO(hterkelsen): How can users add extra `args` from the HtmlElementView widget?
final html.Element content = _contentManager.renderContent(
final DomElement content = _contentManager.renderContent(
viewType,
viewId,
args,
Expand Down
Loading