Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 23cdf0f

Browse files
authored
[ios]limit web view not tappable workaround to a limited depth (#57193)
This PR limits the search depth, because we don't want to enable this workaround for AdMob banner, which has a WKWebView in the depth of 7. See the previous PR for more context: #57168 I was able to confirm that this returns YES for the 3P plugin, and NO for AdMob. *List which issues are fixed by this PR. You must list at least one issue.* flutter/flutter#158961 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent f0ff4f2 commit 23cdf0f

File tree

2 files changed

+127
-3
lines changed

2 files changed

+127
-3
lines changed

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm

+12-3
Original file line numberDiff line numberDiff line change
@@ -566,12 +566,15 @@ - (void)releaseGesture {
566566
self.delayingRecognizer.state = UIGestureRecognizerStateFailed;
567567
}
568568

569-
- (BOOL)containsWebView:(UIView*)view {
569+
- (BOOL)containsWebView:(UIView*)view remainingSubviewDepth:(int)remainingSubviewDepth {
570+
if (remainingSubviewDepth < 0) {
571+
return NO;
572+
}
570573
if ([view isKindOfClass:[WKWebView class]]) {
571574
return YES;
572575
}
573576
for (UIView* subview in view.subviews) {
574-
if ([self containsWebView:subview]) {
577+
if ([self containsWebView:subview remainingSubviewDepth:remainingSubviewDepth - 1]) {
575578
return YES;
576579
}
577580
}
@@ -593,7 +596,13 @@ - (void)blockGesture {
593596
// FlutterPlatformViewGestureRecognizersBlockingPolicyEager, but we should try it if a similar
594597
// issue arises for the other policy.
595598
if (@available(iOS 18.2, *)) {
596-
if ([self containsWebView:self.embeddedView]) {
599+
// This workaround is designed for WKWebView only. The 1P web view plugin provides a
600+
// WKWebView itself as the platform view. However, some 3P plugins provide wrappers of
601+
// WKWebView instead. So we perform DFS to search the view hierarchy (with a depth limit).
602+
// Passing a limit of 0 means only searching for platform view itself; Pass 1 to include its
603+
// children as well, and so on. We should be conservative and start with a small number. The
604+
// AdMob banner has a WKWebView at depth 7.
605+
if ([self containsWebView:self.embeddedView remainingSubviewDepth:1]) {
597606
[self removeGestureRecognizer:self.delayingRecognizer];
598607
[self addGestureRecognizer:self.delayingRecognizer];
599608
}

shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm

+115
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ - (void)checkViewCreatedOnce {
7474
self.viewCreated = YES;
7575
}
7676

77+
- (void)dealloc {
78+
gMockPlatformView = nil;
79+
}
7780
@end
7881

7982
@interface FlutterPlatformViewsTestMockFlutterPlatformFactory
@@ -115,6 +118,10 @@ - (void)checkViewCreatedOnce {
115118
}
116119
self.viewCreated = YES;
117120
}
121+
122+
- (void)dealloc {
123+
gMockPlatformView = nil;
124+
}
118125
@end
119126

120127
@interface FlutterPlatformViewsTestMockWebViewFactory : NSObject <FlutterPlatformViewFactory>
@@ -167,6 +174,10 @@ - (void)checkViewCreatedOnce {
167174
}
168175
self.viewCreated = YES;
169176
}
177+
178+
- (void)dealloc {
179+
gMockPlatformView = nil;
180+
}
170181
@end
171182

172183
@interface FlutterPlatformViewsTestMockWrapperWebViewFactory : NSObject <FlutterPlatformViewFactory>
@@ -180,6 +191,49 @@ @implementation FlutterPlatformViewsTestMockWrapperWebViewFactory
180191
}
181192
@end
182193

194+
@interface FlutterPlatformViewsTestMockNestedWrapperWebView : NSObject <FlutterPlatformView>
195+
@property(nonatomic, strong) UIView* view;
196+
@property(nonatomic, assign) BOOL viewCreated;
197+
@end
198+
199+
@implementation FlutterPlatformViewsTestMockNestedWrapperWebView
200+
- (instancetype)init {
201+
if (self = [super init]) {
202+
_view = [[UIView alloc] init];
203+
UIView* childView = [[UIView alloc] init];
204+
[_view addSubview:childView];
205+
[childView addSubview:[[WKWebView alloc] init]];
206+
gMockPlatformView = _view;
207+
_viewCreated = NO;
208+
}
209+
return self;
210+
}
211+
212+
- (UIView*)view {
213+
[self checkViewCreatedOnce];
214+
return _view;
215+
}
216+
217+
- (void)checkViewCreatedOnce {
218+
if (self.viewCreated) {
219+
abort();
220+
}
221+
self.viewCreated = YES;
222+
}
223+
@end
224+
225+
@interface FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
226+
: NSObject <FlutterPlatformViewFactory>
227+
@end
228+
229+
@implementation FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
230+
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
231+
viewIdentifier:(int64_t)viewId
232+
arguments:(id _Nullable)args {
233+
return [[FlutterPlatformViewsTestMockNestedWrapperWebView alloc] init];
234+
}
235+
@end
236+
183237
namespace flutter {
184238
namespace {
185239
class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate {
@@ -3258,6 +3312,67 @@ - (void)testFlutterPlatformViewTouchesEndedOrTouchesCancelledEventDoesNotFailThe
32583312
}
32593313
}
32603314

3315+
- (void)
3316+
testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNestedWrapperWebView {
3317+
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3318+
3319+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3320+
/*platform=*/GetDefaultTaskRunner(),
3321+
/*raster=*/GetDefaultTaskRunner(),
3322+
/*ui=*/GetDefaultTaskRunner(),
3323+
/*io=*/GetDefaultTaskRunner());
3324+
FlutterPlatformViewsController* flutterPlatformViewsController =
3325+
[[FlutterPlatformViewsController alloc] init];
3326+
flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3327+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3328+
/*delegate=*/mock_delegate,
3329+
/*rendering_api=*/mock_delegate.settings_.enable_impeller
3330+
? flutter::IOSRenderingAPI::kMetal
3331+
: flutter::IOSRenderingAPI::kSoftware,
3332+
/*platform_views_controller=*/flutterPlatformViewsController,
3333+
/*task_runners=*/runners,
3334+
/*worker_task_runner=*/nil,
3335+
/*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3336+
3337+
FlutterPlatformViewsTestMockNestedWrapperWebViewFactory* factory =
3338+
[[FlutterPlatformViewsTestMockNestedWrapperWebViewFactory alloc] init];
3339+
[flutterPlatformViewsController
3340+
registerViewFactory:factory
3341+
withId:@"MockNestedWrapperWebView"
3342+
gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3343+
FlutterResult result = ^(id result) {
3344+
};
3345+
[flutterPlatformViewsController
3346+
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3347+
arguments:@{
3348+
@"id" : @2,
3349+
@"viewType" : @"MockNestedWrapperWebView"
3350+
}]
3351+
result:result];
3352+
3353+
XCTAssertNotNil(gMockPlatformView);
3354+
3355+
// Find touch inteceptor view
3356+
UIView* touchInteceptorView = gMockPlatformView;
3357+
while (touchInteceptorView != nil &&
3358+
![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3359+
touchInteceptorView = touchInteceptorView.superview;
3360+
}
3361+
XCTAssertNotNil(touchInteceptorView);
3362+
3363+
XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
3364+
UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
3365+
UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
3366+
3367+
XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
3368+
XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
3369+
3370+
[(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
3371+
3372+
XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3373+
XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3374+
}
3375+
32613376
- (void)
32623377
testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNonWebView {
32633378
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;

0 commit comments

Comments
 (0)