@capawesome/capacitor-in-app-browser 0.0.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.
Files changed (60) hide show
  1. package/CapawesomeCapacitorInAppBrowser.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +690 -0
  5. package/android/build.gradle +60 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/InAppBrowser.java +242 -0
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/InAppBrowserPlugin.java +289 -0
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/CustomException.java +20 -0
  10. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/CustomExceptions.java +20 -0
  11. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/WebViewDialog.java +343 -0
  12. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/events/BrowserPageNavigationCompletedEvent.java +23 -0
  13. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/events/MessageReceivedEvent.java +23 -0
  14. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/options/ExecuteScriptOptions.java +29 -0
  15. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/options/GetCookiesOptions.java +29 -0
  16. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/options/OpenInExternalBrowserOptions.java +30 -0
  17. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/options/OpenInSystemBrowserOptions.java +74 -0
  18. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/options/OpenInWebViewOptions.java +112 -0
  19. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/options/PostMessageOptions.java +30 -0
  20. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/options/WebViewToolbarOptions.java +87 -0
  21. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/results/ExecuteScriptResult.java +25 -0
  22. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/classes/results/GetCookiesResult.java +28 -0
  23. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/interfaces/Callback.java +5 -0
  24. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/interfaces/EmptyCallback.java +5 -0
  25. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/interfaces/NonEmptyResultCallback.java +7 -0
  26. package/android/src/main/java/io/capawesome/capacitorjs/plugins/inappbrowser/interfaces/Result.java +7 -0
  27. package/android/src/main/res/.gitkeep +0 -0
  28. package/dist/docs.json +1173 -0
  29. package/dist/esm/definitions.d.ts +540 -0
  30. package/dist/esm/definitions.js +19 -0
  31. package/dist/esm/definitions.js.map +1 -0
  32. package/dist/esm/index.d.ts +4 -0
  33. package/dist/esm/index.js +7 -0
  34. package/dist/esm/index.js.map +1 -0
  35. package/dist/esm/web.d.ts +14 -0
  36. package/dist/esm/web.js +35 -0
  37. package/dist/esm/web.js.map +1 -0
  38. package/dist/plugin.cjs.js +68 -0
  39. package/dist/plugin.cjs.js.map +1 -0
  40. package/dist/plugin.js +71 -0
  41. package/dist/plugin.js.map +1 -0
  42. package/ios/Plugin/Classes/Events/BrowserPageNavigationCompletedEvent.swift +16 -0
  43. package/ios/Plugin/Classes/Events/MessageReceivedEvent.swift +24 -0
  44. package/ios/Plugin/Classes/Options/ExecuteScriptOptions.swift +17 -0
  45. package/ios/Plugin/Classes/Options/GetCookiesOptions.swift +20 -0
  46. package/ios/Plugin/Classes/Options/OpenInExternalBrowserOptions.swift +20 -0
  47. package/ios/Plugin/Classes/Options/OpenInSystemBrowserOptions.swift +52 -0
  48. package/ios/Plugin/Classes/Options/OpenInWebViewOptions.swift +48 -0
  49. package/ios/Plugin/Classes/Options/PostMessageOptions.swift +17 -0
  50. package/ios/Plugin/Classes/Options/WebViewToolbarOptions.swift +37 -0
  51. package/ios/Plugin/Classes/Results/ExecuteScriptResult.swift +25 -0
  52. package/ios/Plugin/Classes/Results/GetCookiesResult.swift +20 -0
  53. package/ios/Plugin/Classes/WebViewController.swift +224 -0
  54. package/ios/Plugin/Enums/CustomError.swift +49 -0
  55. package/ios/Plugin/InAppBrowser.swift +194 -0
  56. package/ios/Plugin/InAppBrowserHelper.swift +49 -0
  57. package/ios/Plugin/InAppBrowserPlugin.swift +191 -0
  58. package/ios/Plugin/Info.plist +24 -0
  59. package/ios/Plugin/Protocols/Result.swift +5 -0
  60. package/package.json +91 -0
