Skip to content

Commit

Permalink
[web] Migrate Flutter Web DOM usage to JS static interop - 22.
Browse files Browse the repository at this point in the history
  • Loading branch information
joshualitt committed May 26, 2022
1 parent 5196415 commit 2fe2198
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 70 deletions.
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
5 changes: 3 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,8 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
case 'flutter/platform_views':
_platformViewMessageHandler ??= PlatformViewMessageHandler(
contentManager: platformViewManager,
contentHandler: (html.Element content) {
flutterViewEmbedder.glassPaneElement!.append(content);
contentHandler: (DomElement content) {
flutterViewEmbedder.glassPaneElement!.append(content as html.Element);
},
);
_platformViewMessageHandler!.handlePlatformViewCall(data, callback!);
Expand Down
25 changes: 13 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,10 @@ 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);
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 +173,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

0 comments on commit 2fe2198

Please sign in to comment.