@javascriptcommon/react-native-webview 13.12.20 → 13.16.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 (31) hide show
  1. package/android/gradle.properties +1 -1
  2. package/android/src/main/AndroidManifest.xml +12 -0
  3. package/android/src/main/AndroidManifestNew.xml +13 -0
  4. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java +52 -25
  5. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java +27 -25
  6. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt +11 -8
  7. package/android/src/main/java/com/reactnativecommunity/webview/events/SubResourceErrorEvent.kt +25 -0
  8. package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java +12 -1
  9. package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java +7 -0
  10. package/apple/RNCWebView.h +1 -1
  11. package/apple/RNCWebView.mm +26 -17
  12. package/apple/RNCWebViewImpl.h +2 -1
  13. package/apple/RNCWebViewImpl.m +62 -25
  14. package/apple/RNCWebViewManager.mm +4 -0
  15. package/index.d.ts +2 -3
  16. package/lib/RNCWebViewNativeComponent.d.ts +24 -21
  17. package/lib/RNCWebViewNativeComponent.js +1 -1
  18. package/lib/WebView.android.js +1 -1
  19. package/lib/WebView.ios.js +1 -1
  20. package/lib/WebView.js +1 -1
  21. package/lib/WebView.macos.js +1 -1
  22. package/lib/WebView.windows.js +1 -1
  23. package/lib/WebViewShared.d.ts +3 -1
  24. package/lib/WebViewShared.js +1 -1
  25. package/lib/WebViewTypes.d.ts +32 -8
  26. package/lib/WebViewTypes.js +1 -1
  27. package/package.json +9 -1
  28. package/src/RNCWebViewNativeComponent.ts +27 -21
  29. package/src/WebView.android.tsx +4 -0
  30. package/src/WebViewShared.tsx +10 -0
  31. package/src/WebViewTypes.ts +36 -8
@@ -1,5 +1,5 @@
1
1
  ReactNativeWebView_kotlinVersion=1.6.0
2
- ReactNativeWebView_webkitVersion=1.4.0
2
+ ReactNativeWebView_webkitVersion=1.14.0
3
3
  ReactNativeWebView_compileSdkVersion=31
4
4
  ReactNativeWebView_targetSdkVersion=31
5
5
  ReactNativeWebView_minSdkVersion=21
@@ -1,6 +1,18 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
2
  package="com.reactnativecommunity.webview">
3
3
 
4
+ <queries>
5
+ <intent>
6
+ <action android:name="org.chromium.intent.action.PAY"/>
7
+ </intent>
8
+ <intent>
9
+ <action android:name="org.chromium.intent.action.IS_READY_TO_PAY"/>
10
+ </intent>
11
+ <intent>
12
+ <action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS"/>
13
+ </intent>
14
+ </queries>
15
+
4
16
  <application>
5
17
  <provider
6
18
  android:name=".RNCWebViewFileProvider"
@@ -1,4 +1,17 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
3
+ <queries>
4
+ <intent>
5
+ <action android:name="org.chromium.intent.action.PAY"/>
6
+ </intent>
7
+ <intent>
8
+ <action android:name="org.chromium.intent.action.IS_READY_TO_PAY"/>
9
+ </intent>
10
+ <intent>
11
+ <action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS"/>
12
+ </intent>
13
+ </queries>
14
+
2
15
  <application>
3
16
  <provider
4
17
  android:name=".RNCWebViewFileProvider"
@@ -2,6 +2,7 @@ package com.reactnativecommunity.webview;
2
2
 
3
3
  import android.annotation.SuppressLint;
4
4
  import android.graphics.Rect;
5
+ import android.net.Uri;
5
6
  import android.text.TextUtils;
6
7
  import android.view.ActionMode;
7
8
  import android.view.Menu;
@@ -14,7 +15,12 @@ import android.webkit.WebChromeClient;
14
15
  import android.webkit.WebView;
15
16
  import android.webkit.WebViewClient;
16
17
 
18
+ import androidx.annotation.NonNull;
17
19
  import androidx.annotation.Nullable;
20
+ import androidx.webkit.JavaScriptReplyProxy;
21
+ import androidx.webkit.WebMessageCompat;
22
+ import androidx.webkit.WebViewCompat;
23
+ import androidx.webkit.WebViewFeature;
18
24
 
