From b71f4059cecde869b6ba1793ffcbaf51021ab8b0 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Mon, 15 May 2023 16:21:57 -0400 Subject: [PATCH 1/3] [web] Support platform view creation params --- .../platform_views/content_manager.dart | 26 +++----- .../platform_views/message_handler.dart | 4 +- .../src/ui_web/platform_view_registry.dart | 23 +++++-- .../platform_views/message_handler_test.dart | 63 ++++++++++++++++++- 4 files changed, 89 insertions(+), 27 deletions(-) diff --git a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart index 28aeb8da0855e..cd715acefd32f 100644 --- a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart +++ b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart @@ -2,25 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; + 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 = 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 = DomElement Function(int viewId); - /// This class handles the lifecycle of Platform Views in the DOM of a Flutter Web App. /// /// There are three important parts of Platform Views. This class manages two of @@ -68,8 +57,8 @@ class PlatformViewManager { /// `factoryFunction` needs to be a [PlatformViewFactory]. bool registerFactory(String viewType, Function factoryFunction, {bool isVisible = true}) { - assert(factoryFunction is PlatformViewFactory || - factoryFunction is ParameterizedPlatformViewFactory); + assert(factoryFunction is ui_web.PlatformViewFactory || + factoryFunction is ui_web.ParameterizedPlatformViewFactory); if (_factories.containsKey(viewType)) { return false; @@ -121,10 +110,11 @@ class PlatformViewManager { final Function factoryFunction = _factories[viewType]!; late DomElement content; - if (factoryFunction is ParameterizedPlatformViewFactory) { - content = factoryFunction(viewId, params: params); + if (factoryFunction is ui_web.ParameterizedPlatformViewFactory) { + content = factoryFunction(viewId, params: params) as DomElement; } else { - content = (factoryFunction as PlatformViewFactory).call(viewId); + factoryFunction as ui_web.PlatformViewFactory; + content = factoryFunction(viewId) as DomElement; } _ensureContentCorrectlySized(content, viewType); diff --git a/lib/web_ui/lib/src/engine/platform_views/message_handler.dart b/lib/web_ui/lib/src/engine/platform_views/message_handler.dart index 30658e57f6c91..b6a5f4533c366 100644 --- a/lib/web_ui/lib/src/engine/platform_views/message_handler.dart +++ b/lib/web_ui/lib/src/engine/platform_views/message_handler.dart @@ -68,6 +68,7 @@ class PlatformViewMessageHandler { final Map args = methodCall.arguments as Map; final int viewId = args.readInt('id'); final String viewType = args.readString('viewType'); + final Object? params = args['params']; if (!_contentManager.knowsViewType(viewType)) { callback(_codec.encodeErrorEnvelope( @@ -89,11 +90,10 @@ class PlatformViewMessageHandler { return; } - // TODO(hterkelsen): How can users add extra `args` from the HtmlElementView widget? final DomElement content = _contentManager.renderContent( viewType, viewId, - args, + params, ); // For now, we don't need anything fancier. If needed, this can be converted diff --git a/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart b/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart index 1d4e77d23d588..f4ba450be3080 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart @@ -4,21 +4,32 @@ import 'package:ui/src/engine.dart'; +/// A function which takes a unique `id` and some `params` and creates an HTML +/// element. +typedef ParameterizedPlatformViewFactory = Object Function( + int viewId, { + Object? params, +}); + +/// A function which takes a unique `id` and creates an HTML element. +typedef PlatformViewFactory = Object Function(int viewId); + /// The platform view registry for this app. final PlatformViewRegistry platformViewRegistry = PlatformViewRegistry(); /// A registry for factories that create platform views. class PlatformViewRegistry { - /// Register [viewTypeId] as being creating by the given [viewFactory]. - /// [viewFactory] can be any function that takes an integer and returns an - /// `HTMLElement` DOM object. + /// Register [viewType] as being created by the given [viewFactory]. + /// + /// [viewFactory] can be any function that takes an integer and optional + /// `params` and returns an `HTMLElement` DOM object. bool registerViewFactory( - String viewTypeId, - Object Function(int viewId) viewFactory, { + String viewType, + Function viewFactory, { bool isVisible = true, }) { return platformViewManager.registerFactory( - viewTypeId, + viewType, viewFactory, isVisible: isVisible, ); diff --git a/lib/web_ui/test/engine/platform_views/message_handler_test.dart b/lib/web_ui/test/engine/platform_views/message_handler_test.dart index 429779ac09a34..a5782a841387e 100644 --- a/lib/web_ui/test/engine/platform_views/message_handler_test.dart +++ b/lib/web_ui/test/engine/platform_views/message_handler_test.dart @@ -15,6 +15,8 @@ void main() { const MethodCodec codec = StandardMethodCodec(); +typedef PlatformViewFactoryCall = ({int viewId, Object? params}); + void testMain() { group('PlatformViewMessageHandler', () { group('handlePlatformViewCall', () { @@ -109,6 +111,64 @@ void testMain() { reason: 'The response should be a success envelope, with null in it.'); }); + + test('passes creation params to the factory', () async { + final List factoryCalls = []; + contentManager.registerFactory(viewType, (int viewId, {Object? params}) { + factoryCalls.add((viewId: viewId, params: params)); + return createDomHTMLDivElement(); + }); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + contentManager: contentManager, + ); + + final List> completers = >[]; + + completers.add(Completer()); + messageHandler.handlePlatformViewCall( + _getCreateMessage(viewType, 111), + completers.last.complete, + ); + + completers.add(Completer()); + messageHandler.handlePlatformViewCall( + _getCreateMessage(viewType, 222, {'foo': 'bar'}), + completers.last.complete, + ); + + final List responses = await Future.wait( + completers.map((Completer c) => c.future), + ); + + for (final ByteData? response in responses) { + expect( + codec.decodeEnvelope(response!), + isNull, + reason: 'The response should be a success envelope, with null in it.', + ); + } + + expect(factoryCalls, hasLength(2)); + expect(factoryCalls[0].viewId, 111); + expect(factoryCalls[0].params, isNull); + expect(factoryCalls[1].viewId, 222); + expect(factoryCalls[1].params, {'foo': 'bar'}); + }); + + test('fails if the factory returns a non-DOM object', () async { + contentManager.registerFactory(viewType, (int viewId) { + // Return an object that's not a DOM element. + return Object(); + }); + + final PlatformViewMessageHandler messageHandler = + PlatformViewMessageHandler(contentManager: contentManager); + final ByteData? message = _getCreateMessage(viewType, viewId); + + expect(() { + messageHandler.handlePlatformViewCall(message, (_) {}); + }, throwsA(isA())); + }); }); group('"dispose" message', () { @@ -162,12 +222,13 @@ class _FakePlatformViewManager extends PlatformViewManager { } } -ByteData? _getCreateMessage(String viewType, int viewId) { +ByteData? _getCreateMessage(String viewType, int viewId, [Object? params]) { return codec.encodeMethodCall(MethodCall( 'create', { 'id': viewId, 'viewType': viewType, + if (params != null) 'params': params, }, )); } From c325e82594dbc7cadb556c7550863d13bd13d6a9 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 2 Jun 2023 16:05:54 -0400 Subject: [PATCH 2/3] add tests for other types --- .../platform_views/message_handler_test.dart | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/test/engine/platform_views/message_handler_test.dart b/lib/web_ui/test/engine/platform_views/message_handler_test.dart index a5782a841387e..2f30e7c5e13f4 100644 --- a/lib/web_ui/test/engine/platform_views/message_handler_test.dart +++ b/lib/web_ui/test/engine/platform_views/message_handler_test.dart @@ -136,6 +136,18 @@ void testMain() { completers.last.complete, ); + completers.add(Completer()); + messageHandler.handlePlatformViewCall( + _getCreateMessage(viewType, 333, 'foobar'), + completers.last.complete, + ); + + completers.add(Completer()); + messageHandler.handlePlatformViewCall( + _getCreateMessage(viewType, 444, [1, null, 'str']), + completers.last.complete, + ); + final List responses = await Future.wait( completers.map((Completer c) => c.future), ); @@ -148,11 +160,15 @@ void testMain() { ); } - expect(factoryCalls, hasLength(2)); + expect(factoryCalls, hasLength(4)); expect(factoryCalls[0].viewId, 111); expect(factoryCalls[0].params, isNull); expect(factoryCalls[1].viewId, 222); expect(factoryCalls[1].params, {'foo': 'bar'}); + expect(factoryCalls[2].viewId, 333); + expect(factoryCalls[2].params, 'foobar'); + expect(factoryCalls[3].viewId, 444); + expect(factoryCalls[3].params, [1, null, 'str']); }); test('fails if the factory returns a non-DOM object', () async { From 472ccf666c7c1502361c9f8c99694c4e5117fb37 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 2 Jun 2023 16:13:57 -0400 Subject: [PATCH 3/3] move the assert message to this PR --- .../lib/src/engine/platform_views/content_manager.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart index cd715acefd32f..2f9326ecf3871 100644 --- a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart +++ b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart @@ -57,8 +57,13 @@ class PlatformViewManager { /// `factoryFunction` needs to be a [PlatformViewFactory]. bool registerFactory(String viewType, Function factoryFunction, {bool isVisible = true}) { - assert(factoryFunction is ui_web.PlatformViewFactory || - factoryFunction is ui_web.ParameterizedPlatformViewFactory); + assert( + factoryFunction is ui_web.PlatformViewFactory || + factoryFunction is ui_web.ParameterizedPlatformViewFactory, + 'Factory signature is invalid. Expected either ' + '{${ui_web.PlatformViewFactory}} or {${ui_web.ParameterizedPlatformViewFactory}} ' + 'but got: {${factoryFunction.runtimeType}}', + ); if (_factories.containsKey(viewType)) { return false;