From 5c4064ee6a4047d859c977c325d2c95a62e004aa Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 5 May 2023 16:25:32 -0400 Subject: [PATCH 1/3] [web] New platform view API to get view by ID --- lib/web_ui/lib/src/engine/dom.dart | 2 + .../platform_views/content_manager.dart | 22 +++++-- .../src/ui_web/platform_view_registry.dart | 7 +++ .../platform_views/content_manager_test.dart | 58 +++++++++++++++++++ 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index 45dc726999479..91a626732e4ff 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart @@ -520,6 +520,8 @@ extension DomElementExtension on DomElement { Iterable get children => createDomListWrapper(_children); + external DomElement? get firstElementChild; + @JS('clientHeight') external JSNumber get _clientHeight; double get clientHeight => _clientHeight.toDart; 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..0218ba6c92639 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 @@ -60,6 +60,14 @@ class PlatformViewManager { return _contents.containsKey(viewId); } + /// Returns the HTML element created by a registered factory for [viewId]. + /// + /// Throws an [AssertionError] if [viewId] hasn't been rendered before. + DomElement getViewById(int viewId) { + assert(knowsViewId(viewId), 'No view has been rendered for viewId: $viewId'); + return _contents[viewId]!.firstElementChild!; + } + /// Registers a `factoryFunction` that knows how to render a Platform View of `viewType`. /// /// `viewType` is selected by the programmer, but it can't be overridden once @@ -68,8 +76,13 @@ 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 PlatformViewFactory || + factoryFunction is ParameterizedPlatformViewFactory, + 'Factory signature is invalid. Expected either ' + '{$PlatformViewFactory} or {$ParameterizedPlatformViewFactory} ' + 'but got: {${factoryFunction.runtimeType}}', + ); if (_factories.containsKey(viewType)) { return false; @@ -119,12 +132,13 @@ class PlatformViewManager { ..setAttribute('slot', slotName); final Function factoryFunction = _factories[viewType]!; - late DomElement content; + final DomElement content; if (factoryFunction is ParameterizedPlatformViewFactory) { content = factoryFunction(viewId, params: params); } else { - content = (factoryFunction as PlatformViewFactory).call(viewId); + factoryFunction as PlatformViewFactory; + content = factoryFunction(viewId); } _ensureContentCorrectlySized(content, viewType); 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..45bb823b23597 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 @@ -23,4 +23,11 @@ class PlatformViewRegistry { isVisible: isVisible, ); } + + /// Returns the view previously created for [viewId]. + /// + /// Throws if no view has been created for [viewId]. + Object getViewById(int viewId) { + return platformViewManager.getViewById(viewId); + } } diff --git a/lib/web_ui/test/engine/platform_views/content_manager_test.dart b/lib/web_ui/test/engine/platform_views/content_manager_test.dart index 354471a0459aa..0502984a61b3f 100644 --- a/lib/web_ui/test/engine/platform_views/content_manager_test.dart +++ b/lib/web_ui/test/engine/platform_views/content_manager_test.dart @@ -145,5 +145,63 @@ void testMain() { expect(firstRender, same(anotherRender)); }); }); + + group('getViewById', () { + test('finds created views', () async { + final Map views1 = { + 1: createDomHTMLDivElement(), + 2: createDomHTMLDivElement(), + 5: createDomHTMLDivElement(), + }; + final Map views2 = { + 3: createDomHTMLDivElement(), + 4: createDomHTMLDivElement(), + }; + + contentManager.registerFactory('forTest1', (int id) => views1[id]!); + contentManager.registerFactory('forTest2', (int id) => views2[id]!); + + // Render all 5 views. + for (final int id in views1.keys) { + contentManager.renderContent('forTest1', id, null); + } + for (final int id in views2.keys) { + contentManager.renderContent('forTest2', id, null); + } + + // Check all 5 views. + for (final int id in views1.keys) { + expect(contentManager.getViewById(id), views1[id]); + } + for (final int id in views2.keys) { + expect(contentManager.getViewById(id), views2[id]); + } + + // Throws for unknown viewId. + expect(() { + contentManager.getViewById(99); + }, throwsA(isA())); + }); + + test('throws if view has been cleared', () { + final DomHTMLDivElement view = createDomHTMLDivElement(); + contentManager.registerFactory(viewType, (int id) => view); + + // Throws before viewId is rendered. + expect(() { + contentManager.getViewById(viewId); + }, throwsA(isA())); + + contentManager.renderContent(viewType, viewId, null); + // Succeeds after viewId is rendered. + expect(contentManager.getViewById(viewId), view); + + contentManager.clearPlatformView(viewId); + // Throws after viewId is cleared. + expect(() { + contentManager.getViewById(viewId); + }, throwsA(isA())); + }); + }); }); } From 7559919e7b5e078653967af501dbc6360bb75d53 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 2 Jun 2023 16:16:38 -0400 Subject: [PATCH 2/3] revert assert message --- .../lib/src/engine/platform_views/content_manager.dart | 9 ++------- 1 file changed, 2 insertions(+), 7 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 0218ba6c92639..e4eb8544200a2 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 @@ -76,13 +76,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, - 'Factory signature is invalid. Expected either ' - '{$PlatformViewFactory} or {$ParameterizedPlatformViewFactory} ' - 'but got: {${factoryFunction.runtimeType}}', - ); + assert(factoryFunction is PlatformViewFactory || + factoryFunction is ParameterizedPlatformViewFactory); if (_factories.containsKey(viewType)) { return false; From e7b6f0731f63263884ca4a8a64c797f6620a61f6 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 2 Jun 2023 16:19:47 -0400 Subject: [PATCH 3/3] document reason for first child --- lib/web_ui/lib/src/engine/platform_views/content_manager.dart | 3 +++ 1 file changed, 3 insertions(+) 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 e4eb8544200a2..b6ef99a39d965 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 @@ -65,6 +65,9 @@ class PlatformViewManager { /// Throws an [AssertionError] if [viewId] hasn't been rendered before. DomElement getViewById(int viewId) { assert(knowsViewId(viewId), 'No view has been rendered for viewId: $viewId'); + // `_contents[viewId]` is the element created by us. The + // first (and only) child of that is the element created by the user-supplied + // factory function. return _contents[viewId]!.firstElementChild!; }