19
25
  import com.facebook.common.logging.FLog;
20
26
  import com.facebook.react.bridge.Arguments;
@@ -38,6 +44,7 @@ import org.json.JSONObject;
38
44
 
39
45
  import java.util.List;
40
46
  import java.util.Map;
47
+ import java.util.Set;
41
48
 
42
49
  public class RNCWebView extends WebView implements LifecycleEventListener {
43
50
  protected @Nullable
@@ -46,7 +53,9 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
46
53
  String injectedJSBeforeContentLoaded;
47
54
  protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView";
48
55
  protected @Nullable
49
- RNCWebViewBridge bridge;
56
+ RNCWebViewBridge fallbackBridge;
57
+ protected @Nullable
58
+ WebViewCompat.WebMessageListener bridgeListener = null;
50
59
 
51
60
  /**
52
61
  * android.webkit.WebChromeClient fundamentally does not support JS injection into frames other
@@ -81,10 +90,6 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
81
90
  progressChangedFilter = new ProgressChangedFilter();
82
91
  }
83
92
 
84
- public void setIgnoreErrFailedForThisURL(String url) {
85
- mRNCWebViewClient.setIgnoreErrFailedForThisURL(url);
86
- }
87
-
88
93
  public void setBasicAuthCredential(RNCBasicAuthCredential credential) {
89
94
  mRNCWebViewClient.setBasicAuthCredential(credential);
90
95
  }
@@ -246,12 +251,39 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
246
251
  return this.messagingEnabled;
247
252
  }
248
253
 
249
- protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
250
- if (bridge == null) {
251
- bridge = new RNCWebViewBridge(webView);
252
- addJavascriptInterface(bridge, JAVASCRIPT_INTERFACE);
254
+ protected void createRNCWebViewBridge(RNCWebView webView) {
255
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)){
256
+ if (this.bridgeListener == null) {
257
+ this.bridgeListener = new WebViewCompat.WebMessageListener() {
258
+ @Override
259
+ public void onPostMessage(@NonNull WebView view, @NonNull WebMessageCompat message, @NonNull Uri sourceOrigin, boolean isMainFrame, @NonNull JavaScriptReplyProxy replyProxy) {
260
+ RNCWebView.this.onMessage(message.getData(), sourceOrigin.toString());
261
+ }
262
+ };
263
+ WebViewCompat.addWebMessageListener(
264
+ webView,
265
+ JAVASCRIPT_INTERFACE,
266
+ Set.of("*"),
267
+ this.bridgeListener
268
+ );
269
+ }
270
+ } else {
271
+ if (fallbackBridge == null) {
272
+ fallbackBridge = new RNCWebViewBridge(webView);
273
+ addJavascriptInterface(fallbackBridge, JAVASCRIPT_INTERFACE);
274
+ }
253
275
  }
254
- return bridge;
276
+ injectJavascriptObject();
277
+ }
278
+
279
+ private void injectJavascriptObject() {
280
+ if (getSettings().getJavaScriptEnabled()) {
281
+ String js = "(function(){\n" +
282
+ " window." + JAVASCRIPT_INTERFACE + " = window." + JAVASCRIPT_INTERFACE + " || {};\n" +
283
+ " window." + JAVASCRIPT_INTERFACE + ".injectedObjectJson = function () { return " + (injectedJavaScriptObject == null ? null : ("`" + injectedJavaScriptObject + "`")) + "; };\n" +
284
+ "})();";
285
+ evaluateJavascriptWithFallback(js);
286
+ }
255
287
  }
256
288
 
257
289
  @SuppressLint("AddJavascriptInterface")
@@ -276,6 +308,7 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
276
308
  injectedJS != null &&
277
309
  !TextUtils.isEmpty(injectedJS)) {
278
310
  evaluateJavascriptWithFallback("(function() {\n" + injectedJS + ";\n})();");
311
+ injectJavascriptObject(); // re-inject the Javascript object in case it has been overwritten.
279
312
  }
280
313
  }
281
314
 
@@ -284,17 +317,18 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
284
317
  injectedJSBeforeContentLoaded != null &&
285
318
  !TextUtils.isEmpty(injectedJSBeforeContentLoaded)) {
286
319
  evaluateJavascriptWithFallback("(function() {\n" + injectedJSBeforeContentLoaded + ";\n})();");
320
+ injectJavascriptObject(); // re-inject the Javascript object in case it has been overwritten.
287
321
  }
288
322
  }
289
323
 
324
+ protected String injectedJavaScriptObject = null;
325
+
290
326
  public void setInjectedJavaScriptObject(String obj) {
291
- if (getSettings().getJavaScriptEnabled()) {
292
- RNCWebViewBridge b = createRNCWebViewBridge(this);
293
- b.setInjectedObjectJson(obj);
294
- }
327
+ this.injectedJavaScriptObject = obj;
328
+ injectJavascriptObject();
295
329
  }
296
330
 
297
- public void onMessage(String message) {
331
+ public void onMessage(String message, String sourceUrl) {
298
332
  ThemedReactContext reactContext = getThemedReactContext();
299
333
  RNCWebView mWebView = this;
300
334
 
@@ -306,7 +340,7 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
306
340
  if (mRNCWebViewClient == null) {
307
341
  return;
308
342
  }
309
- WritableMap data = mRNCWebViewClient.createWebViewEvent(webView, webView.getUrl());
343
+ WritableMap data = mRNCWebViewClient.createWebViewEvent(webView, sourceUrl);
310
344
  data.putString("data", message);
311
345
 
312
346
  if (mMessagingJSModule != null) {
@@ -408,16 +442,11 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
408
442
  protected class RNCWebViewBridge {
409
443
  private String TAG = "RNCWebViewBridge";
410
444
  RNCWebView mWebView;
411
- String injectedObjectJson;
412
445
 
413
446
  RNCWebViewBridge(RNCWebView c) {
414
447
  mWebView = c;
415
448
  }
416
449
 
417
- public void setInjectedObjectJson(String s) {
418
- injectedObjectJson = s;
419
- }
420
-
421
450
  /**
422
451
  * This method is called whenever JavaScript running within the web view calls:
423
452
  * - window[JAVASCRIPT_INTERFACE].postMessage
@@ -425,14 +454,12 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
425
454
  @JavascriptInterface
426
455
  public void postMessage(String message) {
427
456
  if (mWebView.getMessagingEnabled()) {
428
- mWebView.onMessage(message);
457
+ // Post to main thread because `mWebView.getUrl()` requires to be executed on main.
458
+ mWebView.post(() -> mWebView.onMessage(message, mWebView.getUrl()));
429
459
  } else {
430
460
  FLog.w(TAG, "ReactNativeWebView.postMessage method was called but messaging is disabled. Pass an onMessage handler to the WebView.");
431
461
  }
432
462
  }
433
-
434
- @JavascriptInterface
435
- public String injectedObjectJson() { return injectedObjectJson; }
436
463
  }
437
464
 
438
465
 
@@ -25,6 +25,7 @@ import com.facebook.react.bridge.ReadableArray;
25
25
  import com.facebook.react.bridge.WritableMap;
26
26
  import com.facebook.react.uimanager.ThemedReactContext;
27
27
  import com.facebook.react.uimanager.UIManagerHelper;
28
+ import com.reactnativecommunity.webview.events.SubResourceErrorEvent;
28
29
  import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
29
30
  import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
30
31
  import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
@@ -42,13 +43,8 @@ public class RNCWebViewClient extends WebViewClient {
42
43
 
43
44
  protected boolean mLastLoadFailed = false;
44
45
  protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
45
- protected @Nullable String ignoreErrFailedForThisURL = null;
46
46
  protected @Nullable RNCBasicAuthCredential basicAuthCredential = null;
47
47
 
48
- public void setIgnoreErrFailedForThisURL(@Nullable String url) {
49
- ignoreErrFailedForThisURL = url;
50
- }
51
-
52
48
  public void setBasicAuthCredential(@Nullable RNCBasicAuthCredential credential) {
53
49
  basicAuthCredential = credential;
54
50
  }
@@ -173,12 +169,6 @@ public class RNCWebViewClient extends WebViewClient {
173
169
  // Undesired behavior: Return value of WebView.getUrl() may be the current URL instead of the failing URL.
174
170
  handler.cancel();
175
171
 
176
- if (!topWindowUrl.equalsIgnoreCase(failingUrl)) {
177
- // If error is not due to top-level navigation, then do not call onReceivedError()
178
- Log.w(TAG, "Resource blocked from loading due to SSL error. Blocked URL: "+failingUrl);
179
- return;
180
- }
181
-
182
172
  int code = error.getPrimaryError();
183
173
  String description = "";
184
174
  String descriptionPrefix = "SSL error: ";
@@ -210,6 +200,18 @@ public class RNCWebViewClient extends WebViewClient {
210
200
 
211
201
  description = descriptionPrefix + description;
212
202
 
203
+ if (!topWindowUrl.equalsIgnoreCase(failingUrl)) {
204
+ // If error is not due to top-level navigation, then do not call onReceivedError()
205
+ Log.w(TAG, "Resource blocked from loading due to SSL error. Blocked URL: "+failingUrl);
206
+ this.onReceivedSubResourceSslError(
207
+ webView,
208
+ code,
209
+ description,
210
+ failingUrl
211
+ );
212
+ return;
213
+ }
214
+
213
215
  this.onReceivedError(
214
216
  webView,
215
217
  code,
@@ -218,6 +220,20 @@ public class RNCWebViewClient extends WebViewClient {
218
220
  );
219
221
  }
220
222
 
223
+ public void onReceivedSubResourceSslError(
224
+ WebView webView,
225
+ int errorCode,
226
+ String description,
227
+ String failingUrl) {
228
+
229
+ WritableMap eventData = createWebViewEvent(webView, failingUrl);
230
+ eventData.putDouble("code", errorCode);
231
+ eventData.putString("description", description);
232
+
233
+ int reactTag = RNCWebViewWrapper.getReactTagFromWebView(webView);
234
+ UIManagerHelper.getEventDispatcherForReactTag((ReactContext) webView.getContext(), reactTag).dispatchEvent(new SubResourceErrorEvent(reactTag, eventData));
235
+ }
236
+
221
237
  @Override
222
238
  public void onReceivedError(
223
239
  WebView webView,
@@ -225,20 +241,6 @@ public class RNCWebViewClient extends WebViewClient {
225
241
  String description,
226
242
  String failingUrl) {
227
243
 
228
- if (ignoreErrFailedForThisURL != null
229
- && failingUrl.equals(ignoreErrFailedForThisURL)
230
- && errorCode == -1
231
- && description.equals("net::ERR_FAILED")) {
232
-
233
- // This is a workaround for a bug in the WebView.
234
- // See these chromium issues for more context:
235
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1023678
236
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1050635
237
- // This entire commit should be reverted once this bug is resolved in chromium.
238
- setIgnoreErrFailedForThisURL(null);
239
- return;
240
- }
241
-
242
244
  super.onReceivedError(webView, errorCode, description, failingUrl);
243
245
  mLastLoadFailed = true;
244
246
 
@@ -92,7 +92,6 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
92
92
  WebView.setWebContentsDebuggingEnabled(true)
93
93
  }
94
94
  webView.setDownloadListener(DownloadListener { url, userAgent, contentDisposition, mimetype, contentLength ->
95
- webView.setIgnoreErrFailedForThisURL(url)
96
95
  val module = webView.reactApplicationContext.getNativeModule(RNCWebViewModule::class.java) ?: return@DownloadListener
97
96
  val request: DownloadManager.Request = try {
98
97
  DownloadManager.Request(Uri.parse(url))
@@ -341,11 +340,9 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
341
340
  }
342
341
  "injectJavaScript" -> webView.evaluateJavascriptWithFallback(args.getString(0))
343
342
  "loadUrl" -> {
344
- if (args == null) {
345
- throw RuntimeException("Arguments for loading an url are null!")
346
- }
343
+ val url = args?.getString(0) ?: throw RuntimeException("Arguments for loading an url are null!")
347
344
  webView.progressChangedFilter.setWaitingForCommandLoadUrl(false)
348
- args.getString(0)?.let { webView.loadUrl(it) }
345
+ webView.loadUrl(url)
349
346
  }
350
347
  "requestFocus" -> webView.requestFocus()
351
348
  "clearFormData" -> webView.clearFormData()
@@ -541,8 +538,7 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
541
538
  }
542
539
 
543
540
  fun setContentBlockerEnabled(viewWrapper: RNCWebViewWrapper, value: Boolean) {
544
- val view = viewWrapper.webView
545
- view.settings.contentBlockerEnabled = value
541
+ //
546
542
  }
547
543
 
548
544
  fun setHasOnScroll(viewWrapper: RNCWebViewWrapper, value: Boolean) {
@@ -722,4 +718,11 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
722
718
  fun setWebviewDebuggingEnabled(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
723
719
  RNCWebView.setWebContentsDebuggingEnabled(enabled)
724
720
  }
725
- }
721
+
722
+ fun setPaymentRequestEnabled(viewWrapper: RNCWebViewWrapper, enabled: Boolean) {
723
+ val view = viewWrapper.webView
724
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.PAYMENT_REQUEST)) {
725
+ WebSettingsCompat.setPaymentRequestEnabled(view.settings, enabled)
726
+ }
727
+ }
728
+ }
@@ -0,0 +1,25 @@
1
+ package com.reactnativecommunity.webview.events
2
+
3
+ import com.facebook.react.bridge.WritableMap
4
+ import com.facebook.react.uimanager.events.Event
5
+ import com.facebook.react.uimanager.events.RCTEventEmitter
6
+
7
+ /**
8
+ * Event emitted when there is an error in loading a subresource
9
+ */
10
+ class SubResourceErrorEvent(viewId: Int, private val mEventData: WritableMap) :
11
+ Event<SubResourceErrorEvent>(viewId) {
12
+ companion object {
13
+ const val EVENT_NAME = "topLoadingSubResourceError"
14
+ }
15
+
16
+ override fun getEventName(): String = EVENT_NAME
17
+
18
+ override fun canCoalesce(): Boolean = false
19
+
20
+ override fun getCoalescingKey(): Short = 0
21
+
22
+ override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
+ rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
24
+
25
+ }
@@ -15,6 +15,7 @@ import com.facebook.react.viewmanagers.RNCWebViewManagerDelegate;
15
15
  import com.facebook.react.viewmanagers.RNCWebViewManagerInterface;
16
16
  import com.facebook.react.views.scroll.ScrollEventType;
17
17
  import com.reactnativecommunity.webview.events.TopCustomMenuSelectionEvent;
18
+ import com.reactnativecommunity.webview.events.SubResourceErrorEvent;
18
19
  import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
19
20
  import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
20
21
  import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
@@ -232,7 +233,7 @@ public class RNCWebViewManager extends ViewGroupManager<RNCWebViewWrapper>
232
233
  }
