@@ -69,6 +69,9 @@ - (void)checkViewCreatedOnce {
6969 self.viewCreated = YES ;
7070}
7171
72+ - (void )dealloc {
73+ gMockPlatformView = nil ;
74+ }
7275@end
7376
7477@interface FlutterPlatformViewsTestMockFlutterPlatformFactory
@@ -110,6 +113,10 @@ - (void)checkViewCreatedOnce {
110113 }
111114 self.viewCreated = YES ;
112115}
116+
117+ - (void )dealloc {
118+ gMockPlatformView = nil ;
119+ }
113120@end
114121
115122@interface FlutterPlatformViewsTestMockWebViewFactory : NSObject <FlutterPlatformViewFactory>
@@ -135,6 +142,93 @@ @implementation FlutterPlatformViewsTestNilFlutterPlatformFactory
135142
136143@end
137144
145+ @interface FlutterPlatformViewsTestMockWrapperWebView : NSObject <FlutterPlatformView>
146+ @property (nonatomic , strong ) UIView* view;
147+ @property (nonatomic , assign ) BOOL viewCreated;
148+ @end
149+
150+ @implementation FlutterPlatformViewsTestMockWrapperWebView
151+ - (instancetype )init {
152+ if (self = [super init ]) {
153+ _view = [[UIView alloc ] init ];
154+ [_view addSubview: [[WKWebView alloc ] init ]];
155+ gMockPlatformView = _view;
156+ _viewCreated = NO ;
157+ }
158+ return self;
159+ }
160+
161+ - (UIView*)view {
162+ [self checkViewCreatedOnce ];
163+ return _view;
164+ }
165+
166+ - (void )checkViewCreatedOnce {
167+ if (self.viewCreated ) {
168+ abort ();
169+ }
170+ self.viewCreated = YES ;
171+ }
172+
173+ - (void )dealloc {
174+ gMockPlatformView = nil ;
175+ }
176+ @end
177+
178+ @interface FlutterPlatformViewsTestMockWrapperWebViewFactory : NSObject <FlutterPlatformViewFactory>
179+ @end
180+
181+ @implementation FlutterPlatformViewsTestMockWrapperWebViewFactory
182+ - (NSObject <FlutterPlatformView>*)createWithFrame : (CGRect)frame
183+ viewIdentifier : (int64_t )viewId
184+ arguments : (id _Nullable)args {
185+ return [[FlutterPlatformViewsTestMockWrapperWebView alloc ] init ];
186+ }
187+ @end
188+
189+ @interface FlutterPlatformViewsTestMockNestedWrapperWebView : NSObject <FlutterPlatformView>
190+ @property (nonatomic , strong ) UIView* view;
191+ @property (nonatomic , assign ) BOOL viewCreated;
192+ @end
193+
194+ @implementation FlutterPlatformViewsTestMockNestedWrapperWebView
195+ - (instancetype )init {
196+ if (self = [super init ]) {
197+ _view = [[UIView alloc ] init ];
198+ UIView* childView = [[UIView alloc ] init ];
199+ [_view addSubview: childView];
200+ [childView addSubview: [[WKWebView alloc ] init ]];
201+ gMockPlatformView = _view;
202+ _viewCreated = NO ;
203+ }
204+ return self;
205+ }
206+
207+ - (UIView*)view {
208+ [self checkViewCreatedOnce ];
209+ return _view;
210+ }
211+
212+ - (void )checkViewCreatedOnce {
213+ if (self.viewCreated ) {
214+ abort ();
215+ }
216+ self.viewCreated = YES ;
217+ }
218+ @end
219+
220+ @interface FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
221+ : NSObject <FlutterPlatformViewFactory>
222+ @end
223+
224+ @implementation FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
225+ - (NSObject <FlutterPlatformView>*)createWithFrame : (CGRect)frame
226+ viewIdentifier : (int64_t )viewId
227+ arguments : (id _Nullable)args {
228+ return [[FlutterPlatformViewsTestMockNestedWrapperWebView alloc ] init ];
229+ }
230+ @end
231+
138232namespace flutter {
139233namespace {
140234class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView ::Delegate {
@@ -2883,6 +2977,125 @@ - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled {
28832977 }
28842978}
28852979
2980+ - (void )
2981+ testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldRemoveAndAddBackDelayingRecognizerForWrapperWebView {
2982+ flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2983+
2984+ flutter::TaskRunners runners (/* label=*/ self.name .UTF8String ,
2985+ /* platform=*/ GetDefaultTaskRunner (),
2986+ /* raster=*/ GetDefaultTaskRunner (),
2987+ /* ui=*/ GetDefaultTaskRunner (),
2988+ /* io=*/ GetDefaultTaskRunner ());
2989+ auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2990+ flutterPlatformViewsController->SetTaskRunner (GetDefaultTaskRunner ());
2991+ auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2992+ /* delegate=*/ mock_delegate,
2993+ /* rendering_api=*/ mock_delegate.settings_ .enable_impeller
2994+ ? flutter::IOSRenderingAPI::kMetal
2995+ : flutter::IOSRenderingAPI::kSoftware ,
2996+ /* platform_views_controller=*/ flutterPlatformViewsController,
2997+ /* task_runners=*/ runners,
2998+ /* worker_task_runner=*/ nil ,
2999+ /* is_gpu_disabled_jsync_switch=*/ std::make_shared<fml::SyncSwitch>());
3000+
3001+ FlutterPlatformViewsTestMockWrapperWebViewFactory* factory =
3002+ [[FlutterPlatformViewsTestMockWrapperWebViewFactory alloc ] init ];
3003+ flutterPlatformViewsController->RegisterViewFactory (
3004+ factory, @" MockWrapperWebView" , FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
3005+ FlutterResult result = ^(id result) {
3006+ };
3007+ flutterPlatformViewsController->OnMethodCall (
3008+ [FlutterMethodCall
3009+ methodCallWithMethodName: @" create"
3010+ arguments: @{@" id" : @2 , @" viewType" : @" MockWrapperWebView" }],
3011+ result);
3012+
3013+ XCTAssertNotNil (gMockPlatformView );
3014+
3015+ // Find touch inteceptor view
3016+ UIView* touchInteceptorView = gMockPlatformView ;
3017+ while (touchInteceptorView != nil &&
3018+ ![touchInteceptorView isKindOfClass: [FlutterTouchInterceptingView class ]]) {
3019+ touchInteceptorView = touchInteceptorView.superview ;
3020+ }
3021+ XCTAssertNotNil (touchInteceptorView);
3022+
3023+ XCTAssert (touchInteceptorView.gestureRecognizers .count == 2 );
3024+ UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers [0 ];
3025+ UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers [1 ];
3026+
3027+ XCTAssert ([delayingRecognizer isKindOfClass: [FlutterDelayingGestureRecognizer class ]]);
3028+ XCTAssert ([forwardingRecognizer isKindOfClass: [ForwardingGestureRecognizer class ]]);
3029+
3030+ [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture ];
3031+
3032+ if (@available (iOS 18.2 , *)) {
3033+ // Since we remove and add back delayingRecognizer, it would be reordered to the last.
3034+ XCTAssertEqual (touchInteceptorView.gestureRecognizers [0 ], forwardingRecognizer);
3035+ XCTAssertEqual (touchInteceptorView.gestureRecognizers [1 ], delayingRecognizer);
3036+ } else {
3037+ XCTAssertEqual (touchInteceptorView.gestureRecognizers [0 ], delayingRecognizer);
3038+ XCTAssertEqual (touchInteceptorView.gestureRecognizers [1 ], forwardingRecognizer);
3039+ }
3040+ }
3041+
3042+ - (void )
3043+ testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNestedWrapperWebView {
3044+ flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3045+
3046+ flutter::TaskRunners runners (/* label=*/ self.name .UTF8String ,
3047+ /* platform=*/ GetDefaultTaskRunner (),
3048+ /* raster=*/ GetDefaultTaskRunner (),
3049+ /* ui=*/ GetDefaultTaskRunner (),
3050+ /* io=*/ GetDefaultTaskRunner ());
3051+ auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3052+ flutterPlatformViewsController->SetTaskRunner (GetDefaultTaskRunner ());
3053+ auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3054+ /* delegate=*/ mock_delegate,
3055+ /* rendering_api=*/ mock_delegate.settings_ .enable_impeller
3056+ ? flutter::IOSRenderingAPI::kMetal
3057+ : flutter::IOSRenderingAPI::kSoftware ,
3058+ /* platform_views_controller=*/ flutterPlatformViewsController,
3059+ /* task_runners=*/ runners,
3060+ /* worker_task_runner=*/ nil ,
3061+ /* is_gpu_disabled_jsync_switch=*/ std::make_shared<fml::SyncSwitch>());
3062+
3063+ FlutterPlatformViewsTestMockNestedWrapperWebViewFactory* factory =
3064+ [[FlutterPlatformViewsTestMockNestedWrapperWebViewFactory alloc ] init ];
3065+ flutterPlatformViewsController->RegisterViewFactory (
3066+ factory, @" MockNestedWrapperWebView" ,
3067+ FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
3068+ FlutterResult result = ^(id result) {
3069+ };
3070+ flutterPlatformViewsController->OnMethodCall (
3071+ [FlutterMethodCall
3072+ methodCallWithMethodName: @" create"
3073+ arguments: @{@" id" : @2 , @" viewType" : @" MockNestedWrapperWebView" }],
3074+ result);
3075+
3076+ XCTAssertNotNil (gMockPlatformView );
3077+
3078+ // Find touch inteceptor view
3079+ UIView* touchInteceptorView = gMockPlatformView ;
3080+ while (touchInteceptorView != nil &&
3081+ ![touchInteceptorView isKindOfClass: [FlutterTouchInterceptingView class ]]) {
3082+ touchInteceptorView = touchInteceptorView.superview ;
3083+ }
3084+ XCTAssertNotNil (touchInteceptorView);
3085+
3086+ XCTAssert (touchInteceptorView.gestureRecognizers .count == 2 );
3087+ UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers [0 ];
3088+ UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers [1 ];
3089+
3090+ XCTAssert ([delayingRecognizer isKindOfClass: [FlutterDelayingGestureRecognizer class ]]);
3091+ XCTAssert ([forwardingRecognizer isKindOfClass: [ForwardingGestureRecognizer class ]]);
3092+
3093+ [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture ];
3094+
3095+ XCTAssertEqual (touchInteceptorView.gestureRecognizers [0 ], delayingRecognizer);
3096+ XCTAssertEqual (touchInteceptorView.gestureRecognizers [1 ], forwardingRecognizer);
3097+ }
3098+
28863099- (void )
28873100 testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNonWebView {
28883101 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
0 commit comments