@exodus/react-native-webview 11.26.1-exodus.8 → 13.16.0-exodus.0

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 (118) hide show
  1. package/README.md +36 -63
  2. package/android/build.gradle +83 -110
  3. package/android/gradle.properties +3 -4
  4. package/android/src/main/AndroidManifest.xml +12 -0
  5. package/android/src/main/AndroidManifestNew.xml +26 -0
  6. package/android/src/main/java/com/reactnativecommunity/webview/RNCBasicAuthCredential.java +11 -0
  7. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebChromeClient.java +407 -0
  8. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java +468 -0
  9. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java +330 -0
  10. package/android/src/main/java/com/reactnativecommunity/webview/{WebViewConfig.java → RNCWebViewConfig.java} +3 -4
  11. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewFileProvider.java +1 -1
  12. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt +746 -0
  13. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewMessagingModule.kt +9 -0
  14. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModuleImpl.java +554 -0
  15. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java +57 -12
  16. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewWrapper.kt +39 -0
  17. package/android/src/main/java/com/reactnativecommunity/webview/events/SubResourceErrorEvent.kt +25 -0
  18. package/android/src/main/java/com/reactnativecommunity/webview/events/TopCustomMenuSelectionEvent.kt +24 -0
  19. package/android/src/main/java/com/reactnativecommunity/webview/events/TopHttpErrorEvent.kt +25 -0
  20. package/android/src/main/java/com/reactnativecommunity/webview/events/TopNewWindowEvent.kt +25 -0
  21. package/android/src/main/java/com/reactnativecommunity/webview/events/TopRenderProcessGoneEvent.kt +25 -0
  22. package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java +570 -0
  23. package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewModule.java +57 -0
  24. package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java +341 -0
  25. package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewModule.java +59 -0
  26. package/apple/RCTConvert+WKDataDetectorTypes.h +11 -0
  27. package/apple/RCTConvert+WKDataDetectorTypes.m +27 -0
  28. package/apple/RNCWebView.h +26 -100
  29. package/apple/RNCWebView.mm +555 -0
  30. package/apple/RNCWebViewDecisionManager.h +20 -0
  31. package/apple/RNCWebViewDecisionManager.m +47 -0
  32. package/apple/RNCWebViewImpl.h +164 -0
  33. package/apple/{RNCWebView.m → RNCWebViewImpl.m} +803 -226
  34. package/apple/RNCWebViewManager.h +4 -8
  35. package/apple/RNCWebViewManager.mm +221 -0
  36. package/apple/RNCWebViewModule.h +23 -0
  37. package/apple/RNCWebViewModule.mm +34 -0
  38. package/index.d.ts +2 -3
  39. package/lib/NativeRNCWebViewModule.d.ts +8 -0
  40. package/lib/NativeRNCWebViewModule.js +1 -0
  41. package/lib/RNCWebViewNativeComponent.d.ts +245 -0
  42. package/lib/RNCWebViewNativeComponent.js +1 -0
  43. package/lib/WebView.android.d.ts +0 -1
  44. package/lib/WebView.android.js +1 -135
  45. package/lib/WebView.d.ts +2 -3
  46. package/lib/WebView.ios.d.ts +0 -1
  47. package/lib/WebView.ios.js +1 -114
  48. package/lib/WebView.js +1 -11
  49. package/lib/WebView.macos.d.ts +6 -0
  50. package/lib/WebView.macos.js +1 -0
  51. package/lib/WebView.styles.d.ts +37 -11
  52. package/lib/WebView.styles.js +1 -33
  53. package/lib/WebView.windows.d.ts +17 -0
  54. package/lib/WebView.windows.js +1 -0
  55. package/lib/WebViewNativeComponent.macos.d.ts +3 -0
  56. package/lib/WebViewNativeComponent.macos.js +1 -0
  57. package/lib/WebViewNativeComponent.windows.d.ts +3 -0
  58. package/lib/WebViewNativeComponent.windows.js +1 -0
  59. package/lib/WebViewShared.d.ts +30 -9
  60. package/lib/WebViewShared.js +1 -174
  61. package/lib/WebViewTypes.d.ts +514 -98
  62. package/lib/WebViewTypes.js +1 -6
  63. package/lib/index.d.ts +0 -1
  64. package/lib/index.js +1 -3
  65. package/lib/validation.d.ts +3 -0
  66. package/lib/validation.js +1 -0
  67. package/package.json +57 -33
  68. package/react-native-webview.podspec +32 -5
  69. package/react-native.config.js +22 -18
  70. package/src/NativeRNCWebViewModule.ts +13 -0
  71. package/src/RNCWebViewNativeComponent.ts +348 -0
  72. package/src/WebView.android.tsx +345 -0
  73. package/src/WebView.ios.tsx +341 -0
  74. package/src/WebView.macos.tsx +252 -0
  75. package/src/WebView.styles.ts +41 -0
  76. package/src/WebView.tsx +25 -0
  77. package/src/WebView.windows.tsx +217 -0
  78. package/src/WebViewNativeComponent.macos.ts +7 -0
  79. package/src/WebViewNativeComponent.windows.ts +8 -0
  80. package/src/WebViewShared.tsx +476 -0
  81. package/src/WebViewTypes.ts +1402 -0
  82. package/src/__tests__/WebViewShared-test.js +323 -0
  83. package/src/__tests__/__snapshots__/WebViewShared-test.js.snap +8 -0
  84. package/src/__tests__/validation-test.js +38 -0
  85. package/src/index.ts +4 -0
  86. package/src/validation.ts +20 -0
  87. package/android/.editorconfig +0 -6
  88. package/android/.gradle/7.4.2/checksums/checksums.lock +0 -0
  89. package/android/.gradle/7.4.2/dependencies-accessors/dependencies-accessors.lock +0 -0
  90. package/android/.gradle/7.4.2/dependencies-accessors/gc.properties +0 -0
  91. package/android/.gradle/7.4.2/executionHistory/executionHistory.lock +0 -0
  92. package/android/.gradle/7.4.2/fileChanges/last-build.bin +0 -0
  93. package/android/.gradle/7.4.2/fileHashes/fileHashes.lock +0 -0
  94. package/android/.gradle/7.4.2/gc.properties +0 -0
  95. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  96. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  97. package/android/.gradle/vcs-1/gc.properties +0 -0
  98. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java +0 -1408
  99. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModule.java +0 -506
  100. package/apple/RNCWebViewManager.m +0 -278
  101. package/ios/Pods/Manifest.lock +0 -3
  102. package/ios/Pods/Pods.xcodeproj/project.pbxproj +0 -397
  103. package/ios/Pods/Pods.xcodeproj/xcuserdata/gabrielezenwankwo.xcuserdatad/xcschemes/Pods-RNCWebView.xcscheme +0 -58
  104. package/ios/Pods/Pods.xcodeproj/xcuserdata/gabrielezenwankwo.xcuserdatad/xcschemes/xcschememanagement.plist +0 -16
  105. package/ios/Pods/Target Support Files/Pods-RNCWebView/Pods-RNCWebView-Info.plist +0 -26
  106. package/ios/Pods/Target Support Files/Pods-RNCWebView/Pods-RNCWebView-acknowledgements.markdown +0 -3
  107. package/ios/Pods/Target Support Files/Pods-RNCWebView/Pods-RNCWebView-acknowledgements.plist +0 -29
  108. package/ios/Pods/Target Support Files/Pods-RNCWebView/Pods-RNCWebView-dummy.m +0 -5
  109. package/ios/Pods/Target Support Files/Pods-RNCWebView/Pods-RNCWebView-umbrella.h +0 -16
  110. package/ios/Pods/Target Support Files/Pods-RNCWebView/Pods-RNCWebView.debug.xcconfig +0 -8
  111. package/ios/Pods/Target Support Files/Pods-RNCWebView/Pods-RNCWebView.modulemap +0 -6
  112. package/ios/Pods/Target Support Files/Pods-RNCWebView/Pods-RNCWebView.release.xcconfig +0 -8
  113. package/lib/UpdateOS.d.ts +0 -6
  114. package/lib/UpdateOS.js +0 -49
  115. package/lib/WebViewNativeComponent.android.d.ts +0 -4
  116. package/lib/WebViewNativeComponent.android.js +0 -3
  117. package/lib/WebViewNativeComponent.ios.d.ts +0 -4
  118. package/lib/WebViewNativeComponent.ios.js +0 -3
