@javascriptcommon/react-native-webview 13.12.16

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