diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index da7f156bafe95..9520e37ede0f8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -271,6 +271,11 @@ - (instancetype)initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale { return self; } +- (void)reset { + paths_.clear(); + [self setNeedsDisplay]; +} + // In some scenarios, when we add this view as a maskView of the ChildClippingView, iOS added // this view as a subview of the ChildClippingView. // This results this view blocking touch events on the ChildClippingView. diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 5294a9b254c98..cb6fa4b3e23a1 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; }; 6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; }; 6816DBA42318358200A51400 /* GoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* GoldenTestManager.m */; }; + 682C5873299C182A00915E5C /* golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 682C5872299C182A00915E5C /* golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png */; }; 685B9F392977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 685B9F372977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png */; }; 685B9F3A2977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 685B9F382977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png */; }; 687AF8E9291EBDE0003912C7 /* golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 687AF8E8291EBDE0003912C7 /* golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png */; }; @@ -153,6 +154,7 @@ 6816DBA02317573300A51400 /* GoldenImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenImage.m; sourceTree = ""; }; 6816DBA22318358200A51400 /* GoldenTestManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenTestManager.h; sourceTree = ""; }; 6816DBA32318358200A51400 /* GoldenTestManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenTestManager.m; sourceTree = ""; }; + 682C5872299C182A00915E5C /* golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png"; sourceTree = ""; }; 685B9F372977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png"; sourceTree = ""; }; 685B9F382977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png"; sourceTree = ""; }; 687AF8E8291EBDE0003912C7 /* golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png"; sourceTree = ""; }; @@ -300,6 +302,7 @@ F7B464DC2759D02B00079189 /* Goldens */ = { isa = PBXGroup; children = ( + 682C5872299C182A00915E5C /* golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png */, 685B9F382977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png */, 685B9F372977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png */, 68D50041291ED8CD001ACFE1 /* golden_platform_view_cliprect_with_transform_iPhone 8_16.0_simulator.png */, @@ -447,6 +450,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 682C5873299C182A00915E5C /* golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png in Resources */, 68D50042291ED8CD001ACFE1 /* golden_platform_view_cliprect_with_transform_iPhone 8_16.0_simulator.png in Resources */, F7B464F62759D0A900079189 /* golden_non_full_screen_flutter_view_platform_view_iPhone 8_16.0_simulator.png in Resources */, F7B464F02759D0A900079189 /* golden_bogus_font_text_iPhone 8_16.0_simulator.png in Resources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 5118a4e6b4ad8..2711d846bc631 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -66,7 +66,8 @@ - (BOOL)application:(UIApplication*)application @"--bogus-font-text" : @"bogus_font_text", @"--spawn-engine-works" : @"spawn_engine_works", @"--pointer-events" : @"pointer_events", - @"--platform-view-scrolling-under-widget" : @"platform_view_scrolling_under_widget" + @"--platform-view-scrolling-under-widget" : @"platform_view_scrolling_under_widget", + @"--platform-view-cliprect-after-moved" : @"platform_view_cliprect_after_moved" }; __block NSString* flutterViewControllerTestName = nil; [launchArgsMap diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m index 26838a3a47497..6ecb07a7c7c5d 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m @@ -46,6 +46,7 @@ - (instancetype)initWithLaunchArg:(NSString*)launchArg { @"non_full_screen_flutter_view_platform_view", @"--bogus-font-text" : @"bogus_font_text", @"--spawn-engine-works" : @"spawn_engine_works", + @"--platform-view-cliprect-after-moved" : @"platform_view_cliprect_after_moved", }; }); _identifier = launchArgsMap[launchArg]; diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m index a37122b1a44a6..47d49f3c7779c 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m @@ -98,6 +98,36 @@ - (void)testPlatformView { @end +@interface PlatformViewMutationClipRectAfterMovedTests : GoldenPlatformViewTests + +@end + +@implementation PlatformViewMutationClipRectAfterMovedTests + +- (instancetype)initWithInvocation:(NSInvocation*)invocation { + GoldenTestManager* manager = + [[GoldenTestManager alloc] initWithLaunchArg:@"--platform-view-cliprect-after-moved"]; + return [super initWithManager:manager invocation:invocation]; +} + +- (void)testPlatformView { + // This test needs to wait for several frames for the PlatformView to settle to + // the correct position. The PlatformView accessiblity is set to platform_view[10000] when it is + // ready. + XCUIElement* element = self.application.otherElements[@"platform_view[10000]"]; + BOOL exists = [element waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]; + if (!exists) { + XCTFail(@"It took longer than %@ second to find the platform view." + @"There might be issues with the platform view's construction," + @"or with how the scenario is built.", + @(kSecondsToWaitForPlatformView)); + } + + [self checkPlatformViewGolden]; +} + +@end + @interface PlatformViewMutationClipRRectTests : GoldenPlatformViewTests @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000..a48b0da44c6a2 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_after_moved_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 96384f1079bd3..adcb6dd73677b 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -573,6 +573,61 @@ class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenar } } +/// Platform view with clip rect then the PlatformView is moved for 10 frames. +/// +/// The clip rect moves with the same transform matrix with the PlatformView. +class PlatformViewClipRectAfterMovedScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Constructs a platform view with clip rect scenario. + PlatformViewClipRectAfterMovedScenario( + PlatformDispatcher dispatcher, { + required this.id, + }) : super(dispatcher); + + /// The platform view identifier. + final int id; + + int _numberOfFrames = 0; + + double _y = 100.0; + + @override + void onBeginFrame(Duration duration) { + final Matrix4 translateMatrix = Matrix4.identity()..translate(0.0, _y); + final SceneBuilder builder = SceneBuilder() + ..pushTransform(translateMatrix.storage) + ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); + + addPlatformView( + _numberOfFrames == 10? 10000:id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + + // Add a translucent rect that has the same size of PlatformView. + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawRect( + const Rect.fromLTWH(0, 0, 500, 500), + Paint()..color = const Color(0x22FF0000), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(Offset.zero, picture); + + finishBuilder(builder); + super.onBeginFrame(duration); + } + + @override + void onDrawFrame() { + if (_numberOfFrames < 10) { + _numberOfFrames ++; + _y -= 10; + window.scheduleFrame(); + } + super.onDrawFrame(); + } +} + /// Platform view with clip rrect. class PlatformViewClipRRectScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect scenario. diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index 61c9437cb0ae5..94445d4254291 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -32,6 +32,7 @@ Map _scenarios = { 'platform_view_max_overlays': () => PlatformViewMaxOverlaysScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_cliprect': () => PlatformViewClipRectScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_cliprect_with_transform': () => PlatformViewClipRectWithTransformScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_cliprect_after_moved': () => PlatformViewClipRectAfterMovedScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_cliprrect': () => PlatformViewClipRRectScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_large_cliprrect': () => PlatformViewLargeClipRRectScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_cliprrect_with_transform': () => PlatformViewClipRRectWithTransformScenario(PlatformDispatcher.instance, id: _viewId++),