From 1a6c8c8362a0f8f3939e08734cacd3196d8b2799 Mon Sep 17 00:00:00 2001 From: Yegor Jbanov Date: Wed, 27 Jan 2021 13:53:37 -0800 Subject: [PATCH] [canvaskit] remove the DOM node of unrendered platform view --- .../src/engine/canvaskit/embedded_views.dart | 24 ++++-- .../test/canvaskit/embedded_views_test.dart | 82 ++++++++++++++++++- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart index 90b047214086f..aa9055f9318ac 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart @@ -359,14 +359,17 @@ class HtmlViewEmbedder { final Set unusedViews = Set.from(_activeCompositionOrder); _activeCompositionOrder.clear(); + List? debugInvalidViewIds; for (int i = 0; i < _compositionOrder.length; i++) { int viewId = _compositionOrder[i]; - assert( - _views.containsKey(viewId), - 'Cannot render platform view $viewId. ' - 'It has not been created, or it has been deleted.', - ); + if (assertionsEnabled) { + if (!_views.containsKey(viewId)) { + debugInvalidViewIds ??= []; + debugInvalidViewIds.add(viewId); + continue; + } + } unusedViews.remove(viewId); html.Element platformViewRoot = _rootViews[viewId]!; @@ -381,6 +384,16 @@ class HtmlViewEmbedder { for (final int unusedViewId in unusedViews) { _releaseOverlay(unusedViewId); + _rootViews[unusedViewId]?.remove(); + } + + if (assertionsEnabled) { + if (debugInvalidViewIds != null && debugInvalidViewIds.isNotEmpty) { + throw AssertionError( + 'Cannot render platform views: ${debugInvalidViewIds.join(', ')}. ' + 'These views have not been created, or they have been deleted.', + ); + } } } @@ -476,6 +489,7 @@ class OverlayCache { for (final Surface overlay in _cache) { overlay.dispose(); } + _cache.clear(); } } diff --git a/lib/web_ui/test/canvaskit/embedded_views_test.dart b/lib/web_ui/test/canvaskit/embedded_views_test.dart index a1dc4f99f1695..4788d61c5cead 100644 --- a/lib/web_ui/test/canvaskit/embedded_views_test.dart +++ b/lib/web_ui/test/canvaskit/embedded_views_test.dart @@ -287,9 +287,79 @@ void testMain() { } on AssertionError catch (error) { expect( error.toString(), - 'Assertion failed: "Cannot render platform view 0. It has not been created, or it has been deleted."', + 'Assertion failed: "Cannot render platform views: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. These views have not been created, or they have been deleted."', ); } + + // Frame 7: + // Render: a platform view after error. + // Expect: success. Just checking the system is not left in a corrupted state. + await _createPlatformView(0, 'test-platform-view'); + renderTestScene(viewCount: 0); + }); + + test('embeds and disposes of a platform view', () async { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelectorAll('#view-0'), + hasLength(1), + ); + + await _disposePlatformView(0); + + sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelectorAll('#view-0'), + hasLength(0), + ); + }); + + test('removed the DOM node of an unrendered platform view', () async { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelectorAll('#view-0'), + hasLength(1), + ); + + // Render a frame without a platform view, but also without disposing of + // the platform view. + sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelectorAll('#view-0'), + hasLength(0), + ); }); // TODO: https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); @@ -311,3 +381,13 @@ Future _createPlatformView(int id, String viewType) { ); return completer.future; } + +Future _disposePlatformView(int id) { + final completer = Completer(); + window.sendPlatformMessage( + 'flutter/platform_views', + codec.encodeMethodCall(MethodCall('dispose', id)), + (dynamic _) => completer.complete(), + ); + return completer.future; +}