@@ -0,0 +1,746 @@
1
+ package com.reactnativecommunity.webview
2
+
3
+ import android.app.DownloadManager
4
+ import android.content.pm.ActivityInfo
5
+ import android.graphics.Bitmap
6
+ import android.graphics.Color
7
+ import android.net.Uri
8
+ import android.os.Build
9
+ import android.os.Environment
10
+ import android.util.Log
11
+ import android.view.View
12
+ import android.view.ViewGroup
13
+ import android.view.WindowManager
14
+ import android.webkit.CookieManager
15
+ import android.webkit.DownloadListener
16
+ import android.webkit.URLUtil
17
+ import android.webkit.WebSettings
18
+ import android.webkit.WebView
19
+ import androidx.webkit.WebSettingsCompat
20
+ import androidx.webkit.WebViewFeature
21
+ import com.facebook.react.bridge.ReadableArray
22
+ import com.facebook.react.bridge.ReadableMap
23
+ import com.facebook.react.common.MapBuilder
24
+ import com.facebook.react.common.build.ReactBuildConfig
25
+ import com.facebook.react.uimanager.ThemedReactContext
26
+ import org.json.JSONException
27
+ import org.json.JSONObject
28
+ import java.io.UnsupportedEncodingException
29
+ import java.net.MalformedURLException
30
+ import java.net.URL
31
+ import java.util.HashSet
32
+ import java.util.Locale
33
+
34
+ val invalidCharRegex = "[\\\\/%\"]".toRegex()
35
+
36
+ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
37
+ companion object {
38
+ const val NAME = "RNCWebView"
39
+ }
40
+
41
+ private val TAG = "RNCWebViewManagerImpl"
42
+ private var mWebViewConfig: RNCWebViewConfig = RNCWebViewConfig { webView: WebView? -> }
43
+ private var mAllowsFullscreenVideo = false
44
+ private var mAllowsProtectedMedia = false
45
+ private var mDownloadingMessage: String? = null
46
+ private var mLackPermissionToDownloadMessage: String? = null
47
+ private var mHasOnOpenWindowEvent = false
48
+ private var mPendingSource: ReadableMap? = null
49
+
50
+ private var mUserAgent: String? = null
51
+ private var mUserAgentWithApplicationName: String? = null
52
+ private val HTML_ENCODING = "UTF-8"
53
+ private val HTML_MIME_TYPE = "text/html"
54
+ private val HTTP_METHOD_POST = "POST"
55
+
56
+ // Use `webView.loadUrl("about:blank")` to reliably reset the view
57
+ // state and release page resources (including any running JavaScript).
58
+ private val BLANK_URL = "about:blank"
59
+
60
+ private val DEFAULT_DOWNLOADING_MESSAGE = "Downloading"
61
+ private val DEFAULT_LACK_PERMISSION_TO_DOWNLOAD_MESSAGE =
62
+ "Cannot download files as permission was denied. Please provide permission to write to storage, in order to download files."
63
+
64
+ fun createRNCWebViewInstance(context: ThemedReactContext): RNCWebView {
65
+ return RNCWebView(context)
66
+ }
67
+
68
+ fun createViewInstance(context: ThemedReactContext): RNCWebViewWrapper {
69
+ val webView = createRNCWebViewInstance(context)
70
+ return createViewInstance(context, webView);
71
+ }
72
+
73
+ fun createViewInstance(context: ThemedReactContext, webView: RNCWebView): RNCWebViewWrapper {
74
+ setupWebChromeClient(webView)
75
+ context.addLifecycleEventListener(webView)
76
+ mWebViewConfig.configWebView(webView)
77
+ val settings = webView.settings
78
+ settings.builtInZoomControls = true
79
+ settings.displayZoomControls = false
80
+ settings.domStorageEnabled = true
81
+ settings.setSupportMultipleWindows(true)
82
+ settings.allowFileAccess = false
83
+ settings.allowContentAccess = false
84
+ settings.allowFileAccessFromFileURLs = false
85
+ settings.allowUniversalAccessFromFileURLs = false
86
+ settings.mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
87
+
88
+ // Fixes broken full-screen modals/galleries due to body height being 0.
89
+ webView.layoutParams = ViewGroup.LayoutParams(
90
+ ViewGroup.LayoutParams.MATCH_PARENT,
91
+ ViewGroup.LayoutParams.MATCH_PARENT
92
+ )
93
+ if (ReactBuildConfig.DEBUG) {
94
+ WebView.setWebContentsDebuggingEnabled(true)
95
+ }
96
+ webView.setDownloadListener(DownloadListener { url, userAgent, contentDisposition, mimetype, contentLength ->
97
+ val module = webView.reactApplicationContext.getNativeModule(RNCWebViewModule::class.java) ?: return@DownloadListener
98
+ val request: DownloadManager.Request = try {
99
+ DownloadManager.Request(Uri.parse(url))
100
+ } catch (e: IllegalArgumentException) {
101
+ Log.w(TAG, "Unsupported URI, aborting download", e)
102
+ return@DownloadListener
103
+ }
104
+ var fileName = URLUtil.guessFileName(url, contentDisposition, mimetype)
105
+
106
+ // Sanitize filename by replacing invalid characters with "_"
107
+ fileName = fileName.replace(invalidCharRegex, "_")
108
+
109
+ val downloadMessage = "Downloading $fileName"
110
+
111
+ //Attempt to add cookie, if it exists
112
+ var urlObj: URL? = null
113
+ try {
114
+ urlObj = URL(url)
115
+ val baseUrl = urlObj.protocol + "://" + urlObj.host
116
+ val cookie = CookieManager.getInstance().getCookie(baseUrl)
117
+ request.addRequestHeader("Cookie", cookie)
118
+ } catch (e: MalformedURLException) {
119
+ Log.w(TAG, "Error getting cookie for DownloadManager", e)
120
+ }
121
+
122
+ //Finish setting up request
123
+ request.addRequestHeader("User-Agent", userAgent)
124
+ request.setTitle(fileName)
125
+ request.setDescription(downloadMessage)
126
+ request.allowScanningByMediaScanner()
127
+ request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
128
+ request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
129
+ module.setDownloadRequest(request)
130
+ if (module.grantFileDownloaderPermissions(
131
+ getDownloadingMessageOrDefault(),
132
+ getLackPermissionToDownloadMessageOrDefault()
133
+ )
134
+ ) {
135
+ module.downloadFile(
136
+ getDownloadingMessageOrDefault()
137
+ )
138
+ }
139
+ })
140
+ return RNCWebViewWrapper(context, webView)
141
+ }
142
+
143
+ private fun setupWebChromeClient(
144
+ webView: RNCWebView,
145
+ ) {
146
+ val activity = webView.themedReactContext.currentActivity
147
+ if (mAllowsFullscreenVideo && activity != null) {
148
+ val initialRequestedOrientation = activity.requestedOrientation
149
+ val webChromeClient: RNCWebChromeClient =
150
+ object : RNCWebChromeClient(webView) {
151
+ override fun getDefaultVideoPoster(): Bitmap? {
152
+ return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888)
153
+ }
154
+
155
+ override fun onShowCustomView(view: View, callback: CustomViewCallback) {
156
+ if (mVideoView != null) {
157
+ callback.onCustomViewHidden()
158
+ return
159
+ }
160
+ mVideoView = view
161
+ mCustomViewCallback = callback
162
+ activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
163
+ mVideoView.systemUiVisibility = FULLSCREEN_SYSTEM_UI_VISIBILITY
164
+ activity.window.setFlags(
165
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
166
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
167
+ )
168
+ mVideoView.setBackgroundColor(Color.BLACK)
169
+
170
+ // Since RN's Modals interfere with the View hierarchy
171
+ // we will decide which View to hide if the hierarchy
172
+ // does not match (i.e., the WebView is within a Modal)
173
+ // NOTE: We could use `mWebView.getRootView()` instead of `getRootView()`
174
+ // but that breaks the Modal's styles and layout, so we need this to render
175
+ // in the main View hierarchy regardless
176
+ val rootView = rootView
177
+ rootView.addView(mVideoView, FULLSCREEN_LAYOUT_PARAMS)
178
+
179
+ // Different root views, we are in a Modal
180
+ if (rootView.rootView !== mWebView.rootView) {
181
+ mWebView.rootView.visibility = View.GONE
182
+ } else {
183
+ // Same view hierarchy (no Modal), just hide the WebView then
184
+ mWebView.visibility = View.GONE
185
+ }
186
+ mWebView.themedReactContext.addLifecycleEventListener(this)
187
+ }
188
+
189
+ override fun onHideCustomView() {
190
+ if (mVideoView == null) {
191
+ return
192
+ }
193
+
194
+ // Same logic as above
195
+ val rootView = rootView
196
+ if (rootView.rootView !== mWebView.rootView) {
197
+ mWebView.rootView.visibility = View.VISIBLE
198
+ } else {
199
+ // Same view hierarchy (no Modal)
200
+ mWebView.visibility = View.VISIBLE
201
+ }
202
+ activity.window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
203
+ rootView.removeView(mVideoView)
204
+ mCustomViewCallback.onCustomViewHidden()
205
+ mVideoView = null
206
+ mCustomViewCallback = null
207
+ activity.requestedOrientation = initialRequestedOrientation
208
+ mWebView.themedReactContext.removeLifecycleEventListener(this)
209
+ }
210
+ }
211
+ webChromeClient.setAllowsProtectedMedia(mAllowsProtectedMedia);
212
+ webChromeClient.setHasOnOpenWindowEvent(mHasOnOpenWindowEvent);
213
+ webView.webChromeClient = webChromeClient
214
+ } else {
215
+ var webChromeClient = webView.webChromeClient as RNCWebChromeClient?
216
+ webChromeClient?.onHideCustomView()
217
+ webChromeClient = object : RNCWebChromeClient(webView) {
218
+ override fun getDefaultVideoPoster(): Bitmap? {
219
+ return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888)
220
+ }
221
+ }
222
+ webChromeClient.setAllowsProtectedMedia(mAllowsProtectedMedia);
223
+ webChromeClient.setHasOnOpenWindowEvent(mHasOnOpenWindowEvent);
224
+ webView.webChromeClient = webChromeClient
225
+ }
226
+ }
227
+
228
+ fun setUserAgent(viewWrapper: RNCWebViewWrapper, userAgent: String?) {
229
+ mUserAgent = userAgent
230
+ setUserAgentString(viewWrapper)
231
+ }
232
+
233
+ fun setApplicationNameForUserAgent(viewWrapper: RNCWebViewWrapper, applicationName: String?) {
234
+ when {
235
+ applicationName != null -> {
236
+ val defaultUserAgent = WebSettings.getDefaultUserAgent(viewWrapper.webView.context)
237
+ mUserAgentWithApplicationName = "$defaultUserAgent $applicationName"
238
+ }
239
+ else -> {
240
+ mUserAgentWithApplicationName = null
241
+ }
242
+ }
243
+ setUserAgentString(viewWrapper)
244
+ }
245
+
246
+ private fun setUserAgentString(viewWrapper: RNCWebViewWrapper) {
247
+ val view = viewWrapper.webView
248
+ when {
249
+ mUserAgent != null -> {
250
+ view.settings.userAgentString = mUserAgent
251
+ }
252
+ mUserAgentWithApplicationName != null -> {
253
+ view.settings.userAgentString = mUserAgentWithApplicationName
254
+ }
255
+ else -> {
256
+ view.settings.userAgentString = WebSettings.getDefaultUserAgent(view.context)
257
+ }
258
+ }
259
+ }
260
+
261
+ fun setBasicAuthCredential(viewWrapper: RNCWebViewWrapper, credential: ReadableMap?) {
262
+ var basicAuthCredential: RNCBasicAuthCredential? = null
263
+ if (credential != null) {
264
+ if (credential.hasKey("username") && credential.hasKey("password")) {
265
+ val username = credential.getString("username")
266
+ val password = credential.getString("password")
267
+ basicAuthCredential = RNCBasicAuthCredential(username, password)
268
+ }
269
+ }
270
+ viewWrapper.webView.setBasicAuthCredential(basicAuthCredential)
271
+ }
272
+
273
+ fun onAfterUpdateTransaction(viewWrapper: RNCWebViewWrapper) {
274
+ mPendingSource?.let { source ->
275
+ loadSource(viewWrapper, source)
276
+ }
277
+ mPendingSource = null
278
+ }
279
+
280
+ fun onDropViewInstance(viewWrapper: RNCWebViewWrapper) {
281
+ val webView = viewWrapper.webView
282
+ webView.themedReactContext.removeLifecycleEventListener(webView)
283
+ webView.cleanupCallbacksAndDestroy()
284
+ webView.mWebChromeClient = null
285
+ }
286
+
287
+ val COMMAND_GO_BACK = 1
288
+ val COMMAND_GO_FORWARD = 2
289
+ val COMMAND_RELOAD = 3
290
+ val COMMAND_STOP_LOADING = 4
291
+ val COMMAND_POST_MESSAGE = 5
292
+ val COMMAND_INJECT_JAVASCRIPT = 6
293
+ val COMMAND_LOAD_URL = 7
294
+ val COMMAND_FOCUS = 8
295
+
296
+ // android commands
297
+ val COMMAND_CLEAR_FORM_DATA = 1000
298
+ val COMMAND_CLEAR_CACHE = 1001
299
+ val COMMAND_CLEAR_HISTORY = 1002
300
+
301
+ fun getCommandsMap(): Map<String, Int>? {
302
+ return MapBuilder.builder<String, Int>()
303
+ .put("goBack", COMMAND_GO_BACK)
304
+ .put("goForward", COMMAND_GO_FORWARD)
305
+ .put("reload", COMMAND_RELOAD)
306
+ .put("stopLoading", COMMAND_STOP_LOADING)
307
+ .put("postMessage", COMMAND_POST_MESSAGE)
308
+ .put("injectJavaScript", COMMAND_INJECT_JAVASCRIPT)
309
+ .put("loadUrl", COMMAND_LOAD_URL)
310
+ .put("requestFocus", COMMAND_FOCUS)
311
+ .put("clearFormData", COMMAND_CLEAR_FORM_DATA)
312
+ .put("clearCache", COMMAND_CLEAR_CACHE)
313
+ .put("clearHistory", COMMAND_CLEAR_HISTORY)
314
+ .build()
315
+ }
316
+
317
+ fun receiveCommand(viewWrapper: RNCWebViewWrapper, commandId: String, args: ReadableArray) {
318
+ val webView = viewWrapper.webView
319
+ when (commandId) {
320
+ "goBack" -> webView.goBack()
321
+ "goForward" -> webView.goForward()
322
+ "reload" -> webView.reload()
323
+ "stopLoading" -> webView.stopLoading()
324
+ "postMessage" -> try {
325
+ val eventInitDict = JSONObject()
326
+ eventInitDict.put("data", args.getString(0))
327
+ webView.evaluateJavascriptWithFallback(
328
+ "(function () {" +
329
+ "var event;" +
330
+ "var data = " + eventInitDict.toString() + ";" +
331
+ "try {" +
332
+ "event = new MessageEvent('message', data);" +
333
+ "} catch (e) {" +
334
+ "event = document.createEvent('MessageEvent');" +
335
+ "event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" +
336
+ "}" +
337
+ "document.dispatchEvent(event);" +
338
+ "})();"
339
+ )
340
+ } catch (e: JSONException) {
341
+ throw RuntimeException(e)
342
+ }
343
+ "injectJavaScript" -> webView.evaluateJavascriptWithFallback(args.getString(0))
344
+ "loadUrl" -> {
345
+ val url = args?.getString(0) ?: throw RuntimeException("Arguments for loading an url are null!")
346
+ webView.progressChangedFilter.setWaitingForCommandLoadUrl(false)
347
+ webView.loadUrl(url)
348
+ }
349
+ "requestFocus" -> webView.requestFocus()
350
+ "clearFormData" -> webView.clearFormData()
351
+ "clearCache" -> {
352
+ val includeDiskFiles = args != null && args.getBoolean(0)
353
+ webView.clearCache(includeDiskFiles)
354
+ }
355
+ "clearHistory" -> webView.clearHistory()
356
+ }
357
+ }
358
+
359
+ fun setMixedContentMode(viewWrapper: RNCWebViewWrapper, mixedContentMode: String?) {
360
+ val view = viewWrapper.webView
361
+ if (mixedContentMode == null || "never" == mixedContentMode) {
362
+ view.settings.mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
363
+ } else if ("always" == mixedContentMode) {
364
+ view.settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
365
+ } else if ("compatibility" == mixedContentMode) {
366
+ view.settings.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
367
+ }
368
+ }
369
+
370
+ fun setAllowUniversalAccessFromFileURLs(viewWrapper: RNCWebViewWrapper, allow: Boolean) {
371
+ viewWrapper.webView.settings.allowUniversalAccessFromFileURLs = allow
372
+ }
373
+
374
+ private fun getDownloadingMessageOrDefault(): String? {
375
+ return mDownloadingMessage ?: DEFAULT_DOWNLOADING_MESSAGE
376
+ }
377
+
378
+ private fun getLackPermissionToDownloadMessageOrDefault(): String? {
379
+ return mLackPermissionToDownloadMessage
380
+ ?: DEFAULT_LACK_PERMISSION_TO_DOWNLOAD_MESSAGE
381
+ }
382
+
383
+ fun setSource(viewWrapper: RNCWebViewWrapper, source: ReadableMap?) {
384
+ mPendingSource = source
385
+ }
386
+
387
+ private fun loadSource(viewWrapper: RNCWebViewWrapper, source: ReadableMap?) {
388
+ val view = viewWrapper.webView
389
+ if (source != null) {
390
+ if (source.hasKey("html")) {
391
+ val html = source.getString("html")
392
+ val baseUrl = if (source.hasKey("baseUrl")) source.getString("baseUrl") else ""
393
+ view.loadDataWithBaseURL(
394
+ baseUrl,
395
+ html!!,
396
+ HTML_MIME_TYPE,
397
+ HTML_ENCODING,
398
+ null
399
+ )
400
+ return
401
+ }
402
+ if (source.hasKey("uri")) {
403
+ val url = source.getString("uri")
404
+ val previousUrl = view.url
405
+ if (previousUrl != null && previousUrl == url) {
406
+ return
407
+ }
408
+ if (source.hasKey("method")) {
409
+ val method = source.getString("method")
410
+ if (method.equals(HTTP_METHOD_POST, ignoreCase = true)) {
411
+ var postData: ByteArray? = null
412
+ if (source.hasKey("body")) {
413
+ val body = source.getString("body")
414
+ postData = try {
415
+ body!!.toByteArray(charset("UTF-8"))
416
+ } catch (e: UnsupportedEncodingException) {
417
+ body!!.toByteArray()
418
+ }
419
+ }
420
+ if (postData == null) {
421
+ postData = ByteArray(0)
422
+ }
423
+ view.postUrl(url!!, postData)
424
+ return
425
+ }
426
+ }
427
+ val headerMap = HashMap<String, String?>()
428
+ if (source.hasKey("headers")) {
429
+ if (newArch) {
430
+ val headerArray = source.getArray("headers");
431
+ for (header in headerArray!!.toArrayList()) {
432
+ val headerCasted = header as HashMap<String, String>
433
+ val name = headerCasted.get("name") ?: ""
434
+ val value = headerCasted.get("value") ?: ""
435
+ if ("user-agent" == name.lowercase(Locale.ENGLISH)) {
436
+ view.settings.userAgentString = value
437
+ } else {
438
+ headerMap[name] = value
439
+ }
440
+ }
441
+ } else {
442
+ val headers = source.getMap("headers")
443
+ val iter = headers!!.keySetIterator()
444
+ while (iter.hasNextKey()) {
445
+ val key = iter.nextKey()
446
+ if ("user-agent" == key.lowercase(Locale.ENGLISH)) {
447
+ view.settings.userAgentString = headers.getString(key)
448
+ } else {
449
+ headerMap[key] = headers.getString(key)
450
+ }
451
+ }
452
+ }
453
+ }
454
+ view.loadUrl(url!!, headerMap)
455
+ return
456
+ }
457
+ }
458
+ view.loadUrl(BLANK_URL)
459
+ }
460
+
461
+ fun setMessagingModuleName(viewWrapper: RNCWebViewWrapper, value: String?) {
462
+ val view = viewWrapper.webView
463
+ view.messagingModuleName = value
464
+ }
465
+
466
+ fun setCacheEnabled(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
467
+ val view = viewWrapper.webView
468
+ view.settings.cacheMode = if (enabled) WebSettings.LOAD_DEFAULT else WebSettings.LOAD_NO_CACHE
469
+ }
470
+
471
+ fun setIncognito(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
472
+ val view = viewWrapper.webView
473
+ // Don't do anything when incognito is disabled
474
+ if (!enabled) {
475
+ return;
476
+ }
477
+
478
+ // Remove all previous cookies
479
+ CookieManager.getInstance().removeAllCookies(null);
480
+
481
+ // Disable caching
482
+ view.settings.cacheMode = WebSettings.LOAD_NO_CACHE
483
+ view.clearHistory();
484
+ view.clearCache(true);
485
+
486
+ // No form data or autofill enabled
487
+ view.clearFormData();
488
+ view.settings.savePassword = false;
489
+ view.settings.saveFormData = false;
490
+ }
491
+
492
+ fun setInjectedJavaScript(viewWrapper: RNCWebViewWrapper, injectedJavaScript: String?) {
493
+ val view = viewWrapper.webView
494
+ view.injectedJS = injectedJavaScript
495
+ }
496
+
497
+ fun setInjectedJavaScriptBeforeContentLoaded(viewWrapper: RNCWebViewWrapper, value: String?) {
498
+ val view = viewWrapper.webView
499
+ view.injectedJSBeforeContentLoaded = value
500
+ }
501
+
502
+ fun setInjectedJavaScriptForMainFrameOnly(viewWrapper: RNCWebViewWrapper, value: Boolean) {
503
+ val view = viewWrapper.webView
504
+ view.injectedJavaScriptForMainFrameOnly = value
505
+ }
506
+
507
+ fun setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(viewWrapper: RNCWebViewWrapper, value: Boolean) {
508
+ val view = viewWrapper.webView
509
+ view.injectedJavaScriptBeforeContentLoadedForMainFrameOnly = value
510
+ }
511
+
512
+ fun setInjectedJavaScriptObject(viewWrapper: RNCWebViewWrapper, value: String?) {
513
+ val view = viewWrapper.webView
514
+ view.setInjectedJavaScriptObject(value)
515
+ }
516
+
517
+ fun setJavaScriptCanOpenWindowsAutomatically(viewWrapper: RNCWebViewWrapper, value: Boolean) {
518
+ val view = viewWrapper.webView
519
+ view.settings.javaScriptCanOpenWindowsAutomatically = value
520
+ }
521
+
522
+ fun setShowsVerticalScrollIndicator(viewWrapper: RNCWebViewWrapper, value: Boolean) {
523
+ val view = viewWrapper.webView
524
+ view.isVerticalScrollBarEnabled = value
525
+ }
526
+
527
+ fun setShowsHorizontalScrollIndicator(viewWrapper: RNCWebViewWrapper, value: Boolean) {
528
+ val view = viewWrapper.webView
529
+ view.isHorizontalScrollBarEnabled = value
530
+ }
531
+
532
+ fun setMessagingEnabled(viewWrapper: RNCWebViewWrapper, value: Boolean) {
533
+ val view = viewWrapper.webView
534
+ view.setMessagingEnabled(value)
535
+ }
536
+
537
+ fun setMediaPlaybackRequiresUserAction(viewWrapper: RNCWebViewWrapper, value: Boolean) {
538
+ val view = viewWrapper.webView
539
+ view.settings.mediaPlaybackRequiresUserGesture = value
540
+ }
541
+
542
+ fun setHasOnScroll(viewWrapper: RNCWebViewWrapper, value: Boolean) {
543
+ val view = viewWrapper.webView
544
+ view.setHasScrollEvent(value)
545
+ }
546
+
547
+ fun setJavaScriptEnabled(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
548
+ val view = viewWrapper.webView
549
+ view.settings.javaScriptEnabled = enabled
550
+ }
551
+
552
+ fun setAllowFileAccess(viewWrapper: RNCWebViewWrapper, allowFileAccess: Boolean) {
553
+ val view = viewWrapper.webView
554
+ view.settings.allowFileAccess = allowFileAccess;
555
+ }
556
+
557
+ fun setAllowFileAccessFromFileURLs(viewWrapper: RNCWebViewWrapper, value: Boolean) {
558
+ val view = viewWrapper.webView
559
+ view.settings.allowFileAccessFromFileURLs = value;
560
+ }
561
+
562
+ fun setAllowsFullscreenVideo(viewWrapper: RNCWebViewWrapper, value: Boolean) {
563
+ val view = viewWrapper.webView
564
+ mAllowsFullscreenVideo = value
565
+ setupWebChromeClient(view)
566
+ }
567
+
568
+ fun setAndroidLayerType(viewWrapper: RNCWebViewWrapper, layerTypeString: String?) {
569
+ val view = viewWrapper.webView
570
+ val layerType = when (layerTypeString) {
571
+ "hardware" -> View.LAYER_TYPE_HARDWARE
572
+ "software" -> View.LAYER_TYPE_SOFTWARE
573
+ else -> View.LAYER_TYPE_NONE
574
+ }
575
+ view.setLayerType(layerType, null)
576
+ }
577
+
578
+ fun setCacheMode(viewWrapper: RNCWebViewWrapper, cacheModeString: String?) {
579
+ val view = viewWrapper.webView
580
+ view.settings.cacheMode = when (cacheModeString) {
581
+ "LOAD_CACHE_ONLY" -> WebSettings.LOAD_CACHE_ONLY
582
+ "LOAD_CACHE_ELSE_NETWORK" -> WebSettings.LOAD_CACHE_ELSE_NETWORK
583
+ "LOAD_NO_CACHE" -> WebSettings.LOAD_NO_CACHE
584
+ "LOAD_DEFAULT" -> WebSettings.LOAD_DEFAULT
585
+ else -> WebSettings.LOAD_DEFAULT
586
+ }
587
+ }
588
+
589
+ fun setDomStorageEnabled(viewWrapper: RNCWebViewWrapper, value: Boolean) {
590
+ val view = viewWrapper.webView
591
+ view.settings.domStorageEnabled = value
592
+ }
593
+
594
+ fun setDownloadingMessage(value: String?) {
595
+ mDownloadingMessage = value
596
+ }
597
+
598
+ fun setForceDarkOn(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
599
+ val view = viewWrapper.webView
600
+ // Only Android 10+ support dark mode
601
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
602
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
603
+ val forceDarkMode =
604
+ if (enabled) WebSettingsCompat.FORCE_DARK_ON else WebSettingsCompat.FORCE_DARK_OFF
605
+ WebSettingsCompat.setForceDark(view.settings, forceDarkMode)
606
+ }
607
+
608
+ // Set how WebView content should be darkened.
609
+ // PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING: checks for the "color-scheme" <meta> tag.
610
+ // If present, it uses media queries. If absent, it applies user-agent (automatic)
611
+ // More information about Force Dark Strategy can be found here:
612
+ // https://developer.android.com/reference/androidx/webkit/WebSettingsCompat#setForceDarkStrategy(android.webkit.WebSettings)
613
+ if (enabled && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
614
+ WebSettingsCompat.setForceDarkStrategy(
615
+ view.settings,
616
+ WebSettingsCompat.DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING
617
+ )
618
+ }
619
+ }
620
+ }
621
+
622
+ fun setGeolocationEnabled(viewWrapper: RNCWebViewWrapper, value: Boolean) {
623
+ val view = viewWrapper.webView
624
+ view.settings.setGeolocationEnabled(value)
625
+ }
626
+
627
+ fun setLackPermissionToDownloadMessage(value: String?) {
628
+ mLackPermissionToDownloadMessage = value
629
+ }
630
+
631
+ fun setHasOnOpenWindowEvent(viewWrapper: RNCWebViewWrapper, value: Boolean) {
632
+ val view = viewWrapper.webView
633
+ mHasOnOpenWindowEvent = value
634
+ setupWebChromeClient(view)
635
+ }
636
+
637
+ fun setMinimumFontSize(viewWrapper: RNCWebViewWrapper, value: Int) {
638
+ val view = viewWrapper.webView
639
+ view.settings.minimumFontSize = value
640
+ }
641
+
642
+ fun setAllowsProtectedMedia(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
643
+ val view = viewWrapper.webView
644
+ // This variable is used to keep consistency
645
+ // in case a new WebChromeClient is created
646
+ // (eg. when mAllowsFullScreenVideo changes)
647
+ mAllowsProtectedMedia = enabled
648
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
649
+ val client = view.webChromeClient
650
+ if (client != null && client is RNCWebChromeClient) {
651
+ client.setAllowsProtectedMedia(enabled)
652
+ }
653
+ }
654
+ }
655
+
656
+ fun setMenuCustomItems(viewWrapper: RNCWebViewWrapper, value: ReadableArray?) {
657
+ val view = viewWrapper.webView
658
+ when (value) {
659
+ null -> view.setMenuCustomItems(null)
660
+ else -> view.setMenuCustomItems(value.toArrayList() as List<Map<String, String>>)
661
+ }
662
+ }
663
+
664
+ fun setNestedScrollEnabled(viewWrapper: RNCWebViewWrapper, value: Boolean) {
665
+ val view = viewWrapper.webView
666
+ view.nestedScrollEnabled = value
667
+ }
668
+
669
+ fun setOverScrollMode(viewWrapper: RNCWebViewWrapper, overScrollModeString: String?) {
670
+ val view = viewWrapper.webView
671
+ view.overScrollMode = when (overScrollModeString) {
672
+ "never" -> View.OVER_SCROLL_NEVER
673
+ "content" -> View.OVER_SCROLL_IF_CONTENT_SCROLLS
674
+ "always" -> View.OVER_SCROLL_ALWAYS
675
+ else -> View.OVER_SCROLL_ALWAYS
676
+ }
677
+ }
678
+
679
+ fun setSaveFormDataDisabled(viewWrapper: RNCWebViewWrapper, disabled: Boolean) {
680
+ val view = viewWrapper.webView
681
+ view.settings.saveFormData = !disabled
682
+ }
683
+
684
+ fun setScalesPageToFit(viewWrapper: RNCWebViewWrapper, value: Boolean) {
685
+ val view = viewWrapper.webView
686
+ view.settings.loadWithOverviewMode = value
687
+ view.settings.useWideViewPort = value
688
+ }
689
+
690
+ fun setSetBuiltInZoomControls(viewWrapper: RNCWebViewWrapper, value: Boolean) {
691
+ val view = viewWrapper.webView
692
+ view.settings.builtInZoomControls = value
693
+ }
694
+
695
+ fun setSetDisplayZoomControls(viewWrapper: RNCWebViewWrapper, value: Boolean) {
696
+ val view = viewWrapper.webView
697
+ view.settings.displayZoomControls = value
698
+
699
+ }
700
+
701
+ fun setSetSupportMultipleWindows(viewWrapper: RNCWebViewWrapper, value: Boolean) {
702
+ val view = viewWrapper.webView
703
+ view.settings.setSupportMultipleWindows(value)
704
+ }
705
+
706
+ fun setTextZoom(viewWrapper: RNCWebViewWrapper, value: Int) {
707
+ val view = viewWrapper.webView
708
+ view.settings.textZoom = value
709
+ }
710
+
711
+ fun setThirdPartyCookiesEnabled(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
712
+ val view = viewWrapper.webView
713
+ CookieManager.getInstance().setAcceptThirdPartyCookies(view, enabled)
714
+ }
715
+
716
+ fun setWebviewDebuggingEnabled(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
717
+ RNCWebView.setWebContentsDebuggingEnabled(enabled)
718
+ }
719
+
720
+ fun setPaymentRequestEnabled(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
721
+ val view = viewWrapper.webView
722
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.PAYMENT_REQUEST)) {
723
+ WebSettingsCompat.setPaymentRequestEnabled(view.settings, enabled)
724
+ }
725
+ }
726
+
727
+ /**
728
+ * Exodus: Set camera permission origin whitelist.
729
+ * Only origins in this whitelist will be allowed to request camera/microphone permissions.
730
+ */
731
+ fun setCameraPermissionOriginWhitelist(viewWrapper: RNCWebViewWrapper, whitelist: ReadableArray?) {
732
+ val view = viewWrapper.webView
733
+ val whitelistSet = HashSet<String>()
734
+ whitelist?.let {
735
+ for (i in 0 until it.size()) {
736
+ it.getString(i)?.let { origin ->
737
+ whitelistSet.add(origin)
738
+ }
739
+ }
740
+ }
741
+ val client = view.webChromeClient
742
+ if (client != null && client is RNCWebChromeClient) {
743
+ client.setCameraPermissionOriginWhitelist(whitelistSet)
744
+ }
745
+ }
746
+ }