From a7cda8d5b61faaf512745dc7a8c63411e1289696 Mon Sep 17 00:00:00 2001 From: Alexandre Richonnier Date: Fri, 10 Mar 2023 11:03:00 +0100 Subject: [PATCH 1/3] fix #1389 #1315 contextMenu ios 13 --- example/lib/in_app_webiew_example.screen.dart | 17 ++--- ios/Classes/InAppWebView/InAppWebView.swift | 62 ++++++++++++------- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 2f56f4c7..c90df746 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -17,11 +17,10 @@ class _InAppWebViewExampleScreenState extends State { InAppWebViewController? webViewController; InAppWebViewSettings settings = InAppWebViewSettings( - mediaPlaybackRequiresUserGesture: false, - allowsInlineMediaPlayback: true, - iframeAllow: "camera; microphone", - iframeAllowFullscreen: true - ); + mediaPlaybackRequiresUserGesture: false, + allowsInlineMediaPlayback: true, + iframeAllow: "camera; microphone", + iframeAllowFullscreen: true); PullToRefreshController? pullToRefreshController; @@ -45,7 +44,7 @@ class _InAppWebViewExampleScreenState extends State { await webViewController?.clearFocus(); }) ], - settings: ContextMenuSettings(hideDefaultSystemContextMenuItems: false), + settings: ContextMenuSettings(hideDefaultSystemContextMenuItems: true), onCreateContextMenu: (hitTestResult) async { print("onCreateContextMenu"); print(hitTestResult.extra); @@ -62,7 +61,9 @@ class _InAppWebViewExampleScreenState extends State { contextMenuItemClicked.title); }); - pullToRefreshController = kIsWeb || ![TargetPlatform.iOS, TargetPlatform.android].contains(defaultTargetPlatform) + pullToRefreshController = kIsWeb || + ![TargetPlatform.iOS, TargetPlatform.android] + .contains(defaultTargetPlatform) ? null : PullToRefreshController( settings: PullToRefreshSettings( @@ -120,7 +121,7 @@ class _InAppWebViewExampleScreenState extends State { // initialFile: "assets/index.html", initialUserScripts: UnmodifiableListView([]), initialSettings: settings, - // contextMenu: contextMenu, + contextMenu: contextMenu, pullToRefreshController: pullToRefreshController, onWebViewCreated: (controller) async { webViewController = controller; diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index d8733865..75744276 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -239,34 +239,50 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, return super.hitTest(point, with: event) } - public override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { - if let _ = sender as? UIMenuController { - if self.settings?.disableContextMenu == true { - if !onCreateContextMenuEventTriggeredWhenMenuDisabled { - // workaround to trigger onCreateContextMenu event as the same on Android - self.onCreateContextMenu() - onCreateContextMenuEventTriggeredWhenMenuDisabled = true - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.onCreateContextMenuEventTriggeredWhenMenuDisabled = false - } - } - return false - } - + + + @available(iOS 13.0, *) + public override func buildMenu(with builder: UIMenuBuilder) { + if #available(iOS 16.0, *) { if let menu = contextMenu { let contextMenuSettings = ContextMenuSettings() if let contextMenuSettingsMap = menu["settings"] as? [String: Any?] { let _ = contextMenuSettings.parse(settings: contextMenuSettingsMap) - if !action.description.starts(with: "onContextMenuActionItemClicked-") && contextMenuSettings.hideDefaultSystemContextMenuItems { - return false + if contextMenuSettings.hideDefaultSystemContextMenuItems { + builder.remove(menu: .lookup) } } } - - if contextMenuIsShowing, !action.description.starts(with: "onContextMenuActionItemClicked-") { - let id = action.description.compactMap({ $0.asciiValue?.description }).joined() - self.channelDelegate?.onContextMenuActionItemClicked(id: id, title: action.description) + } + super.buildMenu(with: builder) + } + + public override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { + if self.settings?.disableContextMenu == true { + if !onCreateContextMenuEventTriggeredWhenMenuDisabled { + // workaround to trigger onCreateContextMenu event as the same on Android + self.onCreateContextMenu() + onCreateContextMenuEventTriggeredWhenMenuDisabled = true + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.onCreateContextMenuEventTriggeredWhenMenuDisabled = false + } } + return false + } + + if let menu = contextMenu { + let contextMenuSettings = ContextMenuSettings() + if let contextMenuSettingsMap = menu["settings"] as? [String: Any?] { + let _ = contextMenuSettings.parse(settings: contextMenuSettingsMap) + if !action.description.starts(with: "onContextMenuActionItemClicked-") && contextMenuSettings.hideDefaultSystemContextMenuItems { + return false + } + } + } + + if contextMenuIsShowing, !action.description.starts(with: "onContextMenuActionItemClicked-") { + let id = action.description.compactMap({ $0.asciiValue?.description }).joined() + self.channelDelegate?.onContextMenuActionItemClicked(id: id, title: action.description) } return super.canPerformAction(action, withSender: sender) @@ -612,7 +628,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, contextMenuIsShowing = true let hitTestResult = HitTestResult(type: .unknownType, extra: nil) - + + + + + if let lastLongPressTouhLocation = lastLongPressTouchPoint { if configuration.preferences.javaScriptEnabled { self.evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._findElementsAtPoint(\(lastLongPressTouhLocation.x),\(lastLongPressTouhLocation.y))", completionHandler: {(value, error) in From 47427044c80405a3104c6c06bfff68ee42ce0f1c Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Mon, 15 May 2023 19:28:08 +0200 Subject: [PATCH 2/3] Update in_app_webiew_example.screen.dart --- example/lib/in_app_webiew_example.screen.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index c90df746..2345c49c 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -17,6 +17,7 @@ class _InAppWebViewExampleScreenState extends State { InAppWebViewController? webViewController; InAppWebViewSettings settings = InAppWebViewSettings( + isInspectable: kDebugMode, mediaPlaybackRequiresUserGesture: false, allowsInlineMediaPlayback: true, iframeAllow: "camera; microphone", @@ -44,7 +45,7 @@ class _InAppWebViewExampleScreenState extends State { await webViewController?.clearFocus(); }) ], - settings: ContextMenuSettings(hideDefaultSystemContextMenuItems: true), + settings: ContextMenuSettings(hideDefaultSystemContextMenuItems: false), onCreateContextMenu: (hitTestResult) async { print("onCreateContextMenu"); print(hitTestResult.extra); From 57106e8dc5bf8324ce095f1933f330e223aa1020 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Mon, 15 May 2023 19:30:25 +0200 Subject: [PATCH 3/3] fix onCreateContextMenu and onHideContextMenu events for iOS 16.0+ --- ios/Classes/InAppWebView/InAppWebView.swift | 136 +++++++++++++------- 1 file changed, 86 insertions(+), 50 deletions(-) diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index 75744276..4d89ab00 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -224,6 +224,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, if !self.responds(to: Selector(targetMethodName)) { let customAction: () -> Void = { self.channelDelegate?.onContextMenuActionItemClicked(id: id, title: title) + if #available(iOS 16.0, *) { + if #unavailable(iOS 16.4) { + self.onHideContextMenu() + } + } } let castedCustomAction: AnyObject = unsafeBitCast(customAction as @convention(block) () -> Void, to: AnyObject.self) let swizzledImplementation = imp_implementationWithBlock(castedCustomAction) @@ -239,8 +244,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, return super.hitTest(point, with: event) } - - @available(iOS 13.0, *) public override func buildMenu(with builder: UIMenuBuilder) { if #available(iOS 16.0, *) { @@ -248,43 +251,71 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, let contextMenuSettings = ContextMenuSettings() if let contextMenuSettingsMap = menu["settings"] as? [String: Any?] { let _ = contextMenuSettings.parse(settings: contextMenuSettingsMap) - if contextMenuSettings.hideDefaultSystemContextMenuItems { + if contextMenuSettings.hideDefaultSystemContextMenuItems { builder.remove(menu: .lookup) } } } + + if #unavailable(iOS 16.4), settings?.disableContextMenu == false { + contextMenuIsShowing = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { + self.onCreateContextMenu() + } + } } super.buildMenu(with: builder) } + @available(iOS 16.4, *) + public func webView(_ webView: WKWebView, willPresentEditMenuWithAnimator animator: UIEditMenuInteractionAnimating) { + onCreateContextMenu() + } + + @available(iOS 16.4, *) + public func webView(_ webView: WKWebView, willDismissEditMenuWithAnimator animator: UIEditMenuInteractionAnimating) { + onHideContextMenu() + } + public override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { - if self.settings?.disableContextMenu == true { - if !onCreateContextMenuEventTriggeredWhenMenuDisabled { - // workaround to trigger onCreateContextMenu event as the same on Android - self.onCreateContextMenu() - onCreateContextMenuEventTriggeredWhenMenuDisabled = true - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.onCreateContextMenuEventTriggeredWhenMenuDisabled = false - } - } - return false + var needCheck = sender is UIMenuController + if #available(iOS 13.0, *) { + needCheck = sender is UIMenuElement || sender is UIMenuController } - if let menu = contextMenu { - let contextMenuSettings = ContextMenuSettings() - if let contextMenuSettingsMap = menu["settings"] as? [String: Any?] { - let _ = contextMenuSettings.parse(settings: contextMenuSettingsMap) - if !action.description.starts(with: "onContextMenuActionItemClicked-") && contextMenuSettings.hideDefaultSystemContextMenuItems { - return false + if needCheck { + if settings?.disableContextMenu == true { + if !onCreateContextMenuEventTriggeredWhenMenuDisabled { + // workaround to trigger onCreateContextMenu event as the same on Android + onCreateContextMenu() + onCreateContextMenuEventTriggeredWhenMenuDisabled = true + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.onCreateContextMenuEventTriggeredWhenMenuDisabled = false + } + } + return false + } + + if let menu = contextMenu { + let contextMenuSettings = ContextMenuSettings() + if let contextMenuSettingsMap = menu["settings"] as? [String: Any?] { + let _ = contextMenuSettings.parse(settings: contextMenuSettingsMap) + if !action.description.starts(with: "onContextMenuActionItemClicked-") && contextMenuSettings.hideDefaultSystemContextMenuItems { + return false + } + } + } + + if contextMenuIsShowing, !action.description.starts(with: "onContextMenuActionItemClicked-") { + let id = action.description.compactMap({ $0.asciiValue?.description }).joined() + channelDelegate?.onContextMenuActionItemClicked(id: id, title: action.description) + if #available(iOS 16.0, *) { + if #unavailable(iOS 16.4) { + onHideContextMenu() + } } } } - - if contextMenuIsShowing, !action.description.starts(with: "onContextMenuActionItemClicked-") { - let id = action.description.compactMap({ $0.asciiValue?.description }).joined() - self.channelDelegate?.onContextMenuActionItemClicked(id: id, title: action.description) - } - return super.canPerformAction(action, withSender: sender) } @@ -336,17 +367,19 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, context: nil) } - NotificationCenter.default.addObserver( - self, - selector: #selector(onCreateContextMenu), - name: UIMenuController.willShowMenuNotification, - object: nil) - - NotificationCenter.default.addObserver( - self, - selector: #selector(onHideContextMenu), - name: UIMenuController.didHideMenuNotification, - object: nil) + if #unavailable(iOS 16.0) { + NotificationCenter.default.addObserver( + self, + selector: #selector(onCreateContextMenu), + name: UIMenuController.willShowMenuNotification, + object: nil) + + NotificationCenter.default.addObserver( + self, + selector: #selector(onHideContextMenu), + name: UIMenuController.didHideMenuNotification, + object: nil) + } // TODO: Still not working on iOS 16.0! // if #available(iOS 16.0, *) { @@ -443,8 +476,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, isFindInteractionEnabled = settings.isFindInteractionEnabled } - // debugging is always enabled for iOS, - // there isn't any option to set about it such as on Android. + if #available(iOS 16.4, *) { + isInspectable = settings.isInspectable + } if settings.clearCache { clearCache() @@ -480,14 +514,16 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, configuration.defaultWebpagePreferences.allowsContentJavaScript = settings.javaScriptEnabled } - if #available(iOS 14.5, *) { + if #available(iOS 15.0, *) { configuration.preferences.isTextInteractionEnabled = settings.isTextInteractionEnabled } - if #available(iOS 15.4, *) { configuration.preferences.isSiteSpecificQuirksModeEnabled = settings.isSiteSpecificQuirksModeEnabled configuration.preferences.isElementFullscreenEnabled = settings.isElementFullscreenEnabled } + if #available(iOS 16.4, *) { + configuration.preferences.shouldPrintBackgrounds = settings.shouldPrintBackgrounds + } } } @@ -628,11 +664,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, contextMenuIsShowing = true let hitTestResult = HitTestResult(type: .unknownType, extra: nil) - - - - - + if let lastLongPressTouhLocation = lastLongPressTouchPoint { if configuration.preferences.javaScriptEnabled { self.evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._findElementsAtPoint(\(lastLongPressTouhLocation.x),\(lastLongPressTouhLocation.y))", completionHandler: {(value, error) in @@ -1207,16 +1239,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, } } - if #available(iOS 14.5, *) { + if #available(iOS 15.0, *) { if newSettingsMap["upgradeKnownHostsToHTTPS"] != nil && settings?.upgradeKnownHostsToHTTPS != newSettings.upgradeKnownHostsToHTTPS { configuration.upgradeKnownHostsToHTTPS = newSettings.upgradeKnownHostsToHTTPS } if newSettingsMap["isTextInteractionEnabled"] != nil && settings?.isTextInteractionEnabled != newSettings.isTextInteractionEnabled { configuration.preferences.isTextInteractionEnabled = newSettings.isTextInteractionEnabled } - } - - if #available(iOS 15.0, *) { if newSettingsMap["underPageBackgroundColor"] != nil, settings?.underPageBackgroundColor != newSettings.underPageBackgroundColor, let underPageBackgroundColor = newSettings.underPageBackgroundColor, !underPageBackgroundColor.isEmpty { self.underPageBackgroundColor = UIColor(hexString: underPageBackgroundColor) @@ -1234,12 +1263,19 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, setMinimumViewportInset(minViewportInset, maximumViewportInset: maxViewportInset) } } - if #available(iOS 16.0, *) { if newSettingsMap["isFindInteractionEnabled"] != nil, settings?.isFindInteractionEnabled != newSettings.isFindInteractionEnabled { isFindInteractionEnabled = newSettings.isFindInteractionEnabled } } + if #available(iOS 16.4, *) { + if newSettingsMap["isInspectable"] != nil, settings?.isInspectable != newSettings.isInspectable { + isInspectable = newSettings.isInspectable + } + if newSettingsMap["shouldPrintBackgrounds"] != nil, settings?.shouldPrintBackgrounds != newSettings.shouldPrintBackgrounds { + configuration.preferences.shouldPrintBackgrounds = newSettings.shouldPrintBackgrounds + } + } scrollView.isScrollEnabled = !(newSettings.disableVerticalScroll && newSettings.disableHorizontalScroll)