@@ -0,0 +1,224 @@
1
+ import Foundation
2
+ import UIKit
3
+ import WebKit
4
+
5
+ @objc public class WebViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate {
6
+ var onClosed: ((WebViewController) -> Void)?
7
+ var onMessageReceived: ((String) -> Void)?
8
+ var onPageLoaded: (() -> Void)?
9
+ var onPageNavigationCompleted: ((String) -> Void)?
10
+
11
+ private static let bridgeJavaScript =
12
+ "window.CapacitorInAppBrowser = window.CapacitorInAppBrowser || { postMessage: function (data) "
13
+ + "{ window.webkit.messageHandlers.capacitorInAppBrowser.postMessage(JSON.stringify(data)); } };"
14
+ private static let messageHandlerName = "capacitorInAppBrowser"
15
+
16
+ private let options: OpenInWebViewOptions
17
+
18
+ private var backButtonItem: UIBarButtonItem?
19
+ private var canGoBackObservation: NSKeyValueObservation?
20
+ private var canGoForwardObservation: NSKeyValueObservation?
21
+ private var forwardButtonItem: UIBarButtonItem?
22
+ private var initialLoadNotified = false
23
+ private var titleObservation: NSKeyValueObservation?
24
+ private var urlObservation: NSKeyValueObservation?
25
+ private var webView: WKWebView?
26
+
27
+ init(options: OpenInWebViewOptions) {
28
+ self.options = options
29
+ super.init(nibName: nil, bundle: nil)
30
+ }
31
+
32
+ required init?(coder: NSCoder) {
33
+ fatalError("init(coder:) has not been implemented")
34
+ }
35
+
36
+ public func executeScript(_ script: String, completion: @escaping (_ result: Any?, _ error: Error?) -> Void) {
37
+ guard let webView = webView else {
38
+ completion(nil, nil)
39
+ return
40
+ }
41
+ webView.evaluateJavaScript(script) { result, error in
42
+ completion(result, error)
43
+ }
44
+ }
45
+
46
+ public func postMessage(_ data: String) {
47
+ webView?.evaluateJavaScript(
48
+ "window.dispatchEvent(new CustomEvent('capacitorInAppBrowserMessage', { detail: \(data) }));",
49
+ completionHandler: nil
50
+ )
51
+ }
52
+
53
+ public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
54
+ if let data = message.body as? String {
55
+ onMessageReceived?(data)
56
+ }
57
+ }
58
+
59
+ override public func viewDidDisappear(_ animated: Bool) {
60
+ super.viewDidDisappear(animated)
61
+ if isBeingDismissed || navigationController?.isBeingDismissed == true {
62
+ cleanup()
63
+ onClosed?(self)
64
+ }
65
+ }
66
+
67
+ override public func viewDidLoad() {
68
+ super.viewDidLoad()
69
+ view.backgroundColor = .systemBackground
70
+ let webView = createWebView()
71
+ self.webView = webView
72
+ view.addSubview(webView)
73
+ NSLayoutConstraint.activate([
74
+ webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
75
+ webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
76
+ webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
77
+ webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
78
+ ])
79
+ configureNavigationBar()
80
+ loadUrl()
81
+ }
82
+
83
+ public func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
84
+ if navigationAction.targetFrame == nil {
85
+ webView.load(navigationAction.request)
86
+ }
87
+ return nil
88
+ }
89
+
90
+ public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
91
+ updateNavigationButtons()
92
+ updateTitle()
93
+ if !initialLoadNotified {
94
+ initialLoadNotified = true
95
+ onPageLoaded?()
96
+ }
97
+ if let url = webView.url?.absoluteString {
98
+ onPageNavigationCompleted?(url)
99
+ }
100
+ }
101
+
102
+ public func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
103
+ decisionHandler(.grant)
104
+ }
105
+
106
+ private func cleanup() {
107
+ webView?.configuration.userContentController.removeScriptMessageHandler(forName: Self.messageHandlerName)
108
+ }
109
+
110
+ private func configureNavigationBar() {
111
+ let closeButtonItem = UIBarButtonItem(
112
+ title: options.toolbar.closeButtonText,
113
+ style: .plain,
114
+ target: self,
115
+ action: #selector(handleClose)
116
+ )
117
+ navigationItem.leftBarButtonItem = closeButtonItem
118
+ if options.toolbar.showNavigationButtons {
119
+ let backButtonItem = UIBarButtonItem(
120
+ image: UIImage(systemName: "chevron.left"),
121
+ style: .plain,
122
+ target: self,
123
+ action: #selector(handleGoBack)
124
+ )
125
+ let forwardButtonItem = UIBarButtonItem(
126
+ image: UIImage(systemName: "chevron.right"),
127
+ style: .plain,
128
+ target: self,
129
+ action: #selector(handleGoForward)
130
+ )
131
+ navigationItem.rightBarButtonItems = [forwardButtonItem, backButtonItem]
132
+ self.backButtonItem = backButtonItem
133
+ self.forwardButtonItem = forwardButtonItem
134
+ updateNavigationButtons()
135
+ }
136
+ if let backgroundColor = options.toolbar.backgroundColor {
137
+ let appearance = UINavigationBarAppearance()
138
+ appearance.configureWithOpaqueBackground()
139
+ appearance.backgroundColor = backgroundColor
140
+ if let color = options.toolbar.color {
141
+ appearance.titleTextAttributes = [.foregroundColor: color]
142
+ }
143
+ navigationController?.navigationBar.standardAppearance = appearance
144
+ navigationController?.navigationBar.scrollEdgeAppearance = appearance
145
+ navigationController?.navigationBar.compactAppearance = appearance
146
+ }
147
+ if let color = options.toolbar.color {
148
+ navigationController?.navigationBar.tintColor = color
149
+ }
150
+ updateTitle()
151
+ }
152
+
153
+ private func createWebView() -> WKWebView {
154
+ let configuration = WKWebViewConfiguration()
155
+ configuration.allowsInlineMediaPlayback = true
156
+ configuration.mediaTypesRequiringUserActionForPlayback = options.mediaPlaybackRequiresUserAction ? .all : []
157
+ configuration.websiteDataStore = options.isolatedDataStore ? .nonPersistent() : .default()
158
+ let userContentController = WKUserContentController()
159
+ userContentController.addUserScript(WKUserScript(source: Self.bridgeJavaScript, injectionTime: .atDocumentStart, forMainFrameOnly: true))
160
+ userContentController.add(self, name: Self.messageHandlerName)
161
+ configuration.userContentController = userContentController
162
+ let webView = WKWebView(frame: .zero, configuration: configuration)
163
+ webView.translatesAutoresizingMaskIntoConstraints = false
164
+ webView.navigationDelegate = self
165
+ webView.uiDelegate = self
166
+ webView.allowsBackForwardNavigationGestures = options.allowsBackForwardNavigationGestures
167
+ webView.scrollView.bounces = options.overscroll
168
+ if let userAgent = options.userAgent {
169
+ webView.customUserAgent = userAgent
170
+ }
171
+ canGoBackObservation = webView.observe(\.canGoBack, options: [.new]) { [weak self] _, _ in
172
+ self?.updateNavigationButtons()
173
+ }
174
+ canGoForwardObservation = webView.observe(\.canGoForward, options: [.new]) { [weak self] _, _ in
175
+ self?.updateNavigationButtons()
176
+ }
177
+ titleObservation = webView.observe(\.title, options: [.new]) { [weak self] _, _ in
178
+ self?.updateTitle()
179
+ }
180
+ urlObservation = webView.observe(\.url, options: [.new]) { [weak self] _, _ in
181
+ self?.updateTitle()
182
+ }
183
+ return webView
184
+ }
185
+
186
+ @objc private func handleClose() {
187
+ dismiss(animated: true)
188
+ }
189
+
190
+ @objc private func handleGoBack() {
191
+ if webView?.canGoBack == true {
192
+ webView?.goBack()
193
+ }
194
+ }
195
+
196
+ @objc private func handleGoForward() {
197
+ if webView?.canGoForward == true {
198
+ webView?.goForward()
199
+ }
200
+ }
201
+
202
+ private func loadUrl() {
203
+ var request = URLRequest(url: options.url)
204
+ for (key, value) in options.headers {
205
+ request.setValue(value, forHTTPHeaderField: key)
206
+ }
207
+ webView?.load(request)
208
+ }
209
+
210
+ private func updateNavigationButtons() {
211
+ backButtonItem?.isEnabled = webView?.canGoBack == true
212
+ forwardButtonItem?.isEnabled = webView?.canGoForward == true
213
+ }
214
+
215
+ private func updateTitle() {
216
+ if let title = options.toolbar.title {
217
+ navigationItem.title = title
218
+ } else if options.toolbar.showUrl {
219
+ navigationItem.title = webView?.url?.absoluteString
220
+ } else {
221
+ navigationItem.title = webView?.title
222
+ }
223
+ }
224
+ }
@@ -0,0 +1,49 @@
1
+ import Foundation
2
+
3
+ enum CustomError: Error {
4
+ case backgroundColorInvalid
5
+ case browserNotFound
6
+ case colorInvalid
7
+ case dataMissing
8
+ case noBrowserOpen
9
+ case scriptMissing
10
+ case toolbarColorInvalid
11
+ case urlInvalid
12
+ case urlMissing
13
+
14
+ var code: String? {
15
+ switch self {
16
+ case .browserNotFound:
17
+ return "BROWSER_NOT_FOUND"
18
+ case .noBrowserOpen:
19
+ return "NO_BROWSER_OPEN"
20
+ default:
21
+ return nil
22
+ }
23
+ }
24
+ }
25
+
26
+ extension CustomError: LocalizedError {
27
+ public var errorDescription: String? {
28
+ switch self {
29
+ case .backgroundColorInvalid:
30
+ return NSLocalizedString("backgroundColor must be a valid hex color code.", comment: "backgroundColorInvalid")
31
+ case .browserNotFound:
32
+ return NSLocalizedString("No app was found on the device that can open the URL.", comment: "browserNotFound")
33
+ case .colorInvalid:
34
+ return NSLocalizedString("color must be a valid hex color code.", comment: "colorInvalid")
35
+ case .dataMissing:
36
+ return NSLocalizedString("data must be provided.", comment: "dataMissing")
37
+ case .noBrowserOpen:
38
+ return NSLocalizedString("No browser is currently open.", comment: "noBrowserOpen")
39
+ case .scriptMissing:
40
+ return NSLocalizedString("script must be provided.", comment: "scriptMissing")
41
+ case .toolbarColorInvalid:
42
+ return NSLocalizedString("toolbarColor must be a valid hex color code.", comment: "toolbarColorInvalid")
43
+ case .urlInvalid:
44
+ return NSLocalizedString("url is invalid.", comment: "urlInvalid")
45
+ case .urlMissing:
46
+ return NSLocalizedString("url must be provided.", comment: "urlMissing")
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,194 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import SafariServices
4
+ import UIKit
5
+ import WebKit
6
+
7
+ @objc public class InAppBrowser: NSObject, SFSafariViewControllerDelegate {
8
+ let plugin: InAppBrowserPlugin
9
+
10
+ private var safariViewController: SFSafariViewController?
11
+ private var webViewController: WebViewController?
12
+
13
+ init(plugin: InAppBrowserPlugin) {
14
+ self.plugin = plugin
15
+ }
16
+
17
+ @objc public func clearCache(completion: @escaping (_ error: Error?) -> Void) {
18
+ DispatchQueue.main.async {
19
+ let types: Set<String> = [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache]
20
+ WKWebsiteDataStore.default().removeData(ofTypes: types, modifiedSince: .distantPast) {
21
+ completion(nil)
22
+ }
23
+ }
24
+ }
25
+
26
+ @objc public func clearSessionData(completion: @escaping (_ error: Error?) -> Void) {
27
+ DispatchQueue.main.async {
28
+ let types: Set<String> = [WKWebsiteDataTypeCookies, WKWebsiteDataTypeLocalStorage, WKWebsiteDataTypeSessionStorage]
29
+ WKWebsiteDataStore.default().removeData(ofTypes: types, modifiedSince: .distantPast) {
30
+ completion(nil)
31
+ }
32
+ }
33
+ }
34
+
35
+ @objc public func close(completion: @escaping (_ error: Error?) -> Void) {
36
+ DispatchQueue.main.async {
37
+ if let webViewController = self.webViewController {
38
+ webViewController.dismiss(animated: true) {
39
+ completion(nil)
40
+ }
41
+ return
42
+ }
43
+ if let safariViewController = self.safariViewController {
44
+ safariViewController.dismiss(animated: true) {
45
+ self.safariViewController = nil
46
+ self.plugin.notifyBrowserClosedListeners()
47
+ completion(nil)
48
+ }
49
+ return
50
+ }
51
+ completion(CustomError.noBrowserOpen)
52
+ }
53
+ }
54
+
55
+ @objc public func executeScript(_ options: ExecuteScriptOptions, completion: @escaping (_ result: ExecuteScriptResult?, _ error: Error?) -> Void) {
56
+ DispatchQueue.main.async {
57
+ guard let webViewController = self.webViewController else {
58
+ completion(nil, CustomError.noBrowserOpen)
59
+ return
60
+ }
61
+ webViewController.executeScript(options.script) { result, _ in
62
+ completion(ExecuteScriptResult(value: result), nil)
63
+ }
64
+ }
65
+ }
66
+
67
+ @objc public func getCookies(_ options: GetCookiesOptions, completion: @escaping (_ result: GetCookiesResult?, _ error: Error?) -> Void) {
68
+ DispatchQueue.main.async {
69
+ WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in
70
+ var result: [String: String] = [:]
71
+ for cookie in cookies where InAppBrowserHelper.isCookie(cookie, matchingURL: options.url) {
72
+ result[cookie.name] = cookie.value
73
+ }
74
+ completion(GetCookiesResult(cookies: result), nil)
75
+ }
76
+ }
77
+ }
78
+
79
+ @objc public func openInExternalBrowser(_ options: OpenInExternalBrowserOptions, completion: @escaping (_ error: Error?) -> Void) {
80
+ DispatchQueue.main.async {
81
+ UIApplication.shared.open(options.url, options: [:]) { success in
82
+ completion(success ? nil : CustomError.browserNotFound)
83
+ }
84
+ }
85
+ }
86
+
87
+ @objc public func openInSystemBrowser(_ options: OpenInSystemBrowserOptions, completion: @escaping (_ error: Error?) -> Void) {
88
+ DispatchQueue.main.async {
89
+ self.dismissActiveBrowser {
90
+ guard let bridgeViewController = self.plugin.bridge?.viewController else {
91
+ completion(CustomError.noBrowserOpen)
92
+ return
93
+ }
94
+ let configuration = SFSafariViewController.Configuration()
95
+ configuration.barCollapsingEnabled = options.barCollapsing
96
+ configuration.entersReaderIfAvailable = options.readerMode
97
+ let safariViewController = SFSafariViewController(url: options.url, configuration: configuration)
98
+ safariViewController.delegate = self
99
+ safariViewController.dismissButtonStyle = options.dismissButtonStyle
100
+ if let toolbarColor = options.toolbarColor {
101
+ safariViewController.preferredBarTintColor = toolbarColor
102
+ }
103
+ self.safariViewController = safariViewController
104
+ bridgeViewController.present(safariViewController, animated: true) {
105
+ completion(nil)
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ @objc public func openInWebView(_ options: OpenInWebViewOptions, completion: @escaping (_ error: Error?) -> Void) {
112
+ DispatchQueue.main.async {
113
+ self.dismissActiveBrowser {
114
+ guard let bridgeViewController = self.plugin.bridge?.viewController else {
115
+ completion(CustomError.noBrowserOpen)
116
+ return
117
+ }
118
+ let webViewController = WebViewController(options: options)
119
+ webViewController.onClosed = { [weak self] viewController in
120
+ guard let self = self else {
121
+ return
122
+ }
123
+ if self.webViewController === viewController {
124
+ self.webViewController = nil
125
+ }
126
+ self.plugin.notifyBrowserClosedListeners()
127
+ }
128
+ webViewController.onMessageReceived = { [weak self] data in
129
+ self?.handleMessageReceived(data)
130
+ }
131
+ webViewController.onPageLoaded = { [weak self] in
132
+ self?.plugin.notifyBrowserPageLoadedListeners()
133
+ }
134
+ webViewController.onPageNavigationCompleted = { [weak self] url in
135
+ self?.plugin.notifyBrowserPageNavigationCompletedListeners(BrowserPageNavigationCompletedEvent(url: url))
136
+ }
137
+ let navigationController = UINavigationController(rootViewController: webViewController)
138
+ navigationController.modalPresentationStyle = .fullScreen
139
+ navigationController.setNavigationBarHidden(!options.toolbar.visible, animated: false)
140
+ self.webViewController = webViewController
141
+ bridgeViewController.present(navigationController, animated: true) {
142
+ completion(nil)
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ @objc public func postMessage(_ options: PostMessageOptions, completion: @escaping (_ error: Error?) -> Void) {
149
+ DispatchQueue.main.async {
150
+ guard let webViewController = self.webViewController else {
151
+ completion(CustomError.noBrowserOpen)
152
+ return
153
+ }
154
+ let data = (try? JSONSerialization.data(withJSONObject: options.data)).flatMap { String(data: $0, encoding: .utf8) } ?? "{}"
155
+ webViewController.postMessage(data)
156
+ completion(nil)
157
+ }
158
+ }
159
+
160
+ public func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
161
+ if didLoadSuccessfully {
162
+ plugin.notifyBrowserPageLoadedListeners()
163
+ }
164
+ }
165
+
166
+ public func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
167
+ safariViewController = nil
168
+ plugin.notifyBrowserClosedListeners()
169
+ }
170
+
171
+ private func dismissActiveBrowser(_ completion: @escaping () -> Void) {
172
+ if let webViewController = self.webViewController {
173
+ webViewController.dismiss(animated: false) {
174
+ completion()
175
+ }
176
+ } else if let safariViewController = self.safariViewController {
177
+ safariViewController.dismiss(animated: false) {
178
+ self.safariViewController = nil
179
+ self.plugin.notifyBrowserClosedListeners()
180
+ completion()
181
+ }
182
+ } else {
183
+ completion()
184
+ }
185
+ }
186
+
187
+ private func handleMessageReceived(_ data: String) {
188
+ var value: Any = data
189
+ if let jsonData = data.data(using: .utf8), let parsedValue = try? JSONSerialization.jsonObject(with: jsonData, options: [.fragmentsAllowed]) {
190
+ value = parsedValue
191
+ }
192
+ plugin.notifyMessageReceivedListeners(MessageReceivedEvent(data: value))
193
+ }
194
+ }
@@ -0,0 +1,49 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ public class InAppBrowserHelper {
5
+ public static func isCookie(_ cookie: HTTPCookie, matchingURL url: URL) -> Bool {
6
+ guard let host = url.host else {
7
+ return false
8
+ }
9
+ guard isHost(host, matchingDomain: cookie.domain) else {
10
+ return false
11
+ }
12
+ return isPath(url.path.isEmpty ? "/" : url.path, matchingCookiePath: cookie.path)
13
+ }
14
+
15
+ public static func parseColor(_ string: String) -> UIColor? {
16
+ guard string.hasPrefix("#") else {
17
+ return nil
18
+ }
19
+ let hex = String(string.dropFirst())
20
+ guard hex.count == 6 || hex.count == 8, let value = UInt64(hex, radix: 16) else {
21
+ return nil
22
+ }
23
+ let alpha = hex.count == 8 ? CGFloat((value >> 24) & 0xFF) / 255.0 : 1.0
24
+ let red = CGFloat((value >> 16) & 0xFF) / 255.0
25
+ let green = CGFloat((value >> 8) & 0xFF) / 255.0
26
+ let blue = CGFloat(value & 0xFF) / 255.0
27
+ return UIColor(red: red, green: green, blue: blue, alpha: alpha)
28
+ }
29
+
30
+ private static func isHost(_ host: String, matchingDomain domain: String) -> Bool {
31
+ let normalizedDomain = (domain.hasPrefix(".") ? String(domain.dropFirst()) : domain).lowercased()
32
+ let normalizedHost = host.lowercased()
33
+ return normalizedHost == normalizedDomain || normalizedHost.hasSuffix("." + normalizedDomain)
34
+ }
35
+
36
+ private static func isPath(_ path: String, matchingCookiePath cookiePath: String) -> Bool {
37
+ if path == cookiePath {
38
+ return true
39
+ }
40
+ guard path.hasPrefix(cookiePath) else {
41
+ return false
42
+ }
43
+ if cookiePath.hasSuffix("/") {
44
+ return true
45
+ }
46
+ let index = path.index(path.startIndex, offsetBy: cookiePath.count)
47
+ return path[index] == "/"
48
+ }
49
+ }