[ios]limit web view not tappable workaround to a limited depth (flutter/engine#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: https://github.com/flutter/engine/pull/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.* https://github.com/flutter/flutter/issues/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
This commit is contained in:
parent
ee9024da88
commit
e2970df999
@ -566,12 +566,15 @@ static BOOL _preparedOnce = NO;
|
|||||||
self.delayingRecognizer.state = UIGestureRecognizerStateFailed;
|
self.delayingRecognizer.state = UIGestureRecognizerStateFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)containsWebView:(UIView*)view {
|
- (BOOL)containsWebView:(UIView*)view remainingSubviewDepth:(int)remainingSubviewDepth {
|
||||||
|
if (remainingSubviewDepth < 0) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
if ([view isKindOfClass:[WKWebView class]]) {
|
if ([view isKindOfClass:[WKWebView class]]) {
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
for (UIView* subview in view.subviews) {
|
for (UIView* subview in view.subviews) {
|
||||||
if ([self containsWebView:subview]) {
|
if ([self containsWebView:subview remainingSubviewDepth:remainingSubviewDepth - 1]) {
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -593,7 +596,13 @@ static BOOL _preparedOnce = NO;
|
|||||||
// FlutterPlatformViewGestureRecognizersBlockingPolicyEager, but we should try it if a similar
|
// FlutterPlatformViewGestureRecognizersBlockingPolicyEager, but we should try it if a similar
|
||||||
// issue arises for the other policy.
|
// issue arises for the other policy.
|
||||||
if (@available(iOS 18.2, *)) {
|
if (@available(iOS 18.2, *)) {
|
||||||
if ([self containsWebView:self.embeddedView]) {
|
// This workaround is designed for WKWebView only. The 1P web view plugin provides a
|
||||||
|
// WKWebView itself as the platform view. However, some 3P plugins provide wrappers of
|
||||||
|
// WKWebView instead. So we perform DFS to search the view hierarchy (with a depth limit).
|
||||||
|
// Passing a limit of 0 means only searching for platform view itself; Pass 1 to include its
|
||||||
|
// children as well, and so on. We should be conservative and start with a small number. The
|
||||||
|
// AdMob banner has a WKWebView at depth 7.
|
||||||
|
if ([self containsWebView:self.embeddedView remainingSubviewDepth:1]) {
|
||||||
[self removeGestureRecognizer:self.delayingRecognizer];
|
[self removeGestureRecognizer:self.delayingRecognizer];
|
||||||
[self addGestureRecognizer:self.delayingRecognizer];
|
[self addGestureRecognizer:self.delayingRecognizer];
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,9 @@ const float kFloatCompareEpsilon = 0.001;
|
|||||||
self.viewCreated = YES;
|
self.viewCreated = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
gMockPlatformView = nil;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface FlutterPlatformViewsTestMockFlutterPlatformFactory
|
@interface FlutterPlatformViewsTestMockFlutterPlatformFactory
|
||||||
@ -115,6 +118,10 @@ const float kFloatCompareEpsilon = 0.001;
|
|||||||
}
|
}
|
||||||
self.viewCreated = YES;
|
self.viewCreated = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
gMockPlatformView = nil;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface FlutterPlatformViewsTestMockWebViewFactory : NSObject <FlutterPlatformViewFactory>
|
@interface FlutterPlatformViewsTestMockWebViewFactory : NSObject <FlutterPlatformViewFactory>
|
||||||
@ -167,6 +174,10 @@ const float kFloatCompareEpsilon = 0.001;
|
|||||||
}
|
}
|
||||||
self.viewCreated = YES;
|
self.viewCreated = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
gMockPlatformView = nil;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface FlutterPlatformViewsTestMockWrapperWebViewFactory : NSObject <FlutterPlatformViewFactory>
|
@interface FlutterPlatformViewsTestMockWrapperWebViewFactory : NSObject <FlutterPlatformViewFactory>
|
||||||
@ -180,6 +191,49 @@ const float kFloatCompareEpsilon = 0.001;
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface FlutterPlatformViewsTestMockNestedWrapperWebView : NSObject <FlutterPlatformView>
|
||||||
|
@property(nonatomic, strong) UIView* view;
|
||||||
|
@property(nonatomic, assign) BOOL viewCreated;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FlutterPlatformViewsTestMockNestedWrapperWebView
|
||||||
|
- (instancetype)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
_view = [[UIView alloc] init];
|
||||||
|
UIView* childView = [[UIView alloc] init];
|
||||||
|
[_view addSubview:childView];
|
||||||
|
[childView addSubview:[[WKWebView alloc] init]];
|
||||||
|
gMockPlatformView = _view;
|
||||||
|
_viewCreated = NO;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIView*)view {
|
||||||
|
[self checkViewCreatedOnce];
|
||||||
|
return _view;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)checkViewCreatedOnce {
|
||||||
|
if (self.viewCreated) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
self.viewCreated = YES;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
|
||||||
|
: NSObject <FlutterPlatformViewFactory>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
|
||||||
|
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
|
||||||
|
viewIdentifier:(int64_t)viewId
|
||||||
|
arguments:(id _Nullable)args {
|
||||||
|
return [[FlutterPlatformViewsTestMockNestedWrapperWebView alloc] init];
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
namespace flutter {
|
namespace flutter {
|
||||||
namespace {
|
namespace {
|
||||||
class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate {
|
class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate {
|
||||||
@ -3258,6 +3312,67 @@ fml::RefPtr<fml::TaskRunner> GetDefaultTaskRunner() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)
|
||||||
|
testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNestedWrapperWebView {
|
||||||
|
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
|
||||||
|
|
||||||
|
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
|
||||||
|
/*platform=*/GetDefaultTaskRunner(),
|
||||||
|
/*raster=*/GetDefaultTaskRunner(),
|
||||||
|
/*ui=*/GetDefaultTaskRunner(),
|
||||||
|
/*io=*/GetDefaultTaskRunner());
|
||||||
|
FlutterPlatformViewsController* flutterPlatformViewsController =
|
||||||
|
[[FlutterPlatformViewsController alloc] init];
|
||||||
|
flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
|
||||||
|
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
|
||||||
|
/*delegate=*/mock_delegate,
|
||||||
|
/*rendering_api=*/mock_delegate.settings_.enable_impeller
|
||||||
|
? flutter::IOSRenderingAPI::kMetal
|
||||||
|
: flutter::IOSRenderingAPI::kSoftware,
|
||||||
|
/*platform_views_controller=*/flutterPlatformViewsController,
|
||||||
|
/*task_runners=*/runners,
|
||||||
|
/*worker_task_runner=*/nil,
|
||||||
|
/*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
|
||||||
|
|
||||||
|
FlutterPlatformViewsTestMockNestedWrapperWebViewFactory* factory =
|
||||||
|
[[FlutterPlatformViewsTestMockNestedWrapperWebViewFactory alloc] init];
|
||||||
|
[flutterPlatformViewsController
|
||||||
|
registerViewFactory:factory
|
||||||
|
withId:@"MockNestedWrapperWebView"
|
||||||
|
gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
|
||||||
|
FlutterResult result = ^(id result) {
|
||||||
|
};
|
||||||
|
[flutterPlatformViewsController
|
||||||
|
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
|
||||||
|
arguments:@{
|
||||||
|
@"id" : @2,
|
||||||
|
@"viewType" : @"MockNestedWrapperWebView"
|
||||||
|
}]
|
||||||
|
result:result];
|
||||||
|
|
||||||
|
XCTAssertNotNil(gMockPlatformView);
|
||||||
|
|
||||||
|
// Find touch inteceptor view
|
||||||
|
UIView* touchInteceptorView = gMockPlatformView;
|
||||||
|
while (touchInteceptorView != nil &&
|
||||||
|
![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
|
||||||
|
touchInteceptorView = touchInteceptorView.superview;
|
||||||
|
}
|
||||||
|
XCTAssertNotNil(touchInteceptorView);
|
||||||
|
|
||||||
|
XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
|
||||||
|
UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
|
||||||
|
UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
|
||||||
|
|
||||||
|
XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
|
||||||
|
XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
|
||||||
|
|
||||||
|
[(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
|
||||||
|
|
||||||
|
XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
|
||||||
|
XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
|
||||||
|
}
|
||||||
|
|
||||||
- (void)
|
- (void)
|
||||||
testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNonWebView {
|
testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNonWebView {
|
||||||
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
|
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user