233
234
 
234
235
  @Override
235
- @ReactProp(name = "suppressMenuItems ")
236
+ @ReactProp(name = "suppressMenuItems")
236
237
  public void setSuppressMenuItems(RNCWebViewWrapper view, @Nullable ReadableArray items) {}
237
238
 
238
239
  @Override
@@ -337,6 +338,12 @@ public class RNCWebViewManager extends ViewGroupManager<RNCWebViewWrapper>
337
338
  mRNCWebViewManagerImpl.setWebviewDebuggingEnabled(view, value);
338
339
  }
339
340
 
341
+ @Override
342
+ @ReactProp(name = "paymentRequestEnabled")
343
+ public void setPaymentRequestEnabled(RNCWebViewWrapper view, boolean value) {
344
+ mRNCWebViewManagerImpl.setPaymentRequestEnabled(view, value);
345
+ }
346
+
340
347
  /* iOS PROPS - no implemented here */
341
348
  @Override
342
349
  public void setAllowingReadAccessToURL(RNCWebViewWrapper view, @Nullable String value) {}
@@ -401,6 +408,9 @@ public class RNCWebViewManager extends ViewGroupManager<RNCWebViewWrapper>
401
408
  @Override
402
409
  public void setRefreshControlLightMode(RNCWebViewWrapper view, boolean value) {}
403
410
 
411
+ @Override
412
+ public void setIndicatorStyle(RNCWebViewWrapper view, @Nullable String value) {}
413
+
404
414
  @Override
405
415
  public void setScrollEnabled(RNCWebViewWrapper view, boolean value) {}
406
416
 
@@ -521,6 +531,7 @@ public class RNCWebViewManager extends ViewGroupManager<RNCWebViewWrapper>
521
531
  export.put(TopLoadingStartEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingStart"));
522
532
  export.put(TopLoadingFinishEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingFinish"));
523
533
  export.put(TopLoadingErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingError"));
534
+ export.put(SubResourceErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingSubResourceError"));
524
535
  export.put(TopMessageEvent.EVENT_NAME, MapBuilder.of("registrationName", "onMessage"));
525
536
  // !Default events but adding them here explicitly for clarity
526
537
 
@@ -11,6 +11,7 @@ import com.facebook.react.uimanager.ViewGroupManager;
11
11
  import com.facebook.react.uimanager.annotations.ReactProp;
12
12
  import com.facebook.react.views.scroll.ScrollEventType;
13
13
  import com.reactnativecommunity.webview.events.TopCustomMenuSelectionEvent;
14
+ import com.reactnativecommunity.webview.events.SubResourceErrorEvent;
14
15
  import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
15
16
  import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
16
17
  import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
@@ -278,6 +279,11 @@ public class RNCWebViewManager extends ViewGroupManager<RNCWebViewWrapper> {
278
279
  mRNCWebViewManagerImpl.setUserAgent(view, value);
279
280
  }
280
281
 
282
+ @ReactProp(name = "paymentRequestEnabled")
283
+ public void setPaymentRequestEnabled(RNCWebViewWrapper view, boolean value) {
284
+ mRNCWebViewManagerImpl.setPaymentRequestEnabled(view, value);
285
+ }
286
+
281
287
  @Override
282
288
  protected void addEventEmitters(@NonNull ThemedReactContext reactContext, RNCWebViewWrapper viewWrapper) {
283
289
  // Do not register default touch emitter and let WebView implementation handle touches
@@ -294,6 +300,7 @@ public class RNCWebViewManager extends ViewGroupManager<RNCWebViewWrapper> {
294
300
  export.put(TopLoadingStartEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingStart"));
295
301
  export.put(TopLoadingFinishEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingFinish"));
296
302
  export.put(TopLoadingErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingError"));
303
+ export.put(SubResourceErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingSubResourceError"));
297
304
  export.put(TopMessageEvent.EVENT_NAME, MapBuilder.of("registrationName", "onMessage"));
298
305
  // !Default events but adding them here explicitly for clarity
299
306
 
@@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
22
22
 
23
23
  namespace facebook {
24
24
  namespace react {
25
- bool operator==(const RNCWebViewMenuItemsStruct& a, const RNCWebViewMenuItemsStruct& b)
25
+ inline bool operator==(const RNCWebViewMenuItemsStruct& a, const RNCWebViewMenuItemsStruct& b)
26
26
  {
27
27
  return b.key == a.key && b.label == a.label;
28
28
  }
@@ -71,9 +71,9 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
71
71
  if (self = [super initWithFrame:frame]) {
72
72
  static const auto defaultProps = std::make_shared<const RNCWebViewProps>();
73
73
  _props = defaultProps;
74
-
74
+
75
75
  _view = [[RNCWebViewImpl alloc] init];
76
-
76
+
77
77
  _view.onShouldStartLoadWithRequest = [self](NSDictionary* dictionary) {
78
78
  if (_eventEmitter) {
79
79
  auto webViewEventEmitter = std::static_pointer_cast<RNCWebViewEventEmitter const>(_eventEmitter);
@@ -83,7 +83,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
83
83
  .title = std::string([[dictionary valueForKey:@"title"] UTF8String]),
84
84
  .navigationType = stringToOnShouldStartLoadWithRequestNavigationTypeEnum(std::string([[dictionary valueForKey:@"navigationType"] UTF8String])),
85
85
  .canGoBack = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
86
- .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
86
+ .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoForward"] boolValue]),
87
87
  .isTopFrame = static_cast<bool>([[dictionary valueForKey:@"isTopFrame"] boolValue]),
88
88
  .loading = static_cast<bool>([[dictionary valueForKey:@"loading"] boolValue]),
89
89
  .mainDocumentURL = std::string([[dictionary valueForKey:@"mainDocumentURL"] UTF8String])
@@ -100,7 +100,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
100
100
  .title = std::string([[dictionary valueForKey:@"title"] UTF8String]),
101
101
  .navigationType = stringToOnLoadingStartNavigationTypeEnum(std::string([[dictionary valueForKey:@"navigationType"] UTF8String])),
102
102
  .canGoBack = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
103
- .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
103
+ .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoForward"] boolValue]),
104
104
  .loading = static_cast<bool>([[dictionary valueForKey:@"loading"] boolValue]),
105
105
  .mainDocumentURL = std::string([[dictionary valueForKey:@"mainDocumentURL"] UTF8String], [[dictionary valueForKey:@"mainDocumentURL"] lengthOfBytesUsingEncoding:NSUTF8StringEncoding])
106
106
  };
@@ -117,7 +117,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
117
117
  .code = [[dictionary valueForKey:@"code"] intValue],
118
118
  .description = std::string([[dictionary valueForKey:@"description"] UTF8String] ?: ""),
119
119
  .canGoBack = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
120
- .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
120
+ .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoForward"] boolValue]),
121
121
  .loading = static_cast<bool>([[dictionary valueForKey:@"loading"] boolValue]),
122
122
  .domain = std::string([[dictionary valueForKey:@"domain"] UTF8String])
123
123
  };
@@ -132,7 +132,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
132
132
  .lockIdentifier = [[dictionary valueForKey:@"lockIdentifier"] doubleValue],
133
133
  .title = std::string([[dictionary valueForKey:@"title"] UTF8String]),
134
134
  .canGoBack = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
135
- .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
135
+ .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoForward"] boolValue]),
136
136
  .loading = static_cast<bool>([[dictionary valueForKey:@"loading"] boolValue]),
137
137
  .data = std::string([[dictionary valueForKey:@"data"] UTF8String])
138
138
  };
@@ -148,7 +148,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
148
148
  .title = std::string([[dictionary valueForKey:@"title"] UTF8String]),
149
149
  .navigationType = stringToOnLoadingFinishNavigationTypeEnum(std::string([[dictionary valueForKey:@"navigationType"] UTF8String], [[dictionary valueForKey:@"navigationType"] lengthOfBytesUsingEncoding:NSUTF8StringEncoding])),
150
150
  .canGoBack = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
151
- .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
151
+ .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoForward"] boolValue]),
152
152
  .loading = static_cast<bool>([[dictionary valueForKey:@"loading"] boolValue]),
153
153
  .mainDocumentURL = std::string([[dictionary valueForKey:@"mainDocumentURL"] UTF8String], [[dictionary valueForKey:@"mainDocumentURL"] lengthOfBytesUsingEncoding:NSUTF8StringEncoding])
154
154
  };
@@ -163,7 +163,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
163
163
  .lockIdentifier = [[dictionary valueForKey:@"lockIdentifier"] doubleValue],
164
164
  .title = std::string([[dictionary valueForKey:@"title"] UTF8String]),
165
165
  .canGoBack = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
166
- .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
166
+ .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoForward"] boolValue]),
167
167
  .loading = static_cast<bool>([[dictionary valueForKey:@"loading"] boolValue]),
168
168
  .progress = [[dictionary valueForKey:@"progress"] doubleValue]
169
169
  };
@@ -178,7 +178,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
178
178
  .lockIdentifier = [[dictionary valueForKey:@"lockIdentifier"] doubleValue],
179
179
  .title = std::string([[dictionary valueForKey:@"title"] UTF8String]),
180
180
  .canGoBack = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
181
- .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
181
+ .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoForward"] boolValue]),
182
182
  .loading = static_cast<bool>([[dictionary valueForKey:@"loading"] boolValue])
183
183
  };
184
184
  webViewEventEmitter->onContentProcessDidTerminate(data);
@@ -191,7 +191,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
191
191
  .selectedText = std::string([[dictionary valueForKey:@"selectedText"] UTF8String]),
192
192
  .key = std::string([[dictionary valueForKey:@"key"] UTF8String]),
193
193
  .label = std::string([[dictionary valueForKey:@"label"] UTF8String])
194
-
194
+
195
195
  };
196
196
  webViewEventEmitter->onCustomMenuSelection(data);
197
197
  }
@@ -238,7 +238,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
238
238
  .statusCode = [[dictionary valueForKey:@"statusCode"] intValue],
239
239
  .description = std::string([[dictionary valueForKey:@"description"] UTF8String] ?: ""),
240
240
  .canGoBack = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
241
- .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoBack"] boolValue]),
241
+ .canGoForward = static_cast<bool>([[dictionary valueForKey:@"canGoForward"] boolValue]),
242
242
  .loading = static_cast<bool>([[dictionary valueForKey:@"loading"] boolValue])
243
243
  };
244
244
  webViewEventEmitter->onHttpError(data);
@@ -313,7 +313,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
313
313
  REMAP_WEBVIEW_PROP(showsHorizontalScrollIndicator)
314
314
  REMAP_WEBVIEW_PROP(showsVerticalScrollIndicator)
315
315
  REMAP_WEBVIEW_PROP(keyboardDisplayRequiresUserAction)
316
-
316
+
317
317
  #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */
318
318
  REMAP_WEBVIEW_PROP(automaticallyAdjustContentInsets)
319
319
  #endif
@@ -399,7 +399,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
399
399
  for (const auto &menuItem: newViewProps.suppressMenuItems) {
400
400
  [suppressMenuItems addObject: RCTNSStringFromString(menuItem)];
401
401
  }
402
-
402
+
403
403
  [_view setSuppressMenuItems:suppressMenuItems];
404
404
  }
405
405
  if (oldViewProps.hasOnFileDownload != newViewProps.hasOnFileDownload) {
@@ -411,10 +411,10 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
411
411
  .downloadUrl = std::string([[dictionary valueForKey:@"downloadUrl"] UTF8String])
412
412
  };
413
413
  webViewEventEmitter->onFileDownload(data);
414
- }
414
+ }
415
415
  };
416
416
  } else {
417
- _view.onFileDownload = nil;
417
+ _view.onFileDownload = nil;
418
418
  }
419
419
  }
420
420
  if (oldViewProps.hasOnOpenWindowEvent != newViewProps.hasOnOpenWindowEvent) {
@@ -460,7 +460,16 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
460
460
  }
461
461
  }
462
462
  #endif
463
-
463
+ if (oldViewProps.indicatorStyle != newViewProps.indicatorStyle) {
464
+ if (newViewProps.indicatorStyle == RNCWebViewIndicatorStyle::Black) {
465
+ [_view setIndicatorStyle:@"black"];
466
+ } else if (newViewProps.indicatorStyle == RNCWebViewIndicatorStyle::White) {
467
+ [_view setIndicatorStyle:@"white"];
468
+ } else {
469
+ [_view setIndicatorStyle:@"default"];
470
+ }
471
+ }
472
+
464
473
  NSMutableDictionary* source = [[NSMutableDictionary alloc] init];
465
474
  if (!newViewProps.newSource.uri.empty()) {
466
475
  [source setValue:RCTNSStringFromString(newViewProps.newSource.uri) forKey:@"uri"];
@@ -485,7 +494,7 @@ auto stringToOnLoadingFinishNavigationTypeEnum(std::string value) {
485
494
  [source setValue:RCTNSStringFromString(newViewProps.newSource.method) forKey:@"method"];
486
495
  }
487
496
  [_view setSource:source];
488
-
497
+
489
498
  [super updateProps:props oldProps:oldProps];
490
499
  }
491
500
 
@@ -45,7 +45,7 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
45
45
  @end
46
46
 
47
47
  #if !TARGET_OS_OSX
48
- @interface RNCWebViewImpl : RCTView <UIEditMenuInteractionDelegate>
48
+ @interface RNCWebViewImpl : RCTView <UIEditMenuInteractionDelegate, UIGestureRecognizerDelegate>
49
49
 
50
50
  @property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0));
51
51
  #else
@@ -103,6 +103,7 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
103
103
  @property (nonatomic, assign) BOOL allowsLinkPreview;
104
104
  @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator;
105
105
  @property (nonatomic, assign) BOOL showsVerticalScrollIndicator;
106
+ @property (nonatomic, copy) NSString * _Nullable indicatorStyle;
106
107
  @property (nonatomic, assign) BOOL directionalLockEnabled;
107
108
  @property (nonatomic, assign) BOOL ignoreSilentHardwareSwitch;
108
109
  @property (nonatomic, copy) NSString * _Nullable allowingReadAccessToURL;