@capgo/inappbrowser 7.0.0 → 7.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,11 @@ private struct UrlsHandledByApp {
19
19
  static var blank = true
20
20
  }
21
21
 
22
+ public struct WKWebViewCredentials {
23
+ var username: String
24
+ var password: String
25
+ }
26
+
22
27
  @objc public protocol WKWebViewControllerDelegate {
23
28
  @objc optional func webViewController(_ controller: WKWebViewController, canDismiss url: URL) -> Bool
24
29
 
@@ -28,7 +33,17 @@ private struct UrlsHandledByApp {
28
33
  @objc optional func webViewController(_ controller: WKWebViewController, decidePolicy url: URL, navigationType: NavigationType) -> Bool
29
34
  }
30
35
 
31
- open class WKWebViewController: UIViewController {
36
+ extension Dictionary {
37
+ func mapKeys<T>(_ transform: (Key) throws -> T) rethrows -> [T: Value] {
38
+ var dictionary = [T: Value]()
39
+ for (key, value) in self {
40
+ dictionary[try transform(key)] = value
41
+ }
42
+ return dictionary
43
+ }
44
+ }
45
+
46
+ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
32
47
 
33
48
  public init() {
34
49
  super.init(nibName: nil, bundle: nil)
@@ -38,23 +53,27 @@ open class WKWebViewController: UIViewController {
38
53
  super.init(coder: aDecoder)
39
54
  }
40
55
 
41
- public init(source: WKWebSource?) {
56
+ public init(source: WKWebSource?, credentials: WKWebViewCredentials? = nil) {
42
57
  super.init(nibName: nil, bundle: nil)
43
58
  self.source = source
59
+ self.credentials = credentials
44
60
  self.initWebview()
45
61
  }
46
62
 
47
- public init(url: URL) {
63
+ public init(url: URL, credentials: WKWebViewCredentials? = nil) {
48
64
  super.init(nibName: nil, bundle: nil)
49
65
  self.source = .remote(url)
66
+ self.credentials = credentials
50
67
  self.initWebview()
51
68
  }
52
69
 
53
- public init(url: URL, headers: [String: String]) {
70
+ public init(url: URL, headers: [String: String], isInspectable: Bool, credentials: WKWebViewCredentials? = nil, preventDeeplink: Bool) {
54
71
  super.init(nibName: nil, bundle: nil)
55
72
  self.source = .remote(url)
73
+ self.credentials = credentials
56
74
  self.setHeaders(headers: headers)
57
- self.initWebview()
75
+ self.setPreventDeeplink(preventDeeplink: preventDeeplink)
76
+ self.initWebview(isInspectable: isInspectable)
58
77
  }
59
78
 
60
79
  open var hasDynamicTitle = false
@@ -74,16 +93,34 @@ open class WKWebViewController: UIViewController {
74
93
  var viewHeightLandscape: CGFloat?
75
94
  var viewHeightPortrait: CGFloat?
76
95
  var currentViewHeight: CGFloat?
96
+ open var closeModal = false
97
+ open var closeModalTitle = ""
98
+ open var closeModalDescription = ""
99
+ open var closeModalOk = ""
100
+ open var closeModalCancel = ""
101
+ open var ignoreUntrustedSSLError = false
102
+ var viewWasPresented = false
103
+ var preventDeeplink: Bool = false
104
+
105
+ internal var preShowSemaphore: DispatchSemaphore?
106
+ internal var preShowError: String?
77
107
 
78
108
  func setHeaders(headers: [String: String]) {
79
109
  self.headers = headers
80
- let userAgent = self.headers?["User-Agent"]
110
+ let lowercasedHeaders = headers.mapKeys { $0.lowercased() }
111
+ let userAgent = lowercasedHeaders["user-agent"]
81
112
  self.headers?.removeValue(forKey: "User-Agent")
82
- if userAgent != nil {
113
+ self.headers?.removeValue(forKey: "user-agent")
114
+
115
+ if let userAgent = userAgent {
83
116
  self.customUserAgent = userAgent
84
117
  }
85
118
  }
86
119
 
120
+ func setPreventDeeplink(preventDeeplink: Bool) {
121
+ self.preventDeeplink = preventDeeplink
122
+ }
123
+
87
124
  internal var customUserAgent: String? {
88
125
  didSet {
89
126
  guard let agent = userAgent else {
@@ -113,7 +150,8 @@ open class WKWebViewController: UIViewController {
113
150
 
114
151
  open var websiteTitleInNavigationBar = true
115
152
  open var doneBarButtonItemPosition: NavigationBarPosition = .right
116
- open var leftNavigaionBarItemTypes: [BarButtonItemType] = []
153
+ open var preShowScript: String?
154
+ open var leftNavigationBarItemTypes: [BarButtonItemType] = []
117
155
  open var rightNavigaionBarItemTypes: [BarButtonItemType] = []
118
156
  open var toolbarItemTypes: [BarButtonItemType] = [.back, .forward, .reload, .activity]
119
157
 
@@ -123,6 +161,8 @@ open class WKWebViewController: UIViewController {
123
161
  open var stopBarButtonItemImage: UIImage?
124
162
  open var activityBarButtonItemImage: UIImage?
125
163
 
164
+ open var buttonNearDoneIcon: UIImage?
165
+
126
166
  fileprivate var webView: WKWebView?
127
167
  fileprivate var progressView: UIProgressView?
128
168
 
@@ -173,6 +213,8 @@ open class WKWebViewController: UIViewController {
173
213
  return UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
174
214
  }()
175
215
 
216
+ fileprivate var credentials: WKWebViewCredentials?
217
+
176
218
  deinit {
177
219
  webView?.removeObserver(self, forKeyPath: estimatedProgressKeyPath)
178
220
  if websiteTitleInNavigationBar {
@@ -188,7 +230,64 @@ open class WKWebViewController: UIViewController {
188
230
  }
189
231
  }
190
232
 
191
- open func initWebview() {
233
+ open func setCredentials(credentials: WKWebViewCredentials?) {
234
+ self.credentials = credentials
235
+ }
236
+
237
+ // Method to send a message from Swift to JavaScript
238
+ open func postMessageToJS(message: [String: Any]) {
239
+ if let jsonData = try? JSONSerialization.data(withJSONObject: message, options: []),
240
+ let jsonString = String(data: jsonData, encoding: .utf8) {
241
+ let script = "window.dispatchEvent(new CustomEvent('messageFromNative', { detail: \(jsonString) }));"
242
+ webView?.evaluateJavaScript(script, completionHandler: nil)
243
+ }
244
+ }
245
+
246
+ // Method to receive messages from JavaScript
247
+ public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
248
+ if message.name == "messageHandler" {
249
+ if let messageBody = message.body as? [String: Any] {
250
+ print("Received message from JavaScript:", messageBody)
251
+ self.capBrowserPlugin?.notifyListeners("messageFromWebview", data: messageBody)
252
+ } else {
253
+ print("Received non-dictionary message from JavaScript:", message.body)
254
+ self.capBrowserPlugin?.notifyListeners("messageFromWebview", data: ["rawMessage": String(describing: message.body)])
255
+ }
256
+ } else if message.name == "preShowScriptSuccess" {
257
+ guard let semaphore = preShowSemaphore else {
258
+ print("[InAppBrowser - preShowScriptSuccess]: Semaphore not found")
259
+ return
260
+ }
261
+
262
+ semaphore.signal()
263
+ } else if message.name == "preShowScriptError" {
264
+ guard let semaphore = preShowSemaphore else {
265
+ print("[InAppBrowser - preShowScriptError]: Semaphore not found")
266
+ return
267
+ }
268
+ print("[InAppBrowser - preShowScriptError]: Error!!!!")
269
+ semaphore.signal()
270
+ }
271
+ }
272
+
273
+ func injectJavaScriptInterface() {
274
+ let script = """
275
+ if (!window.mobileApp) {
276
+ window.mobileApp = {
277
+ postMessage: function(message) {
278
+ if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.messageHandler) {
279
+ window.webkit.messageHandlers.messageHandler.postMessage(message);
280
+ }
281
+ }
282
+ };
283
+ }
284
+ """
285
+ DispatchQueue.main.async {
286
+ self.webView?.evaluateJavaScript(script, completionHandler: nil)
287
+ }
288
+ }
289
+
290
+ open func initWebview(isInspectable: Bool = true) {
192
291
 
193
292
  self.view.backgroundColor = UIColor.white
194
293
 
@@ -196,8 +295,18 @@ open class WKWebViewController: UIViewController {
196
295
  self.edgesForExtendedLayout = [.bottom]
197
296
 
198
297
  let webConfiguration = WKWebViewConfiguration()
298
+ let userContentController = WKUserContentController()
299
+ userContentController.add(self, name: "messageHandler")
300
+ userContentController.add(self, name: "preShowScriptError")
301
+ userContentController.add(self, name: "preShowScriptSuccess")
302
+ webConfiguration.userContentController = userContentController
199
303
  let webView = WKWebView(frame: .zero, configuration: webConfiguration)
200
304
 
305
+ if webView.responds(to: Selector(("setInspectable:"))) {
306
+ // Fix: https://stackoverflow.com/questions/76216183/how-to-debug-wkwebview-in-ios-16-4-1-using-xcode-14-2/76603043#76603043
307
+ webView.perform(Selector(("setInspectable:")), with: isInspectable)
308
+ }
309
+
201
310
  webView.uiDelegate = self
202
311
  webView.navigationDelegate = self
203
312
 
@@ -223,8 +332,6 @@ open class WKWebViewController: UIViewController {
223
332
  self.previousToolbarState = (navigation.toolbar.tintColor, navigation.toolbar.isHidden)
224
333
  }
225
334
 
226
- // self.restateViewHeight()
227
-
228
335
  if let s = self.source {
229
336
  self.load(source: s)
230
337
  } else {
@@ -242,19 +349,19 @@ open class WKWebViewController: UIViewController {
242
349
  var bottomPadding = CGFloat(0.0)
243
350
  var topPadding = CGFloat(0.0)
244
351
  if #available(iOS 11.0, *) {
245
- let window = UIApplication.shared.keyWindow
246
- bottomPadding = (window?.safeAreaInsets.bottom)!
247
- topPadding = (window?.safeAreaInsets.top)!
352
+ let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow })
353
+ bottomPadding = window?.safeAreaInsets.bottom ?? 0.0
354
+ topPadding = window?.safeAreaInsets.top ?? 0.0
248
355
  }
249
356
  if UIDevice.current.orientation.isPortrait {
250
357
  self.navigationController?.toolbar.isHidden = false
251
358
  if self.viewHeightPortrait == nil {
252
359
  self.viewHeightPortrait = self.view.safeAreaLayoutGuide.layoutFrame.size.height
253
360
  if toolbarItemTypes.count == 0 {
254
- self.viewHeightPortrait! = self.viewHeightPortrait! + bottomPadding
361
+ self.viewHeightPortrait! += bottomPadding
255
362
  }
256
363
  if self.navigationController?.navigationBar.isHidden == true {
257
- self.viewHeightPortrait = self.viewHeightPortrait! + topPadding
364
+ self.viewHeightPortrait! += topPadding
258
365
  }
259
366
  }
260
367
  self.currentViewHeight = self.viewHeightPortrait
@@ -263,10 +370,10 @@ open class WKWebViewController: UIViewController {
263
370
  if self.viewHeightLandscape == nil {
264
371
  self.viewHeightLandscape = self.view.safeAreaLayoutGuide.layoutFrame.size.height
265
372
  if toolbarItemTypes.count == 0 {
266
- self.viewHeightLandscape! = self.viewHeightLandscape! + bottomPadding
373
+ self.viewHeightLandscape! += bottomPadding
267
374
  }
268
375
  if self.navigationController?.navigationBar.isHidden == true {
269
- self.viewHeightLandscape = self.viewHeightLandscape! + topPadding
376
+ self.viewHeightLandscape! += topPadding
270
377
  }
271
378
  }
272
379
  self.currentViewHeight = self.viewHeightLandscape
@@ -286,8 +393,11 @@ open class WKWebViewController: UIViewController {
286
393
 
287
394
  override open func viewWillAppear(_ animated: Bool) {
288
395
  super.viewWillAppear(animated)
289
- self.setupViewElements()
290
- setUpState()
396
+ if !self.viewWasPresented {
397
+ self.setupViewElements()
398
+ setUpState()
399
+ self.viewWasPresented = true
400
+ }
291
401
  }
292
402
 
293
403
  override open func viewWillDisappear(_ animated: Bool) {
@@ -295,34 +405,33 @@ open class WKWebViewController: UIViewController {
295
405
  rollbackState()
296
406
  }
297
407
 
298
- override open func didReceiveMemoryWarning() {
299
- super.didReceiveMemoryWarning()
300
- // Dispose of any resources that can be recreated.
301
- }
302
-
303
408
  override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
304
409
  switch keyPath {
305
410
  case estimatedProgressKeyPath?:
306
- guard let estimatedProgress = self.webView?.estimatedProgress else {
307
- return
308
- }
309
- self.progressView?.alpha = 1
310
- self.progressView?.setProgress(Float(estimatedProgress), animated: true)
311
-
312
- if estimatedProgress >= 1.0 {
313
- UIView.animate(withDuration: 0.3, delay: 0.3, options: .curveEaseOut, animations: {
314
- self.progressView?.alpha = 0
315
- }, completion: {
316
- _ in
317
- self.progressView?.setProgress(0, animated: false)
318
- })
411
+ DispatchQueue.main.async {
412
+ guard let estimatedProgress = self.webView?.estimatedProgress else {
413
+ return
414
+ }
415
+ self.progressView?.alpha = 1
416
+ self.progressView?.setProgress(Float(estimatedProgress), animated: true)
417
+
418
+ if estimatedProgress >= 1.0 {
419
+ UIView.animate(withDuration: 0.3, delay: 0.3, options: .curveEaseOut, animations: {
420
+ self.progressView?.alpha = 0
421
+ }, completion: {
422
+ _ in
423
+ self.progressView?.setProgress(0, animated: false)
424
+ })
425
+ }
319
426
  }
320
427
  case titleKeyPath?:
321
428
  if self.hasDynamicTitle {
322
429
  self.navigationItem.title = webView?.url?.host
323
430
  }
324
431
  case "URL":
432
+
325
433
  self.capBrowserPlugin?.notifyListeners("urlChangeEvent", data: ["url": webView?.url?.absoluteString ?? ""])
434
+ self.injectJavaScriptInterface()
326
435
  default:
327
436
  super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
328
437
  }
@@ -344,7 +453,9 @@ public extension WKWebViewController {
344
453
  }
345
454
 
346
455
  func load(remote: URL) {
347
- webView?.load(createRequest(url: remote))
456
+ DispatchQueue.main.async {
457
+ self.webView?.load(self.createRequest(url: remote))
458
+ }
348
459
  }
349
460
 
350
461
  func load(file: URL, access: URL) {
@@ -360,6 +471,15 @@ public extension WKWebViewController {
360
471
  webView?.go(to: firstPageItem)
361
472
  }
362
473
  }
474
+ func reload() {
475
+ webView?.reload()
476
+ }
477
+
478
+ func executeScript(script: String, completion: ((Any?, Error?) -> Void)? = nil) {
479
+ DispatchQueue.main.async { [weak self] in
480
+ self?.webView?.evaluateJavaScript(script, completionHandler: completion)
481
+ }
482
+ }
363
483
  }
364
484
 
365
485
  // MARK: - Fileprivate Methods
@@ -444,7 +564,7 @@ fileprivate extension WKWebViewController {
444
564
  // if presentingViewController != nil {
445
565
  switch doneBarButtonItemPosition {
446
566
  case .left:
447
- if !leftNavigaionBarItemTypes.contains(where: { type in
567
+ if !leftNavigationBarItemTypes.contains(where: { type in
448
568
  switch type {
449
569
  case .done:
450
570
  return true
@@ -452,7 +572,7 @@ fileprivate extension WKWebViewController {
452
572
  return false
453
573
  }
454
574
  }) {
455
- leftNavigaionBarItemTypes.insert(.done, at: 0)
575
+ leftNavigationBarItemTypes.insert(.done, at: 0)
456
576
  }
457
577
  case .right:
458
578
  if !rightNavigaionBarItemTypes.contains(where: { type in
@@ -470,7 +590,7 @@ fileprivate extension WKWebViewController {
470
590
  }
471
591
  // }
472
592
 
473
- navigationItem.leftBarButtonItems = leftNavigaionBarItemTypes.map {
593
+ navigationItem.leftBarButtonItems = leftNavigationBarItemTypes.map {
474
594
  barButtonItemType in
475
595
  if let barButtonItem = barButtonItem(barButtonItemType) {
476
596
  return barButtonItem
@@ -478,13 +598,17 @@ fileprivate extension WKWebViewController {
478
598
  return UIBarButtonItem()
479
599
  }
480
600
 
481
- navigationItem.rightBarButtonItems = rightNavigaionBarItemTypes.map {
601
+ var rightBarButtons = rightNavigaionBarItemTypes.map {
482
602
  barButtonItemType in
483
603
  if let barButtonItem = barButtonItem(barButtonItemType) {
484
604
  return barButtonItem
485
605
  }
486
606
  return UIBarButtonItem()
487
607
  }
608
+ if rightBarButtons.count == 1 && buttonNearDoneIcon != nil && rightBarButtons[0] == doneBarButtonItem {
609
+ rightBarButtons.append(UIBarButtonItem(image: buttonNearDoneIcon, style: .plain, target: self, action: #selector(buttonNearDoneDidClick)))
610
+ }
611
+ navigationItem.rightBarButtonItems = rightBarButtons
488
612
 
489
613
  if toolbarItemTypes.count > 0 {
490
614
  for index in 0..<toolbarItemTypes.count - 1 {
@@ -492,13 +616,14 @@ fileprivate extension WKWebViewController {
492
616
  }
493
617
  }
494
618
 
495
- setToolbarItems(toolbarItemTypes.map {
619
+ let gen = toolbarItemTypes.map {
496
620
  barButtonItemType -> UIBarButtonItem in
497
621
  if let barButtonItem = barButtonItem(barButtonItemType) {
498
622
  return barButtonItem
499
623
  }
500
624
  return UIBarButtonItem()
501
- }, animated: true)
625
+ }
626
+ setToolbarItems(gen, animated: true)
502
627
  }
503
628
 
504
629
  func updateBarButtonItems() {
@@ -617,6 +742,10 @@ fileprivate extension WKWebViewController {
617
742
  webView?.goForward()
618
743
  }
619
744
 
745
+ @objc func buttonNearDoneDidClick(sender: AnyObject) {
746
+ self.capBrowserPlugin?.notifyListeners("buttonNearDoneClick", data: [:])
747
+ }
748
+
620
749
  @objc func reloadDidClick(sender: AnyObject) {
621
750
  webView?.stopLoading()
622
751
  if webView?.url != nil {
@@ -666,13 +795,14 @@ fileprivate extension WKWebViewController {
666
795
  self.present(alert, animated: true, completion: nil)
667
796
  } else {
668
797
  let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
798
+ #imageLiteral(resourceName: "simulator_screenshot_B8B44B8D-EB30-425C-9BF4-1F37697A8459.png")
669
799
  activityViewController.setValue(self.shareSubject ?? self.title, forKey: "subject")
670
800
  activityViewController.popoverPresentationController?.barButtonItem = (sender as! UIBarButtonItem)
671
801
  self.present(activityViewController, animated: true, completion: nil)
672
802
  }
673
803
  }
674
804
 
675
- @objc func doneDidClick(sender: AnyObject) {
805
+ func closeView () {
676
806
  var canDismiss = true
677
807
  if let url = self.source?.url {
678
808
  canDismiss = delegate?.webViewController?(self, canDismiss: url) ?? true
@@ -684,6 +814,21 @@ fileprivate extension WKWebViewController {
684
814
  }
685
815
  }
686
816
 
817
+ @objc func doneDidClick(sender: AnyObject) {
818
+ // check if closeModal is true, if true display alert before close
819
+ if self.closeModal {
820
+ let alert = UIAlertController(title: self.closeModalTitle, message: self.closeModalDescription, preferredStyle: UIAlertController.Style.alert)
821
+ alert.addAction(UIAlertAction(title: self.closeModalOk, style: UIAlertAction.Style.default, handler: { _ in
822
+ self.closeView()
823
+ }))
824
+ alert.addAction(UIAlertAction(title: self.closeModalCancel, style: UIAlertAction.Style.default, handler: nil))
825
+ self.present(alert, animated: true, completion: nil)
826
+ } else {
827
+ self.closeView()
828
+ }
829
+
830
+ }
831
+
687
832
  @objc func customDidClick(sender: BlockBarButtonItem) {
688
833
  sender.block?(self)
689
834
  }
@@ -693,11 +838,59 @@ fileprivate extension WKWebViewController {
693
838
 
694
839
  // MARK: - WKUIDelegate
695
840
  extension WKWebViewController: WKUIDelegate {
696
-
841
+ public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
842
+ // Ensure UI updates are on the main thread
843
+ DispatchQueue.main.async {
844
+ let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
845
+ self.present(alertController, animated: true, completion: nil)
846
+ }
847
+ completionHandler()
848
+ }
697
849
  }
698
850
 
699
851
  // MARK: - WKNavigationDelegate
700
852
  extension WKWebViewController: WKNavigationDelegate {
853
+ internal func injectPreShowScript() {
854
+ if preShowSemaphore != nil {
855
+ return
856
+ }
857
+
858
+ // TODO: implement interface
859
+ let script = """
860
+ async function preShowFunction() {
861
+ \(self.preShowScript ?? "")
862
+ };
863
+ preShowFunction().then(
864
+ () => window.webkit.messageHandlers.preShowScriptSuccess.postMessage({})
865
+ ).catch(
866
+ err => {
867
+ console.error('Preshow error', err);
868
+ window.webkit.messageHandlers.preShowScriptError.postMessage(JSON.stringify(err, Object.getOwnPropertyNames(err)));
869
+ }
870
+ )
871
+ """
872
+ print("[InAppBrowser - InjectPreShowScript] PreShowScript script: \(script)")
873
+
874
+ self.preShowSemaphore = DispatchSemaphore(value: 0)
875
+ self.executeScript(script: script) // this will run on the main thread
876
+
877
+ defer {
878
+ self.preShowSemaphore = nil
879
+ self.preShowError = nil
880
+ }
881
+
882
+ if self.preShowSemaphore?.wait(timeout: .now() + 10) == .timedOut {
883
+ print("[InAppBrowser - InjectPreShowScript] PreShowScript running for over 10 seconds. The plugin will not wait any longer!")
884
+ return
885
+ }
886
+
887
+ // "async function preShowFunction() {\n" +
888
+ // self.preShowScript + "\n" +
889
+ // "};\n" +
890
+ // "preShowFunction().then(() => window.PreShowScriptInterface.success()).catch(err => { console.error('Preshow error', err); window.PreShowScriptInterface.error(JSON.stringify(err, Object.getOwnPropertyNames(err))) })";
891
+
892
+ }
893
+
701
894
  public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
702
895
  updateBarButtonItems()
703
896
  self.progressView?.progress = 0
@@ -708,7 +901,21 @@ extension WKWebViewController: WKNavigationDelegate {
708
901
  }
709
902
  public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
710
903
  if !didpageInit && self.capBrowserPlugin?.isPresentAfterPageLoad == true {
711
- self.capBrowserPlugin?.presentView()
904
+ // injectPreShowScript will block, don't execute on the main thread
905
+ if self.preShowScript != nil && !self.preShowScript!.isEmpty {
906
+ DispatchQueue.global(qos: .userInitiated).async {
907
+ self.injectPreShowScript()
908
+ DispatchQueue.main.async { [weak self] in
909
+ self?.capBrowserPlugin?.presentView()
910
+ }
911
+ }
912
+ } else {
913
+ self.capBrowserPlugin?.presentView()
914
+ }
915
+ } else if self.preShowScript != nil && !self.preShowScript!.isEmpty && self.capBrowserPlugin?.isPresentAfterPageLoad == true {
916
+ DispatchQueue.global(qos: .userInitiated).async {
917
+ self.injectPreShowScript()
918
+ }
712
919
  }
713
920
  didpageInit = true
714
921
  updateBarButtonItems()
@@ -717,6 +924,8 @@ extension WKWebViewController: WKNavigationDelegate {
717
924
  self.url = url
718
925
  delegate?.webViewController?(self, didFinish: url)
719
926
  }
927
+ self.injectJavaScriptInterface()
928
+ self.capBrowserPlugin?.notifyListeners("browserPageLoaded", data: [:])
720
929
  }
721
930
 
722
931
  public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
@@ -726,6 +935,7 @@ extension WKWebViewController: WKNavigationDelegate {
726
935
  self.url = url
727
936
  delegate?.webViewController?(self, didFail: url, withError: error)
728
937
  }
938
+ self.capBrowserPlugin?.notifyListeners("pageLoadError", data: [:])
729
939
  }
730
940
 
731
941
  public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
@@ -735,46 +945,76 @@ extension WKWebViewController: WKNavigationDelegate {
735
945
  self.url = url
736
946
  delegate?.webViewController?(self, didFail: url, withError: error)
737
947
  }
948
+ self.capBrowserPlugin?.notifyListeners("pageLoadError", data: [:])
738
949
  }
739
950
 
740
951
  public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
741
- if let bypassedSSLHosts = bypassedSSLHosts, bypassedSSLHosts.contains(challenge.protectionSpace.host) {
952
+ if let credentials = credentials,
953
+ challenge.protectionSpace.receivesCredentialSecurely,
954
+ let url = webView.url, challenge.protectionSpace.host == url.host, challenge.protectionSpace.protocol == url.scheme, challenge.protectionSpace.port == url.port ?? (url.scheme == "https" ? 443 : url.scheme == "http" ? 80 : nil) {
955
+ let urlCredential = URLCredential(user: credentials.username, password: credentials.password, persistence: .none)
956
+ completionHandler(.useCredential, urlCredential)
957
+ } else if let bypassedSSLHosts = bypassedSSLHosts, bypassedSSLHosts.contains(challenge.protectionSpace.host) {
742
958
  let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
743
959
  completionHandler(.useCredential, credential)
744
960
  } else {
745
- completionHandler(.performDefaultHandling, nil)
961
+ guard self.ignoreUntrustedSSLError else {
962
+ completionHandler(.performDefaultHandling, nil)
963
+ return
964
+ }
965
+ /* allows to open links with self-signed certificates
966
+ Follow Apple's guidelines https://developer.apple.com/documentation/foundation/url_loading_system/handling_an_authentication_challenge/performing_manual_server_trust_authentication
967
+ */
968
+ guard let serverTrust = challenge.protectionSpace.serverTrust else {
969
+ completionHandler(.useCredential, nil)
970
+ return
971
+ }
972
+ let credential = URLCredential(trust: serverTrust)
973
+ completionHandler(.useCredential, credential)
746
974
  }
975
+ self.injectJavaScriptInterface()
747
976
  }
748
977
 
749
978
  public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
750
979
  var actionPolicy: WKNavigationActionPolicy = .allow
751
- defer {
752
- decisionHandler(actionPolicy)
980
+
981
+ if self.preventDeeplink {
982
+ actionPolicy = .preventDeeplinkActionPolicy
753
983
  }
984
+
754
985
  guard let u = navigationAction.request.url else {
755
- print("Cannot handle empty URLs")
986
+ decisionHandler(actionPolicy)
987
+ return
988
+ }
989
+
990
+ // Check if the URL is an App Store URL
991
+ if u.absoluteString.contains("apps.apple.com") {
992
+ UIApplication.shared.open(u, options: [:], completionHandler: nil)
993
+ // Cancel the navigation in the web view
994
+ decisionHandler(.cancel)
756
995
  return
757
996
  }
758
997
 
759
998
  if !self.allowsFileURL && u.isFileURL {
760
999
  print("Cannot handle file URLs")
1000
+ decisionHandler(.cancel)
761
1001
  return
762
1002
  }
763
1003
 
764
1004
  if handleURLWithApp(u, targetFrame: navigationAction.targetFrame) {
765
1005
  actionPolicy = .cancel
766
- return
767
1006
  }
768
1007
 
769
1008
  if u.host == self.source?.url?.host, let cookies = availableCookies, !checkRequestCookies(navigationAction.request, cookies: cookies) {
770
1009
  self.load(remote: u)
771
1010
  actionPolicy = .cancel
772
- return
773
1011
  }
774
1012
 
775
1013
  if let navigationType = NavigationType(rawValue: navigationAction.navigationType.rawValue), let result = delegate?.webViewController?(self, decidePolicy: u, navigationType: navigationType) {
776
1014
  actionPolicy = result ? .allow : .cancel
777
1015
  }
1016
+ self.injectJavaScriptInterface()
1017
+ decisionHandler(actionPolicy)
778
1018
  }
779
1019
  }
780
1020
 
@@ -782,3 +1022,7 @@ class BlockBarButtonItem: UIBarButtonItem {
782
1022
 
783
1023
  var block: ((WKWebViewController) -> Void)?
784
1024
  }
1025
+
1026
+ extension WKNavigationActionPolicy {
1027
+ static let preventDeeplinkActionPolicy = WKNavigationActionPolicy(rawValue: WKNavigationActionPolicy.allow.rawValue + 2)!
1028
+ }