@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
@@ -1,1408 +0,0 @@
1
- package com.reactnativecommunity.webview;
2
-
3
- import android.annotation.SuppressLint;
4
- import android.annotation.TargetApi;
5
- import android.app.Activity;
6
- import android.app.DownloadManager;
7
- import android.content.Context;
8
- import android.content.pm.ActivityInfo;
9
- import android.content.pm.PackageManager;
10
- import android.graphics.Bitmap;
11
- import android.graphics.Color;
12
- import android.Manifest;
13
- import android.net.http.SslError;
14
- import android.net.Uri;
15
- import android.os.Build;
16
- import android.os.Environment;
17
- import android.os.Message;
18
- import android.os.SystemClock;
19
- import android.text.TextUtils;
20
- import android.util.Log;
21
- import android.view.Gravity;
22
- import android.view.MotionEvent;
23
- import android.view.View;
24
- import android.view.ViewGroup;
25
- import android.view.ViewGroup.LayoutParams;
26
- import android.view.WindowManager;
27
- import android.webkit.ConsoleMessage;
28
- import android.webkit.CookieManager;
29
- import android.webkit.DownloadListener;
30
- import android.webkit.GeolocationPermissions;
31
- import android.webkit.HttpAuthHandler;
32
- import android.webkit.JavascriptInterface;
33
- import android.webkit.SslErrorHandler;
34
- import android.webkit.PermissionRequest;
35
- import android.webkit.ValueCallback;
36
- import android.webkit.WebChromeClient;
37
- import android.webkit.WebResourceRequest;
38
- import android.webkit.WebResourceResponse;
39
- import android.webkit.WebSettings;
40
- import android.webkit.WebView;
41
- import android.webkit.WebViewClient;
42
- import android.widget.FrameLayout;
43
-
44
- import androidx.annotation.NonNull;
45
- import androidx.annotation.Nullable;
46
- import androidx.annotation.RequiresApi;
47
- import androidx.core.content.ContextCompat;
48
- import androidx.core.util.Pair;
49
- import androidx.webkit.WebSettingsCompat;
50
- import androidx.webkit.WebViewFeature;
51
-
52
- import com.facebook.common.logging.FLog;
53
- import com.facebook.react.modules.core.PermissionAwareActivity;
54
- import com.facebook.react.modules.core.PermissionListener;
55
- import com.facebook.react.views.scroll.ScrollEvent;
56
- import com.facebook.react.views.scroll.ScrollEventType;
57
- import com.facebook.react.views.scroll.OnScrollDispatchHelper;
58
- import com.facebook.react.bridge.Arguments;
59
- import com.facebook.react.bridge.CatalystInstance;
60
- import com.facebook.react.bridge.LifecycleEventListener;
61
- import com.facebook.react.bridge.ReactContext;
62
- import com.facebook.react.bridge.ReadableArray;
63
- import com.facebook.react.bridge.ReadableMap;
64
- import com.facebook.react.bridge.ReadableMapKeySetIterator;
65
- import com.facebook.react.bridge.WritableMap;
66
- import com.facebook.react.bridge.WritableNativeArray;
67
- import com.facebook.react.bridge.WritableNativeMap;
68
- import com.facebook.react.common.MapBuilder;
69
- import com.facebook.react.common.build.ReactBuildConfig;
70
- import com.facebook.react.module.annotations.ReactModule;
71
- import com.facebook.react.uimanager.SimpleViewManager;
72
- import com.facebook.react.uimanager.ThemedReactContext;
73
- import com.facebook.react.uimanager.UIManagerModule;
74
- import com.facebook.react.uimanager.annotations.ReactProp;
75
- import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
76
- import com.facebook.react.uimanager.events.Event;
77
- import com.facebook.react.uimanager.events.EventDispatcher;
78
- import com.reactnativecommunity.webview.RNCWebViewModule.ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState;
79
- import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
80
- import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
81
- import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
82
- import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
83
- import com.reactnativecommunity.webview.events.TopMessageEvent;
84
- import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
85
-
86
- import org.json.JSONException;
87
- import org.json.JSONObject;
88
-
89
- import java.io.UnsupportedEncodingException;
90
- import java.lang.IllegalArgumentException;
91
- import java.net.MalformedURLException;
92
- import java.net.URL;
93
- import java.net.URLEncoder;
94
- import java.util.ArrayList;
95
- import java.util.Collections;
96
- import java.util.HashMap;
97
- import java.util.List;
98
- import java.util.Locale;
99
- import java.util.Map;
100
- import java.util.concurrent.atomic.AtomicReference;
101
-
102
- /**
103
- * Manages instances of {@link WebView}
104
- * <p>
105
- * Can accept following commands:
106
- * - GO_BACK
107
- * - GO_FORWARD
108
- * - RELOAD
109
- * - LOAD_URL
110
- * <p>
111
- * {@link WebView} instances could emit following direct events:
112
- * - topLoadingFinish
113
- * - topLoadingStart
114
- * - topLoadingStart
115
- * - topLoadingProgress
116
- * - topShouldStartLoadWithRequest
117
- * <p>
118
- * Each event will carry the following properties:
119
- * - target - view's react tag
120
- * - url - url set for the webview
121
- * - loading - whether webview is in a loading state
122
- * - title - title of the current page
123
- * - canGoBack - boolean, whether there is anything on a history stack to go back
124
- * - canGoForward - boolean, whether it is possible to request GO_FORWARD command
125
- */
126
- @ReactModule(name = RNCWebViewManager.REACT_CLASS)
127
- public class RNCWebViewManager extends SimpleViewManager<WebView> {
128
- private static final String TAG = "RNCWebViewManager";
129
-
130
- public static final int COMMAND_GO_BACK = 1;
131
- public static final int COMMAND_GO_FORWARD = 2;
132
- public static final int COMMAND_RELOAD = 3;
133
- public static final int COMMAND_STOP_LOADING = 4;
134
- public static final int COMMAND_POST_MESSAGE = 5;
135
- public static final int COMMAND_INJECT_JAVASCRIPT = 6;
136
- public static final int COMMAND_LOAD_URL = 7;
137
- public static final int COMMAND_FOCUS = 8;
138
-
139
- // android commands
140
- public static final int COMMAND_CLEAR_FORM_DATA = 1000;
141
- public static final int COMMAND_CLEAR_CACHE = 1001;
142
- public static final int COMMAND_CLEAR_HISTORY = 1002;
143
-
144
- protected static final String REACT_CLASS = "RNCWebView";
145
- protected static final String HTML_ENCODING = "UTF-8";
146
- protected static final String HTML_MIME_TYPE = "text/html";
147
- protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView";
148
- protected static final String HTTP_METHOD_POST = "POST";
149
- // Use `webView.loadUrl("about:blank")` to reliably reset the view
150
- // state and release page resources (including any running JavaScript).
151
- protected static final String BLANK_URL = "about:blank";
152
- protected static final int SHOULD_OVERRIDE_URL_LOADING_TIMEOUT = 250;
153
- protected static final String DEFAULT_DOWNLOADING_MESSAGE = "Downloading";
154
- protected static final String DEFAULT_LACK_PERMISSION_TO_DOWNLOAD_MESSAGE =
155
- "Cannot download files as permission was denied. Please provide permission to write to storage, in order to download files.";
156
- protected WebViewConfig mWebViewConfig;
157
-
158
- protected RNCWebChromeClient mWebChromeClient = null;
159
- protected boolean mAllowsProtectedMedia = false;
160
- protected @Nullable String mUserAgent = null;
161
- protected @Nullable String mUserAgentWithApplicationName = null;
162
- protected @Nullable String mDownloadingMessage = null;
163
- protected @Nullable String mLackPermissionToDownloadMessage = null;
164
-
165
- public RNCWebViewManager() {
166
- mWebViewConfig = new WebViewConfig() {
167
- public void configWebView(WebView webView) {
168
- }
169
- };
170
- }
171
-
172
- public RNCWebViewManager(WebViewConfig webViewConfig) {
173
- mWebViewConfig = webViewConfig;
174
- }
175
-
176
- @Override
177
- public String getName() {
178
- return REACT_CLASS;
179
- }
180
-
181
- protected RNCWebView createRNCWebViewInstance(ThemedReactContext reactContext) {
182
- return new RNCWebView(reactContext);
183
- }
184
-
185
- @Override
186
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
187
- protected WebView createViewInstance(ThemedReactContext reactContext) {
188
- RNCWebView webView = createRNCWebViewInstance(reactContext);
189
- setupWebChromeClient(reactContext, webView);
190
- reactContext.addLifecycleEventListener(webView);
191
- mWebViewConfig.configWebView(webView);
192
- WebSettings settings = webView.getSettings();
193
- settings.setBuiltInZoomControls(true);
194
- settings.setDisplayZoomControls(false);
195
- settings.setDomStorageEnabled(true);
196
- settings.setSupportMultipleWindows(true);
197
-
198
- settings.setAllowFileAccess(false);
199
- settings.setAllowContentAccess(false);
200
- settings.setAllowFileAccessFromFileURLs(false);
201
- settings.setJavaScriptCanOpenWindowsAutomatically(false);
202
- settings.setAllowUniversalAccessFromFileURLs(false);
203
- setMixedContentMode(webView, "never");
204
-
205
- // Fixes broken full-screen modals/galleries due to body height being 0.
206
- webView.setLayoutParams(
207
- new LayoutParams(LayoutParams.MATCH_PARENT,
208
- LayoutParams.MATCH_PARENT));
209
-
210
- if (ReactBuildConfig.DEBUG) {
211
- WebView.setWebContentsDebuggingEnabled(true);
212
- }
213
-
214
- return webView;
215
- }
216
-
217
- private String getDownloadingMessage() {
218
- return mDownloadingMessage == null ? DEFAULT_DOWNLOADING_MESSAGE : mDownloadingMessage;
219
- }
220
-
221
- private String getLackPermissionToDownloadMessage() {
222
- return mDownloadingMessage == null ? DEFAULT_LACK_PERMISSION_TO_DOWNLOAD_MESSAGE : mLackPermissionToDownloadMessage;
223
- }
224
-
225
- @ReactProp(name = "javaScriptEnabled")
226
- public void setJavaScriptEnabled(WebView view, boolean enabled) {
227
- view.getSettings().setJavaScriptEnabled(enabled);
228
- }
229
-
230
- @ReactProp(name = "setBuiltInZoomControls")
231
- public void setBuiltInZoomControls(WebView view, boolean enabled) {
232
- view.getSettings().setBuiltInZoomControls(enabled);
233
- }
234
-
235
- @ReactProp(name = "setDisplayZoomControls")
236
- public void setDisplayZoomControls(WebView view, boolean enabled) {
237
- view.getSettings().setDisplayZoomControls(enabled);
238
- }
239
-
240
- @ReactProp(name = "setSupportMultipleWindows")
241
- public void setSupportMultipleWindows(WebView view, boolean enabled){
242
- view.getSettings().setSupportMultipleWindows(enabled);
243
- }
244
-
245
- @ReactProp(name = "showsHorizontalScrollIndicator")
246
- public void setShowsHorizontalScrollIndicator(WebView view, boolean enabled) {
247
- view.setHorizontalScrollBarEnabled(enabled);
248
- }
249
-
250
- @ReactProp(name = "showsVerticalScrollIndicator")
251
- public void setShowsVerticalScrollIndicator(WebView view, boolean enabled) {
252
- view.setVerticalScrollBarEnabled(enabled);
253
- }
254
-
255
- @ReactProp(name = "downloadingMessage")
256
- public void setDownloadingMessage(WebView view, String message) {
257
- mDownloadingMessage = message;
258
- }
259
-
260
- @ReactProp(name = "lackPermissionToDownloadMessage")
261
- public void setLackPermissionToDownlaodMessage(WebView view, String message) {
262
- mLackPermissionToDownloadMessage = message;
263
- }
264
-
265
- @ReactProp(name = "cacheEnabled")
266
- public void setCacheEnabled(WebView view, boolean enabled) {
267
- view.getSettings().setCacheMode(enabled ? WebSettings.LOAD_DEFAULT : WebSettings.LOAD_NO_CACHE);
268
- }
269
-
270
- @ReactProp(name = "cacheMode")
271
- public void setCacheMode(WebView view, String cacheModeString) {
272
- Integer cacheMode;
273
- switch (cacheModeString) {
274
- case "LOAD_CACHE_ONLY":
275
- cacheMode = WebSettings.LOAD_CACHE_ONLY;
276
- break;
277
- case "LOAD_CACHE_ELSE_NETWORK":
278
- cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK;
279
- break;
280
- case "LOAD_NO_CACHE":
281
- cacheMode = WebSettings.LOAD_NO_CACHE;
282
- break;
283
- case "LOAD_DEFAULT":
284
- default:
285
- cacheMode = WebSettings.LOAD_DEFAULT;
286
- break;
287
- }
288
- view.getSettings().setCacheMode(cacheMode);
289
- }
290
-
291
- @ReactProp(name = "androidHardwareAccelerationDisabled")
292
- public void setHardwareAccelerationDisabled(WebView view, boolean disabled) {
293
- if (disabled) {
294
- view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
295
- }
296
- }
297
-
298
- @ReactProp(name = "androidLayerType")
299
- public void setLayerType(WebView view, String layerTypeString) {
300
- int layerType = View.LAYER_TYPE_NONE;
301
- switch (layerTypeString) {
302
- case "hardware":
303
- layerType = View.LAYER_TYPE_HARDWARE;
304
- break;
305
- case "software":
306
- layerType = View.LAYER_TYPE_SOFTWARE;
307
- break;
308
- }
309
- view.setLayerType(layerType, null);
310
- }
311
-
312
-
313
- @ReactProp(name = "overScrollMode")
314
- public void setOverScrollMode(WebView view, String overScrollModeString) {
315
- Integer overScrollMode;
316
- switch (overScrollModeString) {
317
- case "never":
318
- overScrollMode = View.OVER_SCROLL_NEVER;
319
- break;
320
- case "content":
321
- overScrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS;
322
- break;
323
- case "always":
324
- default:
325
- overScrollMode = View.OVER_SCROLL_ALWAYS;
326
- break;
327
- }
328
- view.setOverScrollMode(overScrollMode);
329
- }
330
-
331
- @ReactProp(name = "nestedScrollEnabled")
332
- public void setNestedScrollEnabled(WebView view, boolean enabled) {
333
- ((RNCWebView) view).setNestedScrollEnabled(enabled);
334
- }
335
-
336
- @ReactProp(name = "thirdPartyCookiesEnabled")
337
- public void setThirdPartyCookiesEnabled(WebView view, boolean enabled) {
338
- CookieManager.getInstance().setAcceptThirdPartyCookies(view, enabled);
339
- }
340
-
341
- @ReactProp(name = "textZoom")
342
- public void setTextZoom(WebView view, int value) {
343
- view.getSettings().setTextZoom(value);
344
- }
345
-
346
- @ReactProp(name = "scalesPageToFit")
347
- public void setScalesPageToFit(WebView view, boolean enabled) {
348
- view.getSettings().setLoadWithOverviewMode(enabled);
349
- view.getSettings().setUseWideViewPort(enabled);
350
- }
351
-
352
- @ReactProp(name = "domStorageEnabled")
353
- public void setDomStorageEnabled(WebView view, boolean enabled) {
354
- view.getSettings().setDomStorageEnabled(enabled);
355
- }
356
-
357
- @ReactProp(name = "userAgent")
358
- public void setUserAgent(WebView view, @Nullable String userAgent) {
359
- if (userAgent != null) {
360
- mUserAgent = userAgent;
361
- } else {
362
- mUserAgent = null;
363
- }
364
- this.setUserAgentString(view);
365
- }
366
-
367
- protected void setUserAgentString(WebView view) {
368
- if(mUserAgent != null) {
369
- view.getSettings().setUserAgentString(mUserAgent);
370
- } else if(mUserAgentWithApplicationName != null) {
371
- view.getSettings().setUserAgentString(mUserAgentWithApplicationName);
372
- } else {
373
- // handle unsets of `userAgent` prop
374
- view.getSettings().setUserAgentString(WebSettings.getDefaultUserAgent(view.getContext()));
375
- }
376
- }
377
-
378
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
379
- @ReactProp(name = "mediaPlaybackRequiresUserAction")
380
- public void setMediaPlaybackRequiresUserAction(WebView view, boolean requires) {
381
- view.getSettings().setMediaPlaybackRequiresUserGesture(requires);
382
- }
383
-
384
- @ReactProp(name = "saveFormDataDisabled")
385
- public void setSaveFormDataDisabled(WebView view, boolean disable) {
386
- view.getSettings().setSaveFormData(!disable);
387
- }
388
-
389
- @ReactProp(name = "injectedJavaScript")
390
- public void setInjectedJavaScript(WebView view, @Nullable String injectedJavaScript) {
391
- ((RNCWebView) view).setInjectedJavaScript(injectedJavaScript);
392
- }
393
-
394
- @ReactProp(name = "injectedJavaScriptBeforeContentLoaded")
395
- public void setInjectedJavaScriptBeforeContentLoaded(WebView view, @Nullable String injectedJavaScriptBeforeContentLoaded) {
396
- ((RNCWebView) view).setInjectedJavaScriptBeforeContentLoaded(injectedJavaScriptBeforeContentLoaded);
397
- }
398
-
399
- @ReactProp(name = "messagingEnabled")
400
- public void setMessagingEnabled(WebView view, boolean enabled) {
401
- ((RNCWebView) view).setMessagingEnabled(enabled);
402
- }
403
-
404
- @ReactProp(name = "messagingModuleName")
405
- public void setMessagingModuleName(WebView view, String moduleName) {
406
- ((RNCWebView) view).setMessagingModuleName(moduleName);
407
- }
408
-
409
- @ReactProp(name = "incognito")
410
- public void setIncognito(WebView view, boolean enabled) {
411
- // Don't do anything when incognito is disabled
412
- if (!enabled) {
413
- return;
414
- }
415
-
416
- // Remove all previous cookies
417
- CookieManager.getInstance().removeAllCookies(null);
418
-
419
- // Disable caching
420
- view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
421
- view.clearHistory();
422
- view.clearCache(true);
423
-
424
- // No form data or autofill enabled
425
- view.clearFormData();
426
- view.getSettings().setSavePassword(false);
427
- view.getSettings().setSaveFormData(false);
428
- }
429
-
430
- @ReactProp(name = "source")
431
- public void setSource(WebView view, @Nullable ReadableMap source) {
432
- if (source != null) {
433
- if (source.hasKey("uri")) {
434
- String url = source.getString("uri");
435
- String previousUrl = view.getUrl();
436
- if (previousUrl != null && previousUrl.equals(url)) {
437
- return;
438
- }
439
- if (source.hasKey("method")) {
440
- String method = source.getString("method");
441
- if (method.equalsIgnoreCase(HTTP_METHOD_POST)) {
442
- byte[] postData = null;
443
- if (source.hasKey("body")) {
444
- String body = source.getString("body");
445
- try {
446
- postData = body.getBytes("UTF-8");
447
- } catch (UnsupportedEncodingException e) {
448
- postData = body.getBytes();
449
- }
450
- }
451
- if (postData == null) {
452
- postData = new byte[0];
453
- }
454
- view.postUrl(url, postData);
455
- return;
456
- }
457
- }
458
- HashMap<String, String> headerMap = new HashMap<>();
459
- if (source.hasKey("headers")) {
460
- ReadableMap headers = source.getMap("headers");
461
- ReadableMapKeySetIterator iter = headers.keySetIterator();
462
- while (iter.hasNextKey()) {
463
- String key = iter.nextKey();
464
- if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) {
465
- if (view.getSettings() != null) {
466
- view.getSettings().setUserAgentString(headers.getString(key));
467
- }
468
- } else {
469
- headerMap.put(key, headers.getString(key));
470
- }
471
- }
472
- }
473
- view.loadUrl(url, headerMap);
474
- return;
475
- }
476
- }
477
- view.loadUrl(BLANK_URL);
478
- }
479
-
480
- @ReactProp(name = "basicAuthCredential")
481
- public void setBasicAuthCredential(WebView view, @Nullable ReadableMap credential) {
482
- @Nullable BasicAuthCredential basicAuthCredential = null;
483
- if (credential != null) {
484
- if (credential.hasKey("username") && credential.hasKey("password")) {
485
- String username = credential.getString("username");
486
- String password = credential.getString("password");
487
- basicAuthCredential = new BasicAuthCredential(username, password);
488
- }
489
- }
490
- ((RNCWebView) view).setBasicAuthCredential(basicAuthCredential);
491
- }
492
-
493
- @ReactProp(name = "onContentSizeChange")
494
- public void setOnContentSizeChange(WebView view, boolean sendContentSizeChangeEvents) {
495
- ((RNCWebView) view).setSendContentSizeChangeEvents(sendContentSizeChangeEvents);
496
- }
497
-
498
- @ReactProp(name = "mixedContentMode")
499
- public void setMixedContentMode(WebView view, @Nullable String mixedContentMode) {
500
- if (mixedContentMode == null || "never".equals(mixedContentMode)) {
501
- view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
502
- } else if ("always".equals(mixedContentMode)) {
503
- view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
504
- } else if ("compatibility".equals(mixedContentMode)) {
505
- view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
506
- }
507
- }
508
-
509
- @ReactProp(name = "urlPrefixesForDefaultIntent")
510
- public void setUrlPrefixesForDefaultIntent(
511
- WebView view,
512
- @Nullable ReadableArray urlPrefixesForDefaultIntent) {
513
- RNCWebViewClient client = ((RNCWebView) view).getRNCWebViewClient();
514
- if (client != null && urlPrefixesForDefaultIntent != null) {
515
- client.setUrlPrefixesForDefaultIntent(urlPrefixesForDefaultIntent);
516
- }
517
- }
518
-
519
- @ReactProp(name = "geolocationEnabled")
520
- public void setGeolocationEnabled(
521
- WebView view,
522
- @Nullable Boolean isGeolocationEnabled) {
523
- view.getSettings().setGeolocationEnabled(isGeolocationEnabled != null && isGeolocationEnabled);
524
- }
525
-
526
- @ReactProp(name = "onScroll")
527
- public void setOnScroll(WebView view, boolean hasScrollEvent) {
528
- ((RNCWebView) view).setHasScrollEvent(hasScrollEvent);
529
- }
530
-
531
- @ReactProp(name = "forceDarkOn")
532
- public void setForceDarkOn(WebView view, boolean enabled) {
533
- // Only Android 10+ support dark mode
534
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
535
- // Switch WebView dark mode
536
- if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
537
- int forceDarkMode = enabled ? WebSettingsCompat.FORCE_DARK_ON : WebSettingsCompat.FORCE_DARK_OFF;
538
- WebSettingsCompat.setForceDark(view.getSettings(), forceDarkMode);
539
- }
540
-
541
- // Set how WebView content should be darkened.
542
- // PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING: checks for the "color-scheme" <meta> tag.
543
- // If present, it uses media queries. If absent, it applies user-agent (automatic)
544
- // More information about Force Dark Strategy can be found here:
545
- // https://developer.android.com/reference/androidx/webkit/WebSettingsCompat#setForceDarkStrategy(android.webkit.WebSettings)
546
- if (enabled && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
547
- WebSettingsCompat.setForceDarkStrategy(view.getSettings(), WebSettingsCompat.DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING);
548
- }
549
- }
550
- }
551
-
552
- @ReactProp(name = "minimumFontSize")
553
- public void setMinimumFontSize(WebView view, int fontSize) {
554
- view.getSettings().setMinimumFontSize(fontSize);
555
- }
556
-
557
- @ReactProp(name = "allowsProtectedMedia")
558
- public void setAllowsProtectedMedia(WebView view, boolean enabled) {
559
- // This variable is used to keep consistency
560
- // in case a new WebChromeClient is created
561
- // (eg. when mAllowsFullScreenVideo changes)
562
- mAllowsProtectedMedia = enabled;
563
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
564
- WebChromeClient client = view.getWebChromeClient();
565
- if (client != null && client instanceof RNCWebChromeClient) {
566
- ((RNCWebChromeClient) client).setAllowsProtectedMedia(enabled);
567
- }
568
- }
569
- }
570
-
571
- @Override
572
- protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
573
- // Do not register default touch emitter and let WebView implementation handle touches
574
- view.setWebViewClient(new RNCWebViewClient());
575
- }
576
-
577
- @Override
578
- public Map getExportedCustomDirectEventTypeConstants() {
579
- Map export = super.getExportedCustomDirectEventTypeConstants();
580
- if (export == null) {
581
- export = MapBuilder.newHashMap();
582
- }
583
- // Default events but adding them here explicitly for clarity
584
- export.put(TopLoadingStartEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingStart"));
585
- export.put(TopLoadingFinishEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingFinish"));
586
- export.put(TopLoadingErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingError"));
587
- export.put(TopMessageEvent.EVENT_NAME, MapBuilder.of("registrationName", "onMessage"));
588
- // !Default events but adding them here explicitly for clarity
589
-
590
- export.put(TopLoadingProgressEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingProgress"));
591
- export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
592
- export.put(ScrollEventType.getJSEventName(ScrollEventType.SCROLL), MapBuilder.of("registrationName", "onScroll"));
593
- return export;
594
- }
595
-
596
- @Override
597
- public @Nullable
598
- Map<String, Integer> getCommandsMap() {
599
- return MapBuilder.<String, Integer>builder()
600
- .put("goBack", COMMAND_GO_BACK)
601
- .put("goForward", COMMAND_GO_FORWARD)
602
- .put("reload", COMMAND_RELOAD)
603
- .put("stopLoading", COMMAND_STOP_LOADING)
604
- .put("postMessage", COMMAND_POST_MESSAGE)
605
- .put("injectJavaScript", COMMAND_INJECT_JAVASCRIPT)
606
- .put("loadUrl", COMMAND_LOAD_URL)
607
- .put("requestFocus", COMMAND_FOCUS)
608
- .put("clearFormData", COMMAND_CLEAR_FORM_DATA)
609
- .put("clearCache", COMMAND_CLEAR_CACHE)
610
- .put("clearHistory", COMMAND_CLEAR_HISTORY)
611
- .build();
612
- }
613
-
614
- @Override
615
- public void receiveCommand(@NonNull WebView root, String commandId, @Nullable ReadableArray args) {
616
- switch (commandId) {
617
- case "goBack":
618
- root.goBack();
619
- break;
620
- case "goForward":
621
- root.goForward();
622
- break;
623
- case "reload":
624
- root.reload();
625
- break;
626
- case "stopLoading":
627
- root.stopLoading();
628
- break;
629
- case "postMessage":
630
- try {
631
- RNCWebView reactWebView = (RNCWebView) root;
632
- JSONObject eventInitDict = new JSONObject();
633
- eventInitDict.put("data", args.getString(0));
634
- reactWebView.evaluateJavascriptWithFallback("document.dispatchEvent(new MessageEvent('message', " + eventInitDict.toString() + "));");
635
- } catch (JSONException e) {
636
- throw new RuntimeException(e);
637
- }
638
- break;
639
- case "injectJavaScript":
640
- RNCWebView reactWebView = (RNCWebView) root;
641
- reactWebView.evaluateJavascriptWithFallback(args.getString(0));
642
- break;
643
- case "loadUrl":
644
- if (args == null) {
645
- throw new RuntimeException("Arguments for loading an url are null!");
646
- }
647
- ((RNCWebView) root).progressChangedFilter.setWaitingForCommandLoadUrl(false);
648
- root.loadUrl(args.getString(0));
649
- break;
650
- case "requestFocus":
651
- root.requestFocus();
652
- break;
653
- case "clearFormData":
654
- root.clearFormData();
655
- break;
656
- case "clearCache":
657
- boolean includeDiskFiles = args != null && args.getBoolean(0);
658
- root.clearCache(includeDiskFiles);
659
- break;
660
- case "clearHistory":
661
- root.clearHistory();
662
- break;
663
- }
664
- super.receiveCommand(root, commandId, args);
665
- }
666
-
667
- @Override
668
- public void onDropViewInstance(WebView webView) {
669
- super.onDropViewInstance(webView);
670
- ((ThemedReactContext) webView.getContext()).removeLifecycleEventListener((RNCWebView) webView);
671
- ((RNCWebView) webView).cleanupCallbacksAndDestroy();
672
- mWebChromeClient = null;
673
- }
674
-
675
- public static RNCWebViewModule getModule(ReactContext reactContext) {
676
- return reactContext.getNativeModule(RNCWebViewModule.class);
677
- }
678
-
679
- protected void setupWebChromeClient(ReactContext reactContext, WebView webView) {
680
- Activity activity = reactContext.getCurrentActivity();
681
- if (mWebChromeClient != null) {
682
- mWebChromeClient.onHideCustomView();
683
- }
684
-
685
- mWebChromeClient = new RNCWebChromeClient(reactContext, webView) {
686
- @Override
687
- public Bitmap getDefaultVideoPoster() {
688
- return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
689
- }
690
- };
691
- mWebChromeClient.setAllowsProtectedMedia(mAllowsProtectedMedia);
692
- webView.setWebChromeClient(mWebChromeClient);
693
- }
694
-
695
- protected static class RNCWebViewClient extends WebViewClient {
696
-
697
- protected boolean mLastLoadFailed = false;
698
- protected @Nullable
699
- ReadableArray mUrlPrefixesForDefaultIntent;
700
- protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
701
- protected @Nullable String ignoreErrFailedForThisURL = null;
702
- protected @Nullable BasicAuthCredential basicAuthCredential = null;
703
-
704
- public void setIgnoreErrFailedForThisURL(@Nullable String url) {
705
- ignoreErrFailedForThisURL = url;
706
- }
707
-
708
- public void setBasicAuthCredential(@Nullable BasicAuthCredential credential) {
709
- basicAuthCredential = credential;
710
- }
711
-
712
- @Override
713
- public void onPageFinished(WebView webView, String url) {
714
- super.onPageFinished(webView, url);
715
-
716
- if (!mLastLoadFailed) {
717
- RNCWebView reactWebView = (RNCWebView) webView;
718
-
719
- reactWebView.callInjectedJavaScript();
720
-
721
- emitFinishEvent(webView, url);
722
- }
723
- }
724
-
725
- @Override
726
- public void onPageStarted(WebView webView, String url, Bitmap favicon) {
727
- super.onPageStarted(webView, url, favicon);
728
- mLastLoadFailed = false;
729
-
730
- RNCWebView reactWebView = (RNCWebView) webView;
731
- reactWebView.callInjectedJavaScriptBeforeContentLoaded();
732
-
733
- ((RNCWebView) webView).dispatchEvent(
734
- webView,
735
- new TopLoadingStartEvent(
736
- webView.getId(),
737
- createWebViewEvent(webView, url)));
738
- }
739
-
740
- @Override
741
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
742
- final RNCWebView rncWebView = (RNCWebView) view;
743
- final boolean isJsDebugging = ((ReactContext) view.getContext()).getJavaScriptContextHolder().get() == 0;
744
-
745
- if (!isJsDebugging && rncWebView.mCatalystInstance != null) {
746
- final Pair<Integer, AtomicReference<ShouldOverrideCallbackState>> lock = RNCWebViewModule.shouldOverrideUrlLoadingLock.getNewLock();
747
- final int lockIdentifier = lock.first;
748
- final AtomicReference<ShouldOverrideCallbackState> lockObject = lock.second;
749
-
750
- final WritableMap event = createWebViewEvent(view, url);
751
- event.putInt("lockIdentifier", lockIdentifier);
752
- rncWebView.sendDirectMessage("onShouldStartLoadWithRequest", event);
753
-
754
- try {
755
- assert lockObject != null;
756
- synchronized (lockObject) {
757
- final long startTime = SystemClock.elapsedRealtime();
758
- while (lockObject.get() == ShouldOverrideCallbackState.UNDECIDED) {
759
- if (SystemClock.elapsedRealtime() - startTime > SHOULD_OVERRIDE_URL_LOADING_TIMEOUT) {
760
- FLog.w(TAG, "Did not receive response to shouldOverrideUrlLoading in time, defaulting to allow loading.");
761
- RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
762
- return false;
763
- }
764
- lockObject.wait(SHOULD_OVERRIDE_URL_LOADING_TIMEOUT);
765
- }
766
- }
767
- } catch (InterruptedException e) {
768
- FLog.e(TAG, "shouldOverrideUrlLoading was interrupted while waiting for result.", e);
769
- RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
770
- return false;
771
- }
772
-
773
- final boolean shouldOverride = lockObject.get() == ShouldOverrideCallbackState.SHOULD_OVERRIDE;
774
- RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
775
-
776
- return shouldOverride;
777
- } else {
778
- FLog.w(TAG, "Couldn't use blocking synchronous call for onShouldStartLoadWithRequest due to debugging or missing Catalyst instance, falling back to old event-and-load.");
779
- progressChangedFilter.setWaitingForCommandLoadUrl(true);
780
- ((RNCWebView) view).dispatchEvent(
781
- view,
782
- new TopShouldStartLoadWithRequestEvent(
783
- view.getId(),
784
- createWebViewEvent(view, url)));
785
- return true;
786
- }
787
- }
788
-
789
- @TargetApi(Build.VERSION_CODES.N)
790
- @Override
791
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
792
- final String url = request.getUrl().toString();
793
- return this.shouldOverrideUrlLoading(view, url);
794
- }
795
-
796
- @Override
797
- public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
798
- if (basicAuthCredential != null) {
799
- handler.proceed(basicAuthCredential.username, basicAuthCredential.password);
800
- return;
801
- }
802
- super.onReceivedHttpAuthRequest(view, handler, host, realm);
803
- }
804
-
805
- @Override
806
- public void onReceivedSslError(final WebView webView, final SslErrorHandler handler, final SslError error) {
807
- // onReceivedSslError is called for most requests, per Android docs: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%2520android.webkit.SslErrorHandler,%2520android.net.http.SslError)
808
- // WebView.getUrl() will return the top-level window URL.
809
- // If a top-level navigation triggers this error handler, the top-level URL will be the failing URL (not the URL of the currently-rendered page).
810
- // This is desired behavior. We later use these values to determine whether the request is a top-level navigation or a subresource request.
811
- String topWindowUrl = webView.getUrl();
812
- String failingUrl = error.getUrl();
813
-
814
- // Cancel request after obtaining top-level URL.
815
- // If request is cancelled before obtaining top-level URL, undesired behavior may occur.
816
- // Undesired behavior: Return value of WebView.getUrl() may be the current URL instead of the failing URL.
817
- handler.cancel();
818
-
819
- if (!topWindowUrl.equalsIgnoreCase(failingUrl)) {
820
- // If error is not due to top-level navigation, then do not call onReceivedError()
821
- Log.w(TAG, "Resource blocked from loading due to SSL error. Blocked URL: "+failingUrl);
822
- return;
823
- }
824
-
825
- int code = error.getPrimaryError();
826
- String description = "";
827
- String descriptionPrefix = "SSL error: ";
828
-
829
- // https://developer.android.com/reference/android/net/http/SslError.html
830
- switch (code) {
831
- case SslError.SSL_DATE_INVALID:
832
- description = "The date of the certificate is invalid";
833
- break;
834
- case SslError.SSL_EXPIRED:
835
- description = "The certificate has expired";
836
- break;
837
- case SslError.SSL_IDMISMATCH:
838
- description = "Hostname mismatch";
839
- break;
840
- case SslError.SSL_INVALID:
841
- description = "A generic error occurred";
842
- break;
843
- case SslError.SSL_NOTYETVALID:
844
- description = "The certificate is not yet valid";
845
- break;
846
- case SslError.SSL_UNTRUSTED:
847
- description = "The certificate authority is not trusted";
848
- break;
849
- default:
850
- description = "Unknown SSL Error";
851
- break;
852
- }
853
-
854
- description = descriptionPrefix + description;
855
-
856
- this.onReceivedError(
857
- webView,
858
- code,
859
- description,
860
- failingUrl
861
- );
862
- }
863
-
864
- @Override
865
- public void onReceivedError(
866
- WebView webView,
867
- int errorCode,
868
- String description,
869
- String failingUrl) {
870
-
871
- if (ignoreErrFailedForThisURL != null
872
- && failingUrl.equals(ignoreErrFailedForThisURL)
873
- && errorCode == -1
874
- && description.equals("net::ERR_FAILED")) {
875
-
876
- // This is a workaround for a bug in the WebView.
877
- // See these chromium issues for more context:
878
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1023678
879
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1050635
880
- // This entire commit should be reverted once this bug is resolved in chromium.
881
- setIgnoreErrFailedForThisURL(null);
882
- return;
883
- }
884
-
885
- super.onReceivedError(webView, errorCode, description, failingUrl);
886
- mLastLoadFailed = true;
887
-
888
- // In case of an error JS side expect to get a finish event first, and then get an error event
889
- // Android WebView does it in the opposite way, so we need to simulate that behavior
890
- emitFinishEvent(webView, failingUrl);
891
-
892
- WritableMap eventData = createWebViewEvent(webView, failingUrl);
893
- eventData.putDouble("code", errorCode);
894
- eventData.putString("description", description);
895
-
896
- ((RNCWebView) webView).dispatchEvent(
897
- webView,
898
- new TopLoadingErrorEvent(webView.getId(), eventData));
899
- }
900
-
901
- protected void emitFinishEvent(WebView webView, String url) {
902
- ((RNCWebView) webView).dispatchEvent(
903
- webView,
904
- new TopLoadingFinishEvent(
905
- webView.getId(),
906
- createWebViewEvent(webView, url)));
907
- }
908
-
909
- protected WritableMap createWebViewEvent(WebView webView, String url) {
910
- WritableMap event = Arguments.createMap();
911
- event.putDouble("target", webView.getId());
912
- // Don't use webView.getUrl() here, the URL isn't updated to the new value yet in callbacks
913
- // like onPageFinished
914
- event.putString("url", url);
915
- event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100);
916
- event.putString("title", webView.getTitle());
917
- event.putBoolean("canGoBack", webView.canGoBack());
918
- event.putBoolean("canGoForward", webView.canGoForward());
919
- return event;
920
- }
921
-
922
- public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
923
- mUrlPrefixesForDefaultIntent = specialUrls;
924
- }
925
-
926
- public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
927
- progressChangedFilter = filter;
928
- }
929
- }
930
-
931
- protected static class RNCWebChromeClient extends WebChromeClient implements LifecycleEventListener {
932
- protected static final FrameLayout.LayoutParams FULLSCREEN_LAYOUT_PARAMS = new FrameLayout.LayoutParams(
933
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER);
934
-
935
- @RequiresApi(api = Build.VERSION_CODES.KITKAT)
936
- protected static final int FULLSCREEN_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
937
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
938
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
939
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
940
- View.SYSTEM_UI_FLAG_FULLSCREEN |
941
- View.SYSTEM_UI_FLAG_IMMERSIVE |
942
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
943
-
944
- protected static final int COMMON_PERMISSION_REQUEST = 3;
945
-
946
- protected ReactContext mReactContext;
947
- protected View mWebView;
948
-
949
- protected View mVideoView;
950
- protected WebChromeClient.CustomViewCallback mCustomViewCallback;
951
-
952
- /*
953
- * - Permissions -
954
- * As native permissions are asynchronously handled by the PermissionListener, many fields have
955
- * to be stored to send permissions results to the webview
956
- */
957
-
958
- // Webview camera & audio permission callback
959
- protected PermissionRequest permissionRequest;
960
- // Webview camera & audio permission already granted
961
- protected List<String> grantedPermissions;
962
-
963
- // Webview geolocation permission callback
964
- protected GeolocationPermissions.Callback geolocationPermissionCallback;
965
- // Webview geolocation permission origin callback
966
- protected String geolocationPermissionOrigin;
967
-
968
- // true if native permissions dialog is shown, false otherwise
969
- protected boolean permissionsRequestShown = false;
970
- // Pending Android permissions for the next request
971
- protected List<String> pendingPermissions = new ArrayList<>();
972
-
973
- protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
974
-
975
- // True if protected media should be allowed, false otherwise
976
- protected boolean mAllowsProtectedMedia = false;
977
-
978
- public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
979
- this.mReactContext = reactContext;
980
- this.mWebView = webView;
981
- }
982
-
983
- @Override
984
- public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
985
-
986
- final WebView newWebView = new WebView(view.getContext());
987
- final WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
988
- transport.setWebView(newWebView);
989
- resultMsg.sendToTarget();
990
-
991
- return true;
992
- }
993
-
994
- @Override
995
- public boolean onConsoleMessage(ConsoleMessage message) {
996
- if (ReactBuildConfig.DEBUG) {
997
- return super.onConsoleMessage(message);
998
- }
999
- // Ignore console logs in non debug builds.
1000
- return true;
1001
- }
1002
-
1003
- @Override
1004
- public void onProgressChanged(WebView webView, int newProgress) {
1005
- super.onProgressChanged(webView, newProgress);
1006
- final String url = webView.getUrl();
1007
- if (progressChangedFilter.isWaitingForCommandLoadUrl()) {
1008
- return;
1009
- }
1010
- WritableMap event = Arguments.createMap();
1011
- event.putDouble("target", webView.getId());
1012
- event.putString("title", webView.getTitle());
1013
- event.putString("url", url);
1014
- event.putBoolean("canGoBack", webView.canGoBack());
1015
- event.putBoolean("canGoForward", webView.canGoForward());
1016
- event.putDouble("progress", (float) newProgress / 100);
1017
- ((RNCWebView) webView).dispatchEvent(
1018
- webView,
1019
- new TopLoadingProgressEvent(
1020
- webView.getId(),
1021
- event));
1022
- }
1023
-
1024
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
1025
- @Override
1026
- public void onPermissionRequest(final PermissionRequest request) {
1027
- request.deny();
1028
- }
1029
-
1030
-
1031
- @Override
1032
- public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
1033
-
1034
- // Always deny
1035
- callback.invoke(origin, false, false);
1036
-
1037
- }
1038
-
1039
- private PermissionAwareActivity getPermissionAwareActivity() {
1040
- Activity activity = mReactContext.getCurrentActivity();
1041
- if (activity == null) {
1042
- throw new IllegalStateException("Tried to use permissions API while not attached to an Activity.");
1043
- } else if (!(activity instanceof PermissionAwareActivity)) {
1044
- throw new IllegalStateException("Tried to use permissions API but the host Activity doesn't implement PermissionAwareActivity.");
1045
- }
1046
- return (PermissionAwareActivity) activity;
1047
- }
1048
-
1049
- protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType) {
1050
- getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptType);
1051
- }
1052
-
1053
- protected void openFileChooser(ValueCallback<Uri> filePathCallback) {
1054
- getModule(mReactContext).startPhotoPickerIntent(filePathCallback, "");
1055
- }
1056
-
1057
- protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
1058
- getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptType);
1059
- }
1060
-
1061
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
1062
- @Override
1063
- public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
1064
- String[] acceptTypes = fileChooserParams.getAcceptTypes();
1065
- boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
1066
- return getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptTypes, allowMultiple);
1067
- }
1068
-
1069
- @Override
1070
- public void onHostResume() {
1071
- if (mVideoView != null && mVideoView.getSystemUiVisibility() != FULLSCREEN_SYSTEM_UI_VISIBILITY) {
1072
- mVideoView.setSystemUiVisibility(FULLSCREEN_SYSTEM_UI_VISIBILITY);
1073
- }
1074
- }
1075
-
1076
- @Override
1077
- public void onHostPause() { }
1078
-
1079
- @Override
1080
- public void onHostDestroy() { }
1081
-
1082
- protected ViewGroup getRootView() {
1083
- return (ViewGroup) mReactContext.getCurrentActivity().findViewById(android.R.id.content);
1084
- }
1085
-
1086
- public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
1087
- progressChangedFilter = filter;
1088
- }
1089
-
1090
- /**
1091
- * Set whether or not protected media should be allowed
1092
- * /!\ Setting this to false won't revoke permission already granted to the current webpage.
1093
- * In order to do so, you'd need to reload the page /!\
1094
- */
1095
- public void setAllowsProtectedMedia(boolean enabled) {
1096
- mAllowsProtectedMedia = enabled;
1097
- }
1098
- }
1099
-
1100
- /**
1101
- * Subclass of {@link WebView} that implements {@link LifecycleEventListener} interface in order
1102
- * to call {@link WebView#destroy} on activity destroy event and also to clear the client
1103
- */
1104
- protected static class RNCWebView extends WebView implements LifecycleEventListener {
1105
- protected @Nullable
1106
- String injectedJS;
1107
- protected @Nullable
1108
- String injectedJSBeforeContentLoaded;
1109
-
1110
- protected boolean messagingEnabled = false;
1111
- protected @Nullable
1112
- String messagingModuleName;
1113
- protected @Nullable
1114
- RNCWebViewClient mRNCWebViewClient;
1115
- protected @Nullable
1116
- CatalystInstance mCatalystInstance;
1117
- protected boolean sendContentSizeChangeEvents = false;
1118
- private OnScrollDispatchHelper mOnScrollDispatchHelper;
1119
- protected boolean hasScrollEvent = false;
1120
- protected boolean nestedScrollEnabled = false;
1121
- protected ProgressChangedFilter progressChangedFilter;
1122
-
1123
- /**
1124
- * WebView must be created with an context of the current activity
1125
- * <p>
1126
- * Activity Context is required for creation of dialogs internally by WebView
1127
- * Reactive Native needed for access to ReactNative internal system functionality
1128
- */
1129
- public RNCWebView(ThemedReactContext reactContext) {
1130
- super(reactContext);
1131
- this.createCatalystInstance();
1132
- progressChangedFilter = new ProgressChangedFilter();
1133
- }
1134
-
1135
- public void setIgnoreErrFailedForThisURL(String url) {
1136
- mRNCWebViewClient.setIgnoreErrFailedForThisURL(url);
1137
- }
1138
-
1139
- public void setBasicAuthCredential(BasicAuthCredential credential) {
1140
- mRNCWebViewClient.setBasicAuthCredential(credential);
1141
- }
1142
-
1143
- public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
1144
- this.sendContentSizeChangeEvents = sendContentSizeChangeEvents;
1145
- }
1146
-
1147
- public void setHasScrollEvent(boolean hasScrollEvent) {
1148
- this.hasScrollEvent = hasScrollEvent;
1149
- }
1150
-
1151
- public void setNestedScrollEnabled(boolean nestedScrollEnabled) {
1152
- this.nestedScrollEnabled = nestedScrollEnabled;
1153
- }
1154
-
1155
- @Override
1156
- public void onHostResume() {
1157
- // do nothing
1158
- }
1159
-
1160
- @Override
1161
- public void onHostPause() {
1162
- // do nothing
1163
- }
1164
-
1165
- @Override
1166
- public void onHostDestroy() {
1167
- cleanupCallbacksAndDestroy();
1168
- }
1169
-
1170
- @Override
1171
- public boolean onTouchEvent(MotionEvent event) {
1172
- if (this.nestedScrollEnabled) {
1173
- requestDisallowInterceptTouchEvent(true);
1174
- }
1175
- return super.onTouchEvent(event);
1176
- }
1177
-
1178
- @Override
1179
- protected void onSizeChanged(int w, int h, int ow, int oh) {
1180
- super.onSizeChanged(w, h, ow, oh);
1181
-
1182
- if (sendContentSizeChangeEvents) {
1183
- dispatchEvent(
1184
- this,
1185
- new ContentSizeChangeEvent(
1186
- this.getId(),
1187
- w,
1188
- h
1189
- )
1190
- );
1191
- }
1192
- }
1193
-
1194
- @Override
1195
- public void setWebViewClient(WebViewClient client) {
1196
- super.setWebViewClient(client);
1197
- if (client instanceof RNCWebViewClient) {
1198
- mRNCWebViewClient = (RNCWebViewClient) client;
1199
- mRNCWebViewClient.setProgressChangedFilter(progressChangedFilter);
1200
- }
1201
- }
1202
-
1203
- WebChromeClient mWebChromeClient;
1204
- @Override
1205
- public void setWebChromeClient(WebChromeClient client) {
1206
- this.mWebChromeClient = client;
1207
- super.setWebChromeClient(client);
1208
- if (client instanceof RNCWebChromeClient) {
1209
- ((RNCWebChromeClient) client).setProgressChangedFilter(progressChangedFilter);
1210
- }
1211
- }
1212
-
1213
- public @Nullable
1214
- RNCWebViewClient getRNCWebViewClient() {
1215
- return mRNCWebViewClient;
1216
- }
1217
-
1218
- public void setInjectedJavaScript(@Nullable String js) {
1219
- injectedJS = js;
1220
- }
1221
-
1222
- public void setInjectedJavaScriptBeforeContentLoaded(@Nullable String js) {
1223
- injectedJSBeforeContentLoaded = js;
1224
- }
1225
-
1226
- protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
1227
- return new RNCWebViewBridge(webView);
1228
- }
1229
-
1230
- protected void createCatalystInstance() {
1231
- ReactContext reactContext = (ReactContext) this.getContext();
1232
-
1233
- if (reactContext != null) {
1234
- mCatalystInstance = reactContext.getCatalystInstance();
1235
- }
1236
- }
1237
-
1238
- @SuppressLint("AddJavascriptInterface")
1239
- public void setMessagingEnabled(boolean enabled) {
1240
- if (messagingEnabled == enabled) {
1241
- return;
1242
- }
1243
-
1244
- messagingEnabled = enabled;
1245
-
1246
- if (enabled) {
1247
- addJavascriptInterface(createRNCWebViewBridge(this), JAVASCRIPT_INTERFACE);
1248
- } else {
1249
- removeJavascriptInterface(JAVASCRIPT_INTERFACE);
1250
- }
1251
- }
1252
-
1253
- public void setMessagingModuleName(String moduleName) {
1254
- messagingModuleName = moduleName;
1255
- }
1256
-
1257
- protected void evaluateJavascriptWithFallback(String script) {
1258
- evaluateJavascript(script, null);
1259
- }
1260
-
1261
- public void callInjectedJavaScript() {
1262
- if (getSettings().getJavaScriptEnabled() &&
1263
- injectedJS != null &&
1264
- !TextUtils.isEmpty(injectedJS)) {
1265
- evaluateJavascriptWithFallback("(function() {\n" + injectedJS + ";\n})();");
1266
- }
1267
- }
1268
-
1269
- public void callInjectedJavaScriptBeforeContentLoaded() {
1270
- if (getSettings().getJavaScriptEnabled() &&
1271
- injectedJSBeforeContentLoaded != null &&
1272
- !TextUtils.isEmpty(injectedJSBeforeContentLoaded)) {
1273
- evaluateJavascriptWithFallback("(function() {\n" + injectedJSBeforeContentLoaded + ";\n})();");
1274
- }
1275
- }
1276
-
1277
- public void onMessage(String message) {
1278
- ReactContext reactContext = (ReactContext) this.getContext();
1279
- RNCWebView mContext = this;
1280
-
1281
- if (mRNCWebViewClient != null) {
1282
- WebView webView = this;
1283
- webView.post(new Runnable() {
1284
- @Override
1285
- public void run() {
1286
- if (mRNCWebViewClient == null) {
1287
- return;
1288
- }
1289
- WritableMap data = mRNCWebViewClient.createWebViewEvent(webView, webView.getUrl());
1290
- data.putString("data", message);
1291
-
1292
- if (mCatalystInstance != null) {
1293
- mContext.sendDirectMessage("onMessage", data);
1294
- } else {
1295
- dispatchEvent(webView, new TopMessageEvent(webView.getId(), data));
1296
- }
1297
- }
1298
- });
1299
- } else {
1300
- WritableMap eventData = Arguments.createMap();
1301
- eventData.putString("data", message);
1302
-
1303
- if (mCatalystInstance != null) {
1304
- this.sendDirectMessage("onMessage", eventData);
1305
- } else {
1306
- dispatchEvent(this, new TopMessageEvent(this.getId(), eventData));
1307
- }
1308
- }
1309
- }
1310
-
1311
- protected void sendDirectMessage(final String method, WritableMap data) {
1312
- WritableNativeMap event = new WritableNativeMap();
1313
- event.putMap("nativeEvent", data);
1314
-
1315
- WritableNativeArray params = new WritableNativeArray();
1316
- params.pushMap(event);
1317
-
1318
- mCatalystInstance.callFunction(messagingModuleName, method, params);
1319
- }
1320
-
1321
- protected void onScrollChanged(int x, int y, int oldX, int oldY) {
1322
- super.onScrollChanged(x, y, oldX, oldY);
1323
-
1324
- if (!hasScrollEvent) {
1325
- return;
1326
- }
1327
-
1328
- if (mOnScrollDispatchHelper == null) {
1329
- mOnScrollDispatchHelper = new OnScrollDispatchHelper();
1330
- }
1331
-
1332
- if (mOnScrollDispatchHelper.onScrollChanged(x, y)) {
1333
- ScrollEvent event = ScrollEvent.obtain(
1334
- this.getId(),
1335
- ScrollEventType.SCROLL,
1336
- x,
1337
- y,
1338
- mOnScrollDispatchHelper.getXFlingVelocity(),
1339
- mOnScrollDispatchHelper.getYFlingVelocity(),
1340
- this.computeHorizontalScrollRange(),
1341
- this.computeVerticalScrollRange(),
1342
- this.getWidth(),
1343
- this.getHeight());
1344
-
1345
- dispatchEvent(this, event);
1346
- }
1347
- }
1348
-
1349
- protected void dispatchEvent(WebView webView, Event event) {
1350
- ReactContext reactContext = (ReactContext) webView.getContext();
1351
- EventDispatcher eventDispatcher =
1352
- reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
1353
- eventDispatcher.dispatchEvent(event);
1354
- }
1355
-
1356
- protected void cleanupCallbacksAndDestroy() {
1357
- setWebViewClient(null);
1358
- destroy();
1359
- }
1360
-
1361
- @Override
1362
- public void destroy() {
1363
- if (mWebChromeClient != null) {
1364
- mWebChromeClient.onHideCustomView();
1365
- }
1366
- super.destroy();
1367
- }
1368
-
1369
- protected class RNCWebViewBridge {
1370
- RNCWebView mContext;
1371
-
1372
- RNCWebViewBridge(RNCWebView c) {
1373
- mContext = c;
1374
- }
1375
-
1376
- /**
1377
- * This method is called whenever JavaScript running within the web view calls:
1378
- * - window[JAVASCRIPT_INTERFACE].postMessage
1379
- */
1380
- @JavascriptInterface
1381
- public void postMessage(String message) {
1382
- mContext.onMessage(message);
1383
- }
1384
- }
1385
-
1386
- protected static class ProgressChangedFilter {
1387
- private boolean waitingForCommandLoadUrl = false;
1388
-
1389
- public void setWaitingForCommandLoadUrl(boolean isWaiting) {
1390
- waitingForCommandLoadUrl = isWaiting;
1391
- }
1392
-
1393
- public boolean isWaitingForCommandLoadUrl() {
1394
- return waitingForCommandLoadUrl;
1395
- }
1396
- }
1397
- }
1398
- }
1399
-
1400
- class BasicAuthCredential {
1401
- String username;
1402
- String password;
1403
-
1404
- BasicAuthCredential(String username, String password) {
1405
- this.username = username;
1406
- this.password = password;
1407
- }
1408
- }