From b080bdf56fe70df7905a69ad1c4626b1d028b049 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Tue, 15 Dec 2020 16:42:40 -0800 Subject: [PATCH] Prevent recycling of canvas multiple times (#23089) --- lib/web_ui/lib/src/engine/html/picture.dart | 4 ++ .../engine/surface/scene_builder_test.dart | 40 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/web_ui/lib/src/engine/html/picture.dart b/lib/web_ui/lib/src/engine/html/picture.dart index fc40beeb75..4bdff64df2 100644 --- a/lib/web_ui/lib/src/engine/html/picture.dart +++ b/lib/web_ui/lib/src/engine/html/picture.dart @@ -314,6 +314,10 @@ class PersistedPicture extends PersistedLeafSurface { // painting. This removes all the setup work and scaffolding objects // that won't be useful for anything anyway. _recycleCanvas(oldCanvas); + if (oldSurface != null) { + // Make sure it doesn't get reused/recycled again. + oldSurface._canvas = null; + } if (rootElement != null) { domRenderer.clearDom(rootElement!); } diff --git a/lib/web_ui/test/engine/surface/scene_builder_test.dart b/lib/web_ui/test/engine/surface/scene_builder_test.dart index 9cb28a174a..e4e88a7050 100644 --- a/lib/web_ui/test/engine/surface/scene_builder_test.dart +++ b/lib/web_ui/test/engine/surface/scene_builder_test.dart @@ -588,6 +588,40 @@ void testMain() { expect(canvas2.width > unscaledWidth, true); expect(canvas2.height > unscaledHeight, true); }); + + test('Should recycle canvas once', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture picture1 = _drawPicture(); + EngineLayer oldLayer = builder.pushClipRect( + const Rect.fromLTRB(10, 10, 300, 300), + ); + builder.addPicture(Offset.zero, picture1); + builder.pop(); + builder.build(); + + // Force update to scene which will utilize reuse code path. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + EngineLayer oldLayer2 = builder2.pushClipRect( + const Rect.fromLTRB(5, 10, 300, 300), + oldLayer: oldLayer + ); + builder2.addPicture(Offset.zero, _drawEmptyPicture()); + builder2.pop(); + + html.HtmlElement contentAfterReuse = builder2.build().webOnlyRootElement; + expect(contentAfterReuse, isNotNull); + + final SurfaceSceneBuilder builder3 = SurfaceSceneBuilder(); + builder3.pushClipRect( + const Rect.fromLTRB(25, 10, 300, 300), + oldLayer: oldLayer2 + ); + builder3.addPicture(Offset.zero, _drawEmptyPicture()); + builder3.pop(); + // This build will crash if canvas gets recycled twice. + html.HtmlElement contentAfterReuse2 = builder3.build().webOnlyRootElement; + expect(contentAfterReuse2, isNotNull); + }); } typedef TestLayerBuilder = EngineLayer Function( @@ -761,6 +795,12 @@ Picture _drawPicture() { return recorder.endRecording(); } +Picture _drawEmptyPicture() { + final EnginePictureRecorder recorder = PictureRecorder(); + recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); + return recorder.endRecording(); +} + Picture _drawPathImagePath() { const double offsetX = 50; const double offsetY = 50; -- GitLab