Ver código fonte

Merge branch 'master' into master

Erik Haider Forsén 4 anos atrás
pai
commit
4b56a6d95e
Nenhuma conta vinculada ao e-mail do autor do commit

+ 5
- 11
.github/workflows/windows-ci.yml Ver arquivo

@@ -15,17 +15,10 @@ jobs:
15 15
       with:
16 16
         node-version: '12.9.1'
17 17
 
18
-    - name: Install Visual Studio components
19
-      shell: powershell
20
-      run: .\.github\workflows\scripts\install-vs-features.ps1 Microsoft.VisualStudio.Component.VC.v141.x86.x64,Microsoft.VisualStudio.ComponentGroup.UWP.VC.v141
21
-
22 18
     - name: Setup MSBuild
23 19
       uses: microsoft/setup-msbuild@v1.0.0
24 20
       with:
25 21
         vs-version: 16.5
26
-       
27
-    - name: Setup NuGet
28
-      uses: NuGet/setup-nuget@v1.0.2
29 22
 
30 23
     - name: Check node modules cache
31 24
       uses: actions/cache@v1
@@ -45,13 +38,14 @@ jobs:
45 38
       run: |
46 39
         yarn build
47 40
         yarn tsc
48
- 
49
-    - name: NuGet restore
50
-      run: nuget restore example\windows\WebViewWindows.sln
51 41
 
52 42
     - name: Build x64 release
53
-      run: msbuild example\windows\WebViewWindows.sln /p:Configuration=Release /p:Platform=x64 -m
43
+      shell: powershell
44
+      run: npx react-native run-windows --root example --arch x64 --release --no-packager --no-deploy --logging
54 45
 
46
+    # Workaround for a bug in package searching during deploy.
47
+    # The deploy script only searches windows/{*/bin/x64/Release,Release/*}, but the build step above placed the pakcages at windows/x64/Release.
48
+    # Copy the packages to Windows/Release before deploying.
55 49
     - name: Deploy
56 50
       shell: powershell
57 51
       run: |

+ 85
- 22
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java Ver arquivo

@@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
4 4
 import android.annotation.TargetApi;
5 5
 import android.app.DownloadManager;
6 6
 import android.content.Context;
7
-import android.content.Intent;
8 7
 import android.content.pm.ActivityInfo;
9 8
 import android.content.pm.PackageManager;
10 9
 import android.graphics.Bitmap;
@@ -14,8 +13,7 @@ import android.net.http.SslError;
14 13
 import android.net.Uri;
15 14
 import android.os.Build;
16 15
 import android.os.Environment;
17
-import androidx.annotation.RequiresApi;
18
-import androidx.core.content.ContextCompat;
16
+import android.os.SystemClock;
19 17
 import android.text.TextUtils;
20 18
 import android.util.Log;
21 19
 import android.view.Gravity;
@@ -41,6 +39,12 @@ import android.webkit.WebView;
41 39
 import android.webkit.WebViewClient;
42 40
 import android.widget.FrameLayout;
43 41
 
42
+import androidx.annotation.Nullable;
43
+import androidx.annotation.RequiresApi;
44
+import androidx.core.content.ContextCompat;
45
+import androidx.core.util.Pair;
46
+
47
+import com.facebook.common.logging.FLog;
44 48
 import com.facebook.react.views.scroll.ScrollEvent;
45 49
 import com.facebook.react.views.scroll.ScrollEventType;
46 50
 import com.facebook.react.views.scroll.OnScrollDispatchHelper;
@@ -64,6 +68,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
64 68
 import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
65 69
 import com.facebook.react.uimanager.events.Event;
66 70
 import com.facebook.react.uimanager.events.EventDispatcher;
71
+import com.reactnativecommunity.webview.RNCWebViewModule.ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState;
67 72
 import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
68 73
 import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
69 74
 import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
@@ -84,8 +89,7 @@ import java.util.ArrayList;
84 89
 import java.util.HashMap;
85 90
 import java.util.Locale;
86 91
 import java.util.Map;
87
-
88
-import javax.annotation.Nullable;
92
+import java.util.concurrent.atomic.AtomicReference;
89 93
 
90 94
 /**
91 95
  * Manages instances of {@link WebView}
@@ -113,6 +117,7 @@ import javax.annotation.Nullable;
113 117
  */
114 118
 @ReactModule(name = RNCWebViewManager.REACT_CLASS)
115 119
 public class RNCWebViewManager extends SimpleViewManager<WebView> {
120
+  private static final String TAG = "RNCWebViewManager";
116 121
 
117 122
   public static final int COMMAND_GO_BACK = 1;
118 123
   public static final int COMMAND_GO_FORWARD = 2;
@@ -136,6 +141,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
136 141
   // Use `webView.loadUrl("about:blank")` to reliably reset the view
137 142
   // state and release page resources (including any running JavaScript).
138 143
   protected static final String BLANK_URL = "about:blank";
144
+  protected static final int SHOULD_OVERRIDE_URL_LOADING_TIMEOUT = 250;
139 145
   protected WebViewConfig mWebViewConfig;
140 146
 
141 147
   protected RNCWebChromeClient mWebChromeClient = null;
@@ -299,6 +305,21 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
299 305
     }
300 306
   }
301 307
 
308
+  @ReactProp(name = "androidLayerType")
309
+  public void setLayerType(WebView view, String layerTypeString) {
310
+    int layerType = View.LAYER_TYPE_NONE;
311
+    switch (layerTypeString) {
312
+        case "hardware":
313
+          layerType = View.LAYER_TYPE_HARDWARE;
314
+          break;
315
+        case "software":
316
+          layerType = View.LAYER_TYPE_SOFTWARE;
317
+          break;
318
+    }
319
+    view.setLayerType(layerType, null);
320
+  }
321
+
322
+
302 323
   @ReactProp(name = "overScrollMode")
303 324
   public void setOverScrollMode(WebView view, String overScrollModeString) {
304 325
     Integer overScrollMode;
@@ -432,6 +453,11 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
432 453
 
433 454
   @ReactProp(name = "incognito")
434 455
   public void setIncognito(WebView view, boolean enabled) {
456
+    // Don't do anything when incognito is disabled
457
+    if (!enabled) {
458
+      return;
459
+    }
460
+
435 461
     // Remove all previous cookies
436 462
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
437 463
       CookieManager.getInstance().removeAllCookies(null);
@@ -441,14 +467,14 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
441 467
 
442 468
     // Disable caching
443 469
     view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
444
-    view.getSettings().setAppCacheEnabled(!enabled);
470
+    view.getSettings().setAppCacheEnabled(false);
445 471
     view.clearHistory();
446
-    view.clearCache(enabled);
472
+    view.clearCache(true);
447 473
 
448 474
     // No form data or autofill enabled
449 475
     view.clearFormData();
450
-    view.getSettings().setSavePassword(!enabled);
451
-    view.getSettings().setSaveFormData(!enabled);
476
+    view.getSettings().setSavePassword(false);
477
+    view.getSettings().setSaveFormData(false);
452 478
   }
453 479
 
454 480
   @ReactProp(name = "source")
@@ -786,15 +812,52 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
786 812
 
787 813
     @Override
788 814
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
789
-      progressChangedFilter.setWaitingForCommandLoadUrl(true);
790
-      dispatchEvent(
791
-        view,
792
-        new TopShouldStartLoadWithRequestEvent(
793
-          view.getId(),
794
-          createWebViewEvent(view, url)));
795
-      return true;
796
-    }
815
+      final RNCWebView rncWebView = (RNCWebView) view;
816
+      final boolean isJsDebugging = ((ReactContext) view.getContext()).getJavaScriptContextHolder().get() == 0;
797 817
 
818
+      if (!isJsDebugging && rncWebView.mCatalystInstance != null) {
819
+        final Pair<Integer, AtomicReference<ShouldOverrideCallbackState>> lock = RNCWebViewModule.shouldOverrideUrlLoadingLock.getNewLock();
820
+        final int lockIdentifier = lock.first;
821
+        final AtomicReference<ShouldOverrideCallbackState> lockObject = lock.second;
822
+
823
+        final WritableMap event = createWebViewEvent(view, url);
824
+        event.putInt("lockIdentifier", lockIdentifier);
825
+        rncWebView.sendDirectMessage("onShouldStartLoadWithRequest", event);
826
+
827
+        try {
828
+          assert lockObject != null;
829
+          synchronized (lockObject) {
830
+            final long startTime = SystemClock.elapsedRealtime();
831
+            while (lockObject.get() == ShouldOverrideCallbackState.UNDECIDED) {
832
+              if (SystemClock.elapsedRealtime() - startTime > SHOULD_OVERRIDE_URL_LOADING_TIMEOUT) {
833
+                FLog.w(TAG, "Did not receive response to shouldOverrideUrlLoading in time, defaulting to allow loading.");
834
+                RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
835
+                return false;
836
+              }
837
+              lockObject.wait(SHOULD_OVERRIDE_URL_LOADING_TIMEOUT);
838
+            }
839
+          }
840
+        } catch (InterruptedException e) {
841
+          FLog.e(TAG, "shouldOverrideUrlLoading was interrupted while waiting for result.", e);
842
+          RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
843
+          return false;
844
+        }
845
+
846
+        final boolean shouldOverride = lockObject.get() == ShouldOverrideCallbackState.SHOULD_OVERRIDE;
847
+        RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
848
+
849
+        return shouldOverride;
850
+      } else {
851
+        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.");
852
+        progressChangedFilter.setWaitingForCommandLoadUrl(true);
853
+        dispatchEvent(
854
+          view,
855
+          new TopShouldStartLoadWithRequestEvent(
856
+            view.getId(),
857
+            createWebViewEvent(view, url)));
858
+        return true;
859
+      }
860
+    }
798 861
 
799 862
     @TargetApi(Build.VERSION_CODES.N)
800 863
     @Override
@@ -1144,6 +1207,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1144 1207
      */
1145 1208
     public RNCWebView(ThemedReactContext reactContext) {
1146 1209
       super(reactContext);
1210
+      this.createCatalystInstance();
1147 1211
       progressChangedFilter = new ProgressChangedFilter();
1148 1212
     }
1149 1213
 
@@ -1252,7 +1316,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1252 1316
 
1253 1317
       if (enabled) {
1254 1318
         addJavascriptInterface(createRNCWebViewBridge(this), JAVASCRIPT_INTERFACE);
1255
-        this.createCatalystInstance();
1256 1319
       } else {
1257 1320
         removeJavascriptInterface(JAVASCRIPT_INTERFACE);
1258 1321
       }
@@ -1308,7 +1371,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1308 1371
             data.putString("data", message);
1309 1372
 
1310 1373
             if (mCatalystInstance != null) {
1311
-              mContext.sendDirectMessage(data);
1374
+              mContext.sendDirectMessage("onMessage", data);
1312 1375
             } else {
1313 1376
               dispatchEvent(webView, new TopMessageEvent(webView.getId(), data));
1314 1377
             }
@@ -1319,21 +1382,21 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1319 1382
         eventData.putString("data", message);
1320 1383
 
1321 1384
         if (mCatalystInstance != null) {
1322
-          this.sendDirectMessage(eventData);
1385
+          this.sendDirectMessage("onMessage", eventData);
1323 1386
         } else {
1324 1387
           dispatchEvent(this, new TopMessageEvent(this.getId(), eventData));
1325 1388
         }
1326 1389
       }
1327 1390
     }
1328 1391
 
1329
-    protected void sendDirectMessage(WritableMap data) {
1392
+    protected void sendDirectMessage(final String method, WritableMap data) {
1330 1393
       WritableNativeMap event = new WritableNativeMap();
1331 1394
       event.putMap("nativeEvent", data);
1332 1395
 
1333 1396
       WritableNativeArray params = new WritableNativeArray();
1334 1397
       params.pushMap(event);
1335 1398
 
1336
-      mCatalystInstance.callFunction(messagingModuleName, "onMessage", params);
1399
+      mCatalystInstance.callFunction(messagingModuleName, method, params);
1337 1400
     }
1338 1401
 
1339 1402
     protected void onScrollChanged(int x, int y, int oldX, int oldY) {

+ 44
- 0
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModule.java Ver arquivo

@@ -12,9 +12,11 @@ import android.os.Environment;
12 12
 import android.os.Parcelable;
13 13
 import android.provider.MediaStore;
14 14
 
15
+import androidx.annotation.Nullable;
15 16
 import androidx.annotation.RequiresApi;
16 17
 import androidx.core.content.ContextCompat;
17 18
 import androidx.core.content.FileProvider;
19
+import androidx.core.util.Pair;
18 20
 
19 21
 import android.util.Log;
20 22
 import android.webkit.MimeTypeMap;
@@ -35,6 +37,8 @@ import java.io.File;
35 37
 import java.io.IOException;
36 38
 import java.util.ArrayList;
37 39
 import java.util.Arrays;
40
+import java.util.HashMap;
41
+import java.util.concurrent.atomic.AtomicReference;
38 42
 
39 43
 import static android.app.Activity.RESULT_OK;
40 44
 
@@ -50,6 +54,35 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
50 54
   private File outputVideo;
51 55
   private DownloadManager.Request downloadRequest;
52 56
 
57
+  protected static class ShouldOverrideUrlLoadingLock {
58
+    protected enum ShouldOverrideCallbackState {
59
+      UNDECIDED,
60
+      SHOULD_OVERRIDE,
61
+      DO_NOT_OVERRIDE,
62
+    }
63
+
64
+    private int nextLockIdentifier = 0;
65
+    private final HashMap<Integer, AtomicReference<ShouldOverrideCallbackState>> shouldOverrideLocks = new HashMap<>();
66
+
67
+    public synchronized Pair<Integer, AtomicReference<ShouldOverrideCallbackState>> getNewLock() {
68
+      final int lockIdentifier = nextLockIdentifier++;
69
+      final AtomicReference<ShouldOverrideCallbackState> shouldOverride = new AtomicReference<>(ShouldOverrideCallbackState.UNDECIDED);
70
+      shouldOverrideLocks.put(lockIdentifier, shouldOverride);
71
+      return new Pair<>(lockIdentifier, shouldOverride);
72
+    }
73
+
74
+    @Nullable
75
+    public synchronized AtomicReference<ShouldOverrideCallbackState> getLock(Integer lockIdentifier) {
76
+      return shouldOverrideLocks.get(lockIdentifier);
77
+    }
78
+
79
+    public synchronized void removeLock(Integer lockIdentifier) {
80
+      shouldOverrideLocks.remove(lockIdentifier);
81
+    }
82
+  }
83
+
84
+  protected static final ShouldOverrideUrlLoadingLock shouldOverrideUrlLoadingLock = new ShouldOverrideUrlLoadingLock();
85
+
53 86
   private enum MimeType {
54 87
     DEFAULT("*/*"),
55 88
     IMAGE("image"),
@@ -105,6 +138,17 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
105 138
     promise.resolve(result);
106 139
   }
107 140
 
141
+  @ReactMethod(isBlockingSynchronousMethod = true)
142
+  public void onShouldStartLoadWithRequestCallback(final boolean shouldStart, final int lockIdentifier) {
143
+    final AtomicReference<ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState> lockObject = shouldOverrideUrlLoadingLock.getLock(lockIdentifier);
144
+    if (lockObject != null) {
145
+      synchronized (lockObject) {
146
+        lockObject.set(shouldStart ? ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState.DO_NOT_OVERRIDE : ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState.SHOULD_OVERRIDE);
147
+        lockObject.notify();
148
+      }
149
+    }
150
+  }
151
+
108 152
   public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
109 153
 
110 154
     if (filePathCallback == null && filePathCallbackLegacy == null) {

+ 2
- 0
android/src/main/java/com/reactnativecommunity/webview/events/TopShouldStartLoadWithRequestEvent.kt Ver arquivo

@@ -14,6 +14,8 @@ class TopShouldStartLoadWithRequestEvent(viewId: Int, private val mData: Writabl
14 14
 
15 15
   init {
16 16
     mData.putString("navigationType", "other")
17
+    // Android does not raise shouldOverrideUrlLoading for inner frames
18
+    mData.putBoolean("isTopFrame", true)
17 19
   }
18 20
 
19 21
   override fun getEventName(): String = EVENT_NAME

+ 9
- 0
apple/RNCWebView.h Ver arquivo

@@ -35,6 +35,7 @@
35 35
 @property (nonatomic, assign) BOOL injectedJavaScriptBeforeContentLoadedForMainFrameOnly;
36 36
 @property (nonatomic, assign) BOOL scrollEnabled;
37 37
 @property (nonatomic, assign) BOOL sharedCookiesEnabled;
38
+@property (nonatomic, assign) BOOL autoManageStatusBarEnabled;
38 39
 @property (nonatomic, assign) BOOL pagingEnabled;
39 40
 @property (nonatomic, assign) CGFloat decelerationRate;
40 41
 @property (nonatomic, assign) BOOL allowsInlineMediaPlayback;
@@ -62,6 +63,12 @@
62 63
 @property (nonatomic, assign) BOOL directionalLockEnabled;
63 64
 @property (nonatomic, assign) BOOL ignoreSilentHardwareSwitch;
64 65
 @property (nonatomic, copy) NSString * _Nullable allowingReadAccessToURL;
66
+@property (nonatomic, assign) BOOL pullToRefreshEnabled;
67
+@property (nonatomic, weak) UIRefreshControl * refreshControl;
68
+
69
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
70
+@property (nonatomic, assign) WKContentMode contentMode;
71
+#endif
65 72
 
66 73
 + (void)setClientAuthenticationCredential:(nullable NSURLCredential*)credential;
67 74
 + (void)setCustomCertificatesForHost:(nullable NSDictionary *)certificates;
@@ -71,5 +78,7 @@
71 78
 - (void)goBack;
72 79
 - (void)reload;
73 80
 - (void)stopLoading;
81
+- (void)addPullToRefreshControl;
82
+- (void)pullToRefresh:(UIRefreshControl *)refreshControl;
74 83
 
75 84
 @end

+ 58
- 40
apple/RNCWebView.m Ver arquivo

@@ -125,6 +125,7 @@ static NSDictionary* customCertificatesForHost;
125 125
     _showsVerticalScrollIndicator = YES;
126 126
     _directionalLockEnabled = YES;
127 127
     _automaticallyAdjustContentInsets = YES;
128
+    _autoManageStatusBarEnabled = YES;
128 129
     _contentInset = UIEdgeInsetsZero;
129 130
     _savedKeyboardDisplayRequiresUserAction = YES;
130 131
     #if !TARGET_OS_OSX
@@ -226,6 +227,14 @@ static NSDictionary* customCertificatesForHost;
226 227
   }
227 228
   wkWebViewConfig.userContentController = [WKUserContentController new];
228 229
 
230
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
231
+  if (@available(iOS 13.0, *)) {
232
+    WKWebpagePreferences *pagePrefs = [[WKWebpagePreferences alloc]init];
233
+    pagePrefs.preferredContentMode = _contentMode;
234
+    wkWebViewConfig.defaultWebpagePreferences = pagePrefs;
235
+  }
236
+#endif
237
+
229 238
   // Shim the HTML5 history API:
230 239
   [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
231 240
                                                             name:HistoryShimName];
@@ -267,9 +276,13 @@ static NSDictionary* customCertificatesForHost;
267 276
     _webView.UIDelegate = self;
268 277
     _webView.navigationDelegate = self;
269 278
 #if !TARGET_OS_OSX
279
+    if (_pullToRefreshEnabled) {
280
+        [self addPullToRefreshControl];
281
+    }
270 282
     _webView.scrollView.scrollEnabled = _scrollEnabled;
271 283
     _webView.scrollView.pagingEnabled = _pagingEnabled;
272
-    _webView.scrollView.bounces = _bounces;
284
+      //For UIRefreshControl to work correctly, the bounces should always be true
285
+    _webView.scrollView.bounces = _pullToRefreshEnabled || _bounces; 
273 286
     _webView.scrollView.showsHorizontalScrollIndicator = _showsHorizontalScrollIndicator;
274 287
     _webView.scrollView.showsVerticalScrollIndicator = _showsVerticalScrollIndicator;
275 288
     _webView.scrollView.directionalLockEnabled = _directionalLockEnabled;
@@ -300,7 +313,6 @@ static NSDictionary* customCertificatesForHost;
300 313
   _webView.allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures;
301 314
 }
302 315
 
303
-
304 316
 - (void)removeFromSuperview
305 317
 {
306 318
     if (_webView) {
@@ -324,9 +336,13 @@ static NSDictionary* customCertificatesForHost;
324 336
 -(void)showFullScreenVideoStatusBars
325 337
 {
326 338
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
339
+    if (!_autoManageStatusBarEnabled) {
340
+      return;
341
+    }
342
+
327 343
     _isFullScreenVideoOpen = YES;
328 344
     RCTUnsafeExecuteOnMainQueueSync(^{
329
-      [RCTSharedApplication() setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
345
+      [RCTSharedApplication() setStatusBarStyle:self->_savedStatusBarStyle animated:YES];
330 346
     });
331 347
 #pragma clang diagnostic pop
332 348
 }
@@ -334,6 +350,10 @@ static NSDictionary* customCertificatesForHost;
334 350
 -(void)hideFullScreenVideoStatusBars
335 351
 {
336 352
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
353
+    if (!_autoManageStatusBarEnabled) {
354
+      return;
355
+    }
356
+
337 357
     _isFullScreenVideoOpen = NO;
338 358
     RCTUnsafeExecuteOnMainQueueSync(^{
339 359
       [RCTSharedApplication() setStatusBarHidden:self->_savedStatusBarHidden animated:YES];
@@ -869,40 +889,9 @@ static NSDictionary* customCertificatesForHost;
869 889
  * topViewController
870 890
  */
871 891
 -(UIViewController *)topViewController{
872
-    UIViewController *controller = [self topViewControllerWithRootViewController:[self getCurrentWindow].rootViewController];
873
-    return controller;
892
+    return RCTPresentedViewController();
874 893
 }
875 894
 
876
-/**
877
- * topViewControllerWithRootViewController
878
- */
879
--(UIViewController *)topViewControllerWithRootViewController:(UIViewController *)viewController{
880
-  if (viewController==nil) return nil;
881
-  if (viewController.presentedViewController!=nil) {
882
-    return [self topViewControllerWithRootViewController:viewController.presentedViewController];
883
-  } else if ([viewController isKindOfClass:[UITabBarController class]]){
884
-    return [self topViewControllerWithRootViewController:[(UITabBarController *)viewController selectedViewController]];
885
-  } else if ([viewController isKindOfClass:[UINavigationController class]]){
886
-    return [self topViewControllerWithRootViewController:[(UINavigationController *)viewController visibleViewController]];
887
-  } else {
888
-    return viewController;
889
-  }
890
-}
891
-/**
892
- * getCurrentWindow
893
- */
894
--(UIWindow *)getCurrentWindow{
895
-  UIWindow *window = [UIApplication sharedApplication].keyWindow;
896
-  if (window.windowLevel!=UIWindowLevelNormal) {
897
-    for (UIWindow *wid in [UIApplication sharedApplication].windows) {
898
-      if (window.windowLevel==UIWindowLevelNormal) {
899
-        window = wid;
900
-        break;
901
-      }
902
-    }
903
-  }
904
-  return window;
905
-}
906 895
 #endif // !TARGET_OS_OSX
907 896
 
908 897
 /**
@@ -929,13 +918,15 @@ static NSDictionary* customCertificatesForHost;
929 918
 
930 919
   WKNavigationType navigationType = navigationAction.navigationType;
931 920
   NSURLRequest *request = navigationAction.request;
921
+  BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
932 922
 
933 923
   if (_onShouldStartLoadWithRequest) {
934 924
     NSMutableDictionary<NSString *, id> *event = [self baseEvent];
935 925
     [event addEntriesFromDictionary: @{
936 926
       @"url": (request.URL).absoluteString,
937 927
       @"mainDocumentURL": (request.mainDocumentURL).absoluteString,
938
-      @"navigationType": navigationTypes[@(navigationType)]
928
+      @"navigationType": navigationTypes[@(navigationType)],
929
+      @"isTopFrame": @(isTopFrame)
939 930
     }];
940 931
     if (![self.delegate webView:self
941 932
       shouldStartLoadForRequest:event
@@ -947,7 +938,6 @@ static NSDictionary* customCertificatesForHost;
947 938
 
948 939
   if (_onLoadingStart) {
949 940
     // We have this check to filter out iframe requests and whatnot
950
-    BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
951 941
     if (isTopFrame) {
952 942
       NSMutableDictionary<NSString *, id> *event = [self baseEvent];
953 943
       [event addEntriesFromDictionary: @{
@@ -1147,6 +1137,35 @@ static NSDictionary* customCertificatesForHost;
1147 1137
   }
1148 1138
 }
1149 1139
 
1140
+- (void)addPullToRefreshControl
1141
+{
1142
+    UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
1143
+    _refreshControl = refreshControl;
1144
+    [_webView.scrollView addSubview: refreshControl];
1145
+    [refreshControl addTarget:self action:@selector(pullToRefresh:) forControlEvents: UIControlEventValueChanged];
1146
+}
1147
+
1148
+- (void)pullToRefresh:(UIRefreshControl *)refreshControl
1149
+{
1150
+    [self reload];
1151
+    [refreshControl endRefreshing];
1152
+}
1153
+
1154
+#if !TARGET_OS_OSX
1155
+- (void)setPullToRefreshEnabled:(BOOL)pullToRefreshEnabled
1156
+{
1157
+    _pullToRefreshEnabled = pullToRefreshEnabled;
1158
+    
1159
+    if (pullToRefreshEnabled) {
1160
+        [self addPullToRefreshControl];
1161
+    } else {
1162
+        [_refreshControl removeFromSuperview];
1163
+    }
1164
+
1165
+    [self setBounces:_bounces];
1166
+}
1167
+#endif // !TARGET_OS_OSX
1168
+
1150 1169
 - (void)stopLoading
1151 1170
 {
1152 1171
   [_webView stopLoading];
@@ -1156,11 +1175,11 @@ static NSDictionary* customCertificatesForHost;
1156 1175
 - (void)setBounces:(BOOL)bounces
1157 1176
 {
1158 1177
   _bounces = bounces;
1159
-  _webView.scrollView.bounces = bounces;
1178
+    //For UIRefreshControl to work correctly, the bounces should always be true
1179
+  _webView.scrollView.bounces = _pullToRefreshEnabled || bounces;
1160 1180
 }
1161 1181
 #endif // !TARGET_OS_OSX
1162 1182
 
1163
-
1164 1183
 - (void)setInjectedJavaScript:(NSString *)source {
1165 1184
   _injectedJavaScript = source;
1166 1185
 
@@ -1372,4 +1391,3 @@ static NSDictionary* customCertificatesForHost;
1372 1391
 }
1373 1392
 
1374 1393
 @end
1375
-

+ 19
- 0
apple/RNCWebViewManager.m Ver arquivo

@@ -14,6 +14,16 @@
14 14
 @interface RNCWebViewManager () <RNCWebViewDelegate>
15 15
 @end
16 16
 
17
+@implementation RCTConvert (WKWebView)
18
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
19
+RCT_ENUM_CONVERTER(WKContentMode, (@{
20
+    @"recommended": @(WKContentModeRecommended),
21
+    @"mobile": @(WKContentModeMobile),
22
+    @"desktop": @(WKContentModeDesktop),
23
+}), WKContentModeRecommended, integerValue)
24
+#endif
25
+@end
26
+
17 27
 @implementation RNCWebViewManager
18 28
 {
19 29
   NSConditionLock *_shouldStartLoadLock;
@@ -56,6 +66,7 @@ RCT_EXPORT_VIEW_PROPERTY(dataDetectorTypes, WKDataDetectorTypes)
56 66
 #endif
57 67
 RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
58 68
 RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
69
+RCT_EXPORT_VIEW_PROPERTY(autoManageStatusBarEnabled, BOOL)
59 70
 RCT_EXPORT_VIEW_PROPERTY(hideKeyboardAccessoryView, BOOL)
60 71
 RCT_EXPORT_VIEW_PROPERTY(allowsBackForwardNavigationGestures, BOOL)
61 72
 RCT_EXPORT_VIEW_PROPERTY(incognito, BOOL)
@@ -70,6 +81,10 @@ RCT_EXPORT_VIEW_PROPERTY(allowingReadAccessToURL, NSString)
70 81
 RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
71 82
 #endif
72 83
 
84
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
85
+RCT_EXPORT_VIEW_PROPERTY(contentMode, WKContentMode)
86
+#endif
87
+
73 88
 /**
74 89
  * Expose methods to enable messaging the webview.
75 90
  */
@@ -89,6 +104,10 @@ RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber *)reactTag message:(NSString *)m
89 104
   }];
90 105
 }
91 106
 
107
+RCT_CUSTOM_VIEW_PROPERTY(pullToRefreshEnabled, BOOL, RNCWebView) {
108
+    view.pullToRefreshEnabled = json == nil ? false : [RCTConvert BOOL: json];
109
+}
110
+
92 111
 RCT_CUSTOM_VIEW_PROPERTY(bounces, BOOL, RNCWebView) {
93 112
   view.bounces = json == nil ? true : [RCTConvert BOOL: json];
94 113
 }

+ 3
- 1
docs/Debugging.md Ver arquivo

@@ -15,12 +15,14 @@ It's possible to debug WebView contents in the iOS simulator or on a device usin
15 15
 3. Safari -> Develop -> [device name] -> [app name] -> [url - title]
16 16
 4. You can now debug the WebView contents just as you would on the web
17 17
 
18
-##### Note:
18
+##### Notes:
19 19
 
20 20
 When debugging on device you must enable Web Inspector in your device settings:
21 21
 
22 22
 Settings -> Safari -> Advanced -> Web Inspector
23 23
 
24
+Also, if you don't see your device in the Develop menu, and you started Safari before you started your simulator, try restarting Safari.
25
+
24 26
 ### Android & Chrome
25 27
 
26 28
 It's possible to debug WebView contents in the Android emulator or on a device using Chrome DevTools.

+ 2
- 1
docs/Guide.md Ver arquivo

@@ -305,6 +305,7 @@ export default class App extends Component {
305 305
             uri:
306 306
               'https://github.com/react-native-community/react-native-webview',
307 307
           }}
308
+          onMessage={(event) => {}}
308 309
           injectedJavaScript={runFirst}
309 310
         />
310 311
       </View>
@@ -313,7 +314,7 @@ export default class App extends Component {
313 314
 }
314 315
 ```
315 316
 
316
-This runs the JavaScript in the `runFirst` string once the page is loaded. In this case, you can see that both the body style was changed to red and the alert showed up after 2 seconds.
317
+This runs the JavaScript in the `runFirst` string once the page is loaded. In this case, you can see that both the body style was changed to red and the alert showed up after 2 seconds. An `onMessage` event is required as well to inject the JavaScript code into the WebView.
317 318
 
318 319
 By setting `injectedJavaScriptForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the main frame) if supported for the given platform. For example, if a page contains an iframe, the javascript will be injected into that iframe as well with this set to `false`. (Note this is not supported on Android.) There is also `injectedJavaScriptBeforeContentLoadedForMainFrameOnly` for injecting prior to content loading. Read more about this in the [Reference](./Reference.md#injectedjavascriptformainframeonly).
319 320
 

+ 122
- 55
docs/Reference.md Ver arquivo

@@ -35,6 +35,7 @@ This document lays out the current public properties and methods for the React N
35 35
 - [`javaScriptEnabled`](Reference.md#javascriptenabled)
36 36
 - [`javaScriptCanOpenWindowsAutomatically`](Reference.md#javascriptcanopenwindowsautomatically)
37 37
 - [`androidHardwareAccelerationDisabled`](Reference.md#androidHardwareAccelerationDisabled)
38
+- [`androidLayerType`](Reference.md#androidLayerType)
38 39
 - [`mixedContentMode`](Reference.md#mixedcontentmode)
39 40
 - [`thirdPartyCookiesEnabled`](Reference.md#thirdpartycookiesenabled)
40 41
 - [`userAgent`](Reference.md#useragent)
@@ -45,6 +46,7 @@ This document lays out the current public properties and methods for the React N
45 46
 - [`overScrollMode`](Reference.md#overscrollmode)
46 47
 - [`contentInset`](Reference.md#contentinset)
47 48
 - [`contentInsetAdjustmentBehavior`](Reference.md#contentInsetAdjustmentBehavior)
49
+- [`contentMode`](Reference.md#contentMode)
48 50
 - [`dataDetectorTypes`](Reference.md#datadetectortypes)
49 51
 - [`scrollEnabled`](Reference.md#scrollenabled)
50 52
 - [`directionalLockEnabled`](Reference.md#directionalLockEnabled)
@@ -66,8 +68,10 @@ This document lays out the current public properties and methods for the React N
66 68
 - [`allowsLinkPreview`](Reference.md#allowsLinkPreview)
67 69
 - [`sharedCookiesEnabled`](Reference.md#sharedCookiesEnabled)
68 70
 - [`textZoom`](Reference.md#textZoom)
71
+- [`pullToRefreshEnabled`](Reference.md#pullToRefreshEnabled)
69 72
 - [`ignoreSilentHardwareSwitch`](Reference.md#ignoreSilentHardwareSwitch)
70 73
 - [`onFileDownload`](Reference.md#onFileDownload)
74
+- [`autoManageStatusBarEnabled`](Reference.md#autoManageStatusBarEnabled)
71 75
 
72 76
 ## Methods Index
73 77
 
@@ -133,9 +137,9 @@ Make sure the string evaluates to a valid type (`true` works) and doesn't otherw
133 137
 
134 138
 On iOS, see [`WKUserScriptInjectionTimeAtDocumentEnd`](https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime/wkuserscriptinjectiontimeatdocumentend?language=objc)
135 139
 
136
-| Type   | Required | Platform |
137
-| ------ | -------- | -------- |
138
-| string | No       | iOS, Android, macOS
140
+| Type   | Required | Platform            |
141
+| ------ | -------- | ------------------- |
142
+| string | No       | iOS, Android, macOS |
139 143
 
140 144
 To learn more, read the [Communicating between JS and Native](Guide.md#communicating-between-js-and-native) guide.
141 145
 
@@ -165,15 +169,15 @@ Make sure the string evaluates to a valid type (`true` works) and doesn't otherw
165 169
 
166 170
 On iOS, see [`WKUserScriptInjectionTimeAtDocumentStart`](https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime/wkuserscriptinjectiontimeatdocumentstart?language=objc)
167 171
 
168
-| Type   | Required | Platform |
169
-| ------ | -------- | -------- |
172
+| Type   | Required | Platform   |
173
+| ------ | -------- | ---------- |
170 174
 | string | No       | iOS, macOS |
171 175
 
172 176
 To learn more, read the [Communicating between JS and Native](Guide.md#communicating-between-js-and-native) guide.
173 177
 
174 178
 Example:
175 179
 
176
-Post message a JSON object of `window.location` to be handled by [`onMessage`](Reference.md#onmessage). `window.ReactNativeWebView.postMessage` *will* be available at this time.
180
+Post message a JSON object of `window.location` to be handled by [`onMessage`](Reference.md#onmessage). `window.ReactNativeWebView.postMessage` _will_ be available at this time.
177 181
 
178 182
 ```jsx
179 183
 const INJECTED_JAVASCRIPT = `(function() {
@@ -195,8 +199,8 @@ If `true` (default; mandatory for Android), loads the `injectedJavaScript` only
195 199
 
196 200
 If `false`, (only supported on iOS and macOS), loads it into all frames (e.g. iframes).
197 201
 
198
-| Type   | Required | Platform |
199
-| ------ | -------- | -------- |
202
+| Type | Required | Platform                                          |
203
+| ---- | -------- | ------------------------------------------------- |
200 204
 | bool | No       | iOS and macOS (only `true` supported for Android) |
201 205
 
202 206
 ---
@@ -207,8 +211,8 @@ If `true` (default; mandatory for Android), loads the `injectedJavaScriptBeforeC
207 211
 
208 212
 If `false`, (only supported on iOS and macOS), loads it into all frames (e.g. iframes).
209 213
 
210
-| Type   | Required | Platform |
211
-| ------ | -------- | -------- |
214
+| Type | Required | Platform                                          |
215
+| ---- | -------- | ------------------------------------------------- |
212 216
 | bool | No       | iOS and macOS (only `true` supported for Android) |
213 217
 
214 218
 ---
@@ -219,8 +223,8 @@ Boolean that determines whether HTML5 audio and video requires the user to tap t
219 223
 
220 224
 NOTE: the default `true` value might cause some videos to hang loading on iOS. Setting it to `false` could fix this issue.
221 225
 
222
-| Type | Required | Platform |
223
-| ---- | -------- | -------- |
226
+| Type | Required | Platform            |
227
+| ---- | -------- | ------------------- |
224 228
 | bool | No       | iOS, Android, macOS |
225 229
 
226 230
 ---
@@ -235,8 +239,8 @@ The `nativeConfig` prop expects an object with the following keys:
235 239
 - `props` (object)
236 240
 - `viewManager` (object)
237 241
 
238
-| Type   | Required | Platform |
239
-| ------ | -------- | -------- |
242
+| Type   | Required | Platform            |
243
+| ------ | -------- | ------------------- |
240 244
 | object | No       | iOS, Android, macOS |
241 245
 
242 246
 ---
@@ -254,7 +258,7 @@ Example:
254 258
 ```jsx
255 259
 <WebView
256 260
   source={{ uri: 'https://reactnative.dev' }}
257
-  onError={syntheticEvent => {
261
+  onError={(syntheticEvent) => {
258 262
     const { nativeEvent } = syntheticEvent;
259 263
     console.warn('WebView error: ', nativeEvent);
260 264
   }}
@@ -294,7 +298,7 @@ Example:
294 298
 ```jsx
295 299
 <WebView
296 300
   source={{ uri: 'https://reactnative.dev' }}
297
-  onLoad={syntheticEvent => {
301
+  onLoad={(syntheticEvent) => {
298 302
     const { nativeEvent } = syntheticEvent;
299 303
     this.url = nativeEvent.url;
300 304
   }}
@@ -327,7 +331,7 @@ Example:
327 331
 ```jsx
328 332
 <WebView
329 333
   source={{ uri: 'https://reactnative.dev' }}
330
-  onLoadEnd={syntheticEvent => {
334
+  onLoadEnd={(syntheticEvent) => {
331 335
     // update component to be aware of loading status
332 336
     const { nativeEvent } = syntheticEvent;
333 337
     this.isLoading = nativeEvent.loading;
@@ -361,7 +365,7 @@ Example:
361 365
 ```jsx
362 366
 <WebView
363 367
   source={{ uri: 'https://reactnative.dev/=' }}
364
-  onLoadStart={syntheticEvent => {
368
+  onLoadStart={(syntheticEvent) => {
365 369
     // update component to be aware of loading status
366 370
     const { nativeEvent } = syntheticEvent;
367 371
     this.isLoading = nativeEvent.loading;
@@ -386,8 +390,8 @@ url
386 390
 
387 391
 Function that is invoked when the `WebView` is loading.
388 392
 
389
-| Type     | Required | Platform |
390
-| -------- | -------- | --------- |
393
+| Type     | Required | Platform            |
394
+| -------- | -------- | ------------------- |
391 395
 | function | No       | iOS, Android, macOS |
392 396
 
393 397
 Example:
@@ -431,7 +435,7 @@ Example:
431 435
 ```jsx
432 436
 <WebView
433 437
   source={{ uri: 'https://reactnative.dev' }}
434
-  onHttpError={syntheticEvent => {
438
+  onHttpError={(syntheticEvent) => {
435 439
     const { nativeEvent } = syntheticEvent;
436 440
     console.warn(
437 441
       'WebView received error status code: ',
@@ -519,7 +523,7 @@ Example:
519 523
 ```jsx
520 524
 <WebView
521 525
   source={{ uri: 'https://reactnative.dev' }}
522
-  onNavigationStateChange={navState => {
526
+  onNavigationStateChange={(navState) => {
523 527
     // Keep track of going back navigation within component
524 528
     this.canGoBack = navState.canGoBack;
525 529
   }}
@@ -538,8 +542,6 @@ title
538 542
 url
539 543
 ```
540 544
 
541
-Note that this method will not be invoked on hash URL changes (e.g. from `https://example.com/users#list` to `https://example.com/users#help`). There is a workaround for this that is described [in the Guide](Guide.md#intercepting-hash-url-changes).
542
-
543 545
 ---
544 546
 
545 547
 ### `onContentProcessDidTerminate`[⬆](#props-index)<!-- Link generated with jump2header -->
@@ -555,7 +557,7 @@ Example:
555 557
 ```jsx
556 558
 <WebView
557 559
   source={{ uri: 'https://reactnative.dev' }}
558
-  onContentProcessDidTerminate={syntheticEvent => {
560
+  onContentProcessDidTerminate={(syntheticEvent) => {
559 561
     const { nativeEvent } = syntheticEvent;
560 562
     console.warn('Content process terminated, reloading', nativeEvent);
561 563
     this.refs.webview.reload();
@@ -580,8 +582,8 @@ url
580 582
 
581 583
 List of origin strings to allow being navigated to. The strings allow wildcards and get matched against _just_ the origin (not the full URL). If the user taps to navigate to a new page but the new page is not in this whitelist, the URL will be handled by the OS. The default whitelisted origins are "http://*" and "https://*".
582 584
 
583
-| Type             | Required | Platform |
584
-| ---------------- | -------- | -------- |
585
+| Type             | Required | Platform            |
586
+| ---------------- | -------- | ------------------- |
585 587
 | array of strings | No       | iOS, Android, macOS |
586 588
 
587 589
 Example:
@@ -600,8 +602,8 @@ Example:
600 602
 
601 603
 Function that returns a view to show if there's an error.
602 604
 
603
-| Type     | Required | Platform |
604
-| -------- | -------- | -------- |
605
+| Type     | Required | Platform            |
606
+| -------- | -------- | ------------------- |
605 607
 | function | No       | iOS, Android, macOS |
606 608
 
607 609
 Example:
@@ -609,7 +611,7 @@ Example:
609 611
 ```jsx
610 612
 <WebView
611 613
   source={{ uri: 'https://reactnative.dev' }}
612
-  renderError={errorName => <Error name={errorName} />}
614
+  renderError={(errorName) => <Error name={errorName} />}
613 615
 />
614 616
 ```
615 617
 
@@ -621,8 +623,8 @@ The function passed to `renderError` will be called with the name of the error
621 623
 
622 624
 Function that returns a loading indicator. The startInLoadingState prop must be set to true in order to use this prop.
623 625
 
624
-| Type     | Required | Platform |
625
-| -------- | -------- | -------- |
626
+| Type     | Required | Platform            |
627
+| -------- | -------- | ------------------- |
626 628
 | function | No       | iOS, Android, macOS |
627 629
 
628 630
 Example:
@@ -653,8 +655,8 @@ Function that allows custom handling of any web view requests. Return `true` fro
653 655
 
654 656
 On Android, is not called on the first load.
655 657
 
656
-| Type     | Required | Platform |
657
-| -------- | -------- | -------- |
658
+| Type     | Required | Platform            |
659
+| -------- | -------- | ------------------- |
658 660
 | function | No       | iOS, Android, macOS |
659 661
 
660 662
 Example:
@@ -662,7 +664,7 @@ Example:
662 664
 ```jsx
663 665
 <WebView
664 666
   source={{ uri: 'https://reactnative.dev' }}
665
-  onShouldStartLoadWithRequest={request => {
667
+  onShouldStartLoadWithRequest={(request) => {
666 668
     // Only allow navigating within this website
667 669
     return request.url.startsWith('https://reactnative.dev');
668 670
   }}
@@ -681,6 +683,7 @@ canGoForward
681 683
 lockIdentifier
682 684
 mainDocumentURL (iOS only)
683 685
 navigationType
686
+isTopFrame
684 687
 ```
685 688
 
686 689
 ---
@@ -689,8 +692,8 @@ navigationType
689 692
 
690 693
 Boolean value that forces the `WebView` to show the loading view on the first load. This prop must be set to `true` in order for the `renderLoading` prop to work.
691 694
 
692
-| Type | Required | Platform |
693
-| ---- | -------- | -------- |
695
+| Type | Required | Platform            |
696
+| ---- | -------- | ------------------- |
694 697
 | bool | No       | iOS, Android, macOS |
695 698
 
696 699
 ---
@@ -778,7 +781,7 @@ A Boolean value indicating whether JavaScript can open windows without user inte
778 781
 
779 782
 ### `androidHardwareAccelerationDisabled`[⬆](#props-index)<!-- Link generated with jump2header -->
780 783
 
781
-Boolean value to disable Hardware Acceleration in the `WebView`. Used on Android only as Hardware Acceleration is a feature only for Android. The default value is `false`.
784
+**Deprecated.** Use the `androidLayerType` prop instead. 
782 785
 
783 786
 | Type | Required | Platform |
784 787
 | ---- | -------- | -------- |
@@ -786,6 +789,24 @@ Boolean value to disable Hardware Acceleration in the `WebView`. Used on Android
786 789
 
787 790
 ---
788 791
 
792
+### `androidLayerType`[⬆](#props-index)<!-- Link generated with jump2header -->
793
+
794
+Specifies the layer type. 
795
+
796
+Possible values for `androidLayerType` are:
797
+
798
+- `none` (default) - The view does not have a layer.
799
+- `software` - The view has a software layer. A software layer is backed by a bitmap and causes the view to be rendered using Android's software rendering pipeline, even if hardware acceleration is enabled.
800
+- `hardware` - The view has a hardware layer. A hardware layer is backed by a hardware specific texture and causes the view to be rendered using Android's hardware rendering pipeline, but only if hardware acceleration is turned on for the view hierarchy.
801
+
802
+Avoid setting both `androidLayerType` and `androidHardwareAccelerationDisabled` props at the same time, as this may cause undefined behaviour.
803
+
804
+| Type   | Required | Platform |
805
+| ------ | -------- | -------- |
806
+| string | No       | Android  |
807
+
808
+---
809
+
789 810
 ### `mixedContentMode`[⬆](#props-index)<!-- Link generated with jump2header -->
790 811
 
791 812
 Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin.
@@ -816,8 +837,8 @@ Boolean value to enable third party cookies in the `WebView`. Used on Android Lo
816 837
 
817 838
 Sets the user-agent for the `WebView`.
818 839
 
819
-| Type   | Required | Platform |
820
-| ------ | -------- | -------- |
840
+| Type   | Required | Platform            |
841
+| ------ | -------- | ------------------- |
821 842
 | string | No       | iOS, Android, macOS |
822 843
 
823 844
 ---
@@ -826,8 +847,8 @@ Sets the user-agent for the `WebView`.
826 847
 
827 848
 Append to the existing user-agent. Setting `userAgent` will override this.
828 849
 
829
-| Type   | Required | Platform |
830
-| ------ | -------- | -------- |
850
+| Type   | Required | Platform            |
851
+| ------ | -------- | ------------------- |
831 852
 | string | No       | iOS, Android, macOS |
832 853
 
833 854
 ```jsx
@@ -917,6 +938,24 @@ Possible values:
917 938
 
918 939
 ---
919 940
 
941
+### `contentMode`[⬆](#props-index)<!-- Link generated with jump2header -->
942
+
943
+Controls the type of content to load. Available on iOS 13 and later. Defaults to `recommended`, which loads mobile content on iPhone & iPad Mini but desktop content on larger iPads.
944
+
945
+See [Introducing Desktop-class Browsing on iPad](https://developer.apple.com/videos/play/wwdc2019/203/) for more.
946
+
947
+Possible values:
948
+
949
+- `recommended`
950
+- `mobile`
951
+- `desktop`
952
+
953
+| Type   | Required | Platform |
954
+| ------ | -------- | -------- |
955
+| string | No       | iOS      |
956
+
957
+---
958
+
920 959
 ### `dataDetectorTypes`[⬆](#props-index)<!-- Link generated with jump2header -->
921 960
 
922 961
 Determines the types of data converted to clickable URLs in the web view's content. By default only phone numbers are detected.
@@ -966,8 +1005,8 @@ The default value is `true`.
966 1005
 
967 1006
 Boolean value that determines whether a horizontal scroll indicator is shown in the `WebView`. The default value is `true`.
968 1007
 
969
-| Type | Required | Platform |
970
-| ---- | -------- | -------- |
1008
+| Type | Required | Platform            |
1009
+| ---- | -------- | ------------------- |
971 1010
 | bool | No       | iOS, Android, macOS |
972 1011
 
973 1012
 ---
@@ -976,8 +1015,8 @@ Boolean value that determines whether a horizontal scroll indicator is shown in
976 1015
 
977 1016
 Boolean value that determines whether a vertical scroll indicator is shown in the `WebView`. The default value is `true`.
978 1017
 
979
-| Type | Required | Platform |
980
-| ---- | -------- | -------- |
1018
+| Type | Required | Platform            |
1019
+| ---- | -------- | ------------------- |
981 1020
 | bool | No       | iOS, Android, macOS |
982 1021
 
983 1022
 ---
@@ -996,8 +1035,8 @@ Set whether Geolocation is enabled in the `WebView`. The default value is `false
996 1035
 
997 1036
 Boolean that sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs. The default value is `false`.
998 1037
 
999
-| Type | Required | Platform |
1000
-| ---- | -------- | -------- |
1038
+| Type | Required | Platform            |
1039
+| ---- | -------- | ------------------- |
1001 1040
 | bool | No       | iOS, Android, macOS |
1002 1041
 
1003 1042
 ---
@@ -1076,8 +1115,8 @@ If true, this will be able horizontal swipe gestures. The default value is `fals
1076 1115
 
1077 1116
 Does not store any data within the lifetime of the WebView.
1078 1117
 
1079
-| Type    | Required | Platform |
1080
-| ------- | -------- | -------- |
1118
+| Type    | Required | Platform            |
1119
+| ------- | -------- | ------------------- |
1081 1120
 | boolean | No       | iOS, Android, macOS |
1082 1121
 
1083 1122
 ---
@@ -1106,8 +1145,8 @@ Sets whether the WebView should disable saving form data. The default value is `
1106 1145
 
1107 1146
 Sets whether WebView should use browser caching.
1108 1147
 
1109
-| Type    | Required | Default | Platform |
1110
-| ------- | -------- | ------- | -------- |
1148
+| Type    | Required | Default | Platform            |
1149
+| ------- | -------- | ------- | ------------------- |
1111 1150
 | boolean | No       | true    | iOS, Android, macOS |
1112 1151
 
1113 1152
 ---
@@ -1173,6 +1212,16 @@ Example:
1173 1212
 
1174 1213
 `<WebView textZoom={100} />`
1175 1214
 
1215
+---
1216
+
1217
+### `pullToRefreshEnabled`[⬆](#props-index)<!-- Link generated with jump2header -->
1218
+
1219
+Boolean value that determines whether a pull to refresh gesture is available in the `WebView`. The default value is `false`. If `true`, sets `bounces` automatically to `true`.
1220
+
1221
+| Type    | Required | Platform |
1222
+| ------- | -------- | -------- |
1223
+| boolean | No       | iOS      |
1224
+
1176 1225
 ### `ignoreSilentHardwareSwitch`[⬆](#props-index)<!-- Link generated with jump2header -->
1177 1226
 
1178 1227
 (ios only)
@@ -1202,18 +1251,35 @@ the file.
1202 1251
 If not provided, the default is to let the webview try to render the file.
1203 1252
 
1204 1253
 Example:
1254
+
1205 1255
 ```jsx
1206 1256
 <WebView
1207 1257
   source={{ uri: 'https://reactnative.dev' }}
1208
-  onFileDownload={ ( { nativeEvent: { downloadUrl } } ) => {
1258
+  onFileDownload={({ nativeEvent: { downloadUrl } }) => {
1209 1259
     // You use downloadUrl which is a string to download files however you want.
1210 1260
   }}
1211
-  />
1261
+/>
1212 1262
 ```
1213 1263
 
1264
+| Type     | Required | Platform |
1265
+| -------- | -------- | -------- |
1266
+| function | No       | iOS      |
1267
+
1268
+---
1269
+
1270
+### `autoManageStatusBarEnabled`
1271
+
1272
+If set to `true`, the status bar will be automatically hidden/shown by WebView, specifically when full screen video is being watched. If `false`, WebView will not manage the status bar at all. The default value is `true`.
1273
+
1214 1274
 | Type    | Required | Platform |
1215 1275
 | ------- | -------- | -------- |
1216
-| function | No       | iOS      |
1276
+| boolean | No       | iOS      |
1277
+
1278
+Example:
1279
+
1280
+```javascript
1281
+<WebView autoManageStatusBarEnabled={false} />
1282
+```
1217 1283
 
1218 1284
 ## Methods
1219 1285
 
@@ -1278,6 +1344,7 @@ Request the webView to ask for focus. (People working on TV apps might want havi
1278 1344
 ```javascript
1279 1345
 postMessage('message');
1280 1346
 ```
1347
+
1281 1348
 Post a message to WebView, handled by [`onMessage`](Reference.md#onmessage).
1282 1349
 
1283 1350
 ### `clearFormData()`[⬆](#methods-index)<!-- Link generated with jump2header -->

+ 24
- 1
index.d.ts Ver arquivo

@@ -6,7 +6,7 @@ export { FileDownload, WebViewMessageEvent, WebViewNavigation } from "./lib/WebV
6 6
 
7 7
 export type WebViewProps = IOSWebViewProps & AndroidWebViewProps;
8 8
 
9
-declare class WebView extends Component<WebViewProps> {
9
+declare class WebView<P = {}> extends Component<WebViewProps & P> {
10 10
     /**
11 11
      * Go back one page in the webview's history.
12 12
      */
@@ -41,6 +41,29 @@ declare class WebView extends Component<WebViewProps> {
41 41
      * Focuses on WebView redered page.
42 42
      */
43 43
     requestFocus: () => void;
44
+    
45
+     /**
46
+     * Posts a message to WebView.
47
+     */
48
+    postMessage: (message: string) => void;
49
+    
50
+     /**
51
+     * (Android only)
52
+     * Removes the autocomplete popup from the currently focused form field, if present.
53
+     */
54
+    clearFormData: () => void;
55
+
56
+     /**
57
+     * (Android only)
58
+     * Clears the resource cache. Note that the cache is per-application, so this will clear the cache for all WebViews used.
59
+     */
60
+    clearCache: (clear: boolean) => void;
61
+
62
+     /**
63
+     * (Android only)
64
+     * Tells this WebView to clear its internal back/forward list.
65
+     */
66
+    clearHistory: () => void;
44 67
 }
45 68
 
46 69
 export {WebView};

+ 2
- 0
metro.config.windows.js Ver arquivo

@@ -35,6 +35,8 @@ module.exports = {
35 35
       new RegExp(
36 36
         `${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
37 37
       ),
38
+      // Avoid error EBUSY: resource busy or locked, open '...\vnext\msbuild.ProjectImports.zip' when building 'vnext\Microsoft.ReactNative.sln' with '/bl'
39
+      /.*\.ProjectImports\.zip/,
38 40
     ]),
39 41
   },
40 42
   transformer: {

+ 2
- 3
package.json Ver arquivo

@@ -8,7 +8,7 @@
8 8
     "Thibault Malbranche <malbranche.thibault@gmail.com>"
9 9
   ],
10 10
   "license": "MIT",
11
-  "version": "10.4.0",
11
+  "version": "10.9.0",
12 12
   "homepage": "https://github.com/react-native-community/react-native-webview#readme",
13 13
   "scripts": {
14 14
     "start": "node node_modules/react-native/local-cli/cli.js start",
@@ -29,8 +29,7 @@
29 29
     "type": "Component"
30 30
   },
31 31
   "peerDependencies": {
32
-    "react": "16.11.0",
33
-    "react-native": ">=0.60 <0.63"
32
+    "react-native": ">=0.60 <0.64"
34 33
   },
35 34
   "dependencies": {
36 35
     "escape-string-regexp": "2.0.0",

+ 8
- 3
src/WebView.android.tsx Ver arquivo

@@ -61,6 +61,7 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
61 61
     saveFormDataDisabled: false,
62 62
     cacheEnabled: true,
63 63
     androidHardwareAccelerationDisabled: false,
64
+    androidLayerType: 'none',
64 65
     originWhitelist: defaultOriginWhitelist,
65 66
   };
66 67
 
@@ -76,6 +77,7 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
76 77
     lastErrorEvent: null,
77 78
   };
78 79
 
80
+  onShouldStartLoadWithRequest: ReturnType<typeof createOnShouldStartLoadWithRequest> | null = null;
79 81
 
80 82
   webViewRef = React.createRef<NativeWebViewAndroid>();
81 83
 
@@ -279,8 +281,11 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
279 281
   onShouldStartLoadWithRequestCallback = (
280 282
     shouldStart: boolean,
281 283
     url: string,
284
+    lockIdentifier?: number,
282 285
   ) => {
283
-    if (shouldStart) {
286
+    if (lockIdentifier) {
287
+      NativeModules.RNCWebView.onShouldStartLoadWithRequestCallback(shouldStart, lockIdentifier);
288
+    } else if (shouldStart) {
284 289
       UIManager.dispatchViewManagerCommand(
285 290
         this.getWebViewHandle(),
286 291
         this.getCommands().loadUrl,
@@ -337,7 +342,7 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
337 342
     const NativeWebView
338 343
       = (nativeConfig.component as typeof NativeWebViewAndroid) || RNCWebView;
339 344
 
340
-    const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
345
+    this.onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
341 346
       this.onShouldStartLoadWithRequestCallback,
342 347
       // casting cause it's in the default props
343 348
       originWhitelist as readonly string[],
@@ -357,7 +362,7 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
357 362
         onHttpError={this.onHttpError}
358 363
         onRenderProcessGone={this.onRenderProcessGone}
359 364
         onMessage={this.onMessage}
360
-        onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
365
+        onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest}
361 366
         ref={this.webViewRef}
362 367
         // TODO: find a better way to type this.
363 368
         source={resolveAssetSource(source as ImageSourcePropType)}

+ 2
- 2
src/WebViewShared.tsx Ver arquivo

@@ -2,8 +2,8 @@ import escapeStringRegexp from 'escape-string-regexp';
2 2
 import React from 'react';
3 3
 import { Linking, View, ActivityIndicator, Text } from 'react-native';
4 4
 import {
5
-  WebViewNavigationEvent,
6 5
   OnShouldStartLoadWithRequest,
6
+  ShouldStartLoadRequestEvent,
7 7
 } from './WebViewTypes';
8 8
 import styles from './WebView.styles';
9 9
 
@@ -39,7 +39,7 @@ const createOnShouldStartLoadWithRequest = (
39 39
   originWhitelist: readonly string[],
40 40
   onShouldStartLoadWithRequest?: OnShouldStartLoadWithRequest,
41 41
 ) => {
42
-  return ({ nativeEvent }: WebViewNavigationEvent) => {
42
+  return ({ nativeEvent }: ShouldStartLoadRequestEvent) => {
43 43
     let shouldStart = true;
44 44
     const { url, lockIdentifier } = nativeEvent;
45 45
 

+ 57
- 9
src/WebViewTypes.ts Ver arquivo

@@ -113,6 +113,10 @@ export interface WebViewNavigation extends WebViewNativeEvent {
113 113
   mainDocumentURL?: string;
114 114
 }
115 115
 
116
+export interface ShouldStartLoadRequest extends WebViewNavigation {
117
+  isTopFrame: boolean;
118
+}
119
+
116 120
 export interface FileDownload {
117 121
   downloadUrl: string;
118 122
 }
@@ -149,6 +153,8 @@ export type WebViewProgressEvent = NativeSyntheticEvent<
149 153
 
150 154
 export type WebViewNavigationEvent = NativeSyntheticEvent<WebViewNavigation>;
151 155
 
156
+export type ShouldStartLoadRequestEvent = NativeSyntheticEvent<ShouldStartLoadRequest>;
157
+
152 158
 export type FileDownloadEvent = NativeSyntheticEvent<FileDownload>;
153 159
 
154 160
 export type WebViewMessageEvent = NativeSyntheticEvent<WebViewMessage>;
@@ -176,6 +182,8 @@ export type OverScrollModeType = 'always' | 'content' | 'never';
176 182
 
177 183
 export type CacheMode = 'LOAD_DEFAULT' | 'LOAD_CACHE_ONLY' | 'LOAD_CACHE_ELSE_NETWORK' | 'LOAD_NO_CACHE';
178 184
 
185
+export type AndroidLayerType = 'none' | 'software' | 'hardware';
186
+
179 187
 export interface WebViewSourceUri {
180 188
   /**
181 189
    * The URI to load in the `WebView`. Can be a local or remote file.
@@ -238,7 +246,7 @@ export interface WebViewNativeConfig {
238 246
 }
239 247
 
240 248
 export type OnShouldStartLoadWithRequest = (
241
-  event: WebViewNavigation,
249
+  event: ShouldStartLoadRequest,
242 250
 ) => boolean;
243 251
 
244 252
 export interface CommonNativeWebViewProps extends ViewProps {
@@ -258,7 +266,7 @@ export interface CommonNativeWebViewProps extends ViewProps {
258 266
   onLoadingStart: (event: WebViewNavigationEvent) => void;
259 267
   onHttpError: (event: WebViewHttpErrorEvent) => void;
260 268
   onMessage: (event: WebViewMessageEvent) => void;
261
-  onShouldStartLoadWithRequest: (event: WebViewNavigationEvent) => void;
269
+  onShouldStartLoadWithRequest: (event: ShouldStartLoadRequestEvent) => void;
262 270
   showsHorizontalScrollIndicator?: boolean;
263 271
   showsVerticalScrollIndicator?: boolean;
264 272
   // TODO: find a better way to type this.
@@ -266,7 +274,7 @@ export interface CommonNativeWebViewProps extends ViewProps {
266 274
   source: any;
267 275
   userAgent?: string;
268 276
   /**
269
-   * Append to the existing user-agent. Overriden if `userAgent` is set.
277
+   * Append to the existing user-agent. Overridden if `userAgent` is set.
270 278
    */
271 279
   applicationNameForUserAgent?: string;
272 280
 }
@@ -278,6 +286,7 @@ export interface AndroidNativeWebViewProps extends CommonNativeWebViewProps {
278 286
   allowFileAccessFromFileURLs?: boolean;
279 287
   allowUniversalAccessFromFileURLs?: boolean;
280 288
   androidHardwareAccelerationDisabled?: boolean;
289
+  androidLayerType?: AndroidLayerType;
281 290
   domStorageEnabled?: boolean;
282 291
   geolocationEnabled?: boolean;
283 292
   javaScriptEnabled?: boolean;
@@ -292,12 +301,9 @@ export interface AndroidNativeWebViewProps extends CommonNativeWebViewProps {
292 301
   readonly urlPrefixesForDefaultIntent?: string[];
293 302
 }
294 303
 
295
-export enum ContentInsetAdjustmentBehavior {
296
-  automatic = 'automatic',
297
-  scrollableAxes = 'scrollableAxes',
298
-  never = 'never',
299
-  always = 'always'
300
-};
304
+export declare type ContentInsetAdjustmentBehavior = 'automatic' | 'scrollableAxes' | 'never' | 'always';
305
+
306
+export declare type ContentMode = 'recommended' | 'mobile' | 'desktop';
301 307
 
302 308
 export interface IOSNativeWebViewProps extends CommonNativeWebViewProps {
303 309
   allowingReadAccessToURL?: string;
@@ -305,9 +311,11 @@ export interface IOSNativeWebViewProps extends CommonNativeWebViewProps {
305 311
   allowsInlineMediaPlayback?: boolean;
306 312
   allowsLinkPreview?: boolean;
307 313
   automaticallyAdjustContentInsets?: boolean;
314
+  autoManageStatusBarEnabled?: boolean;
308 315
   bounces?: boolean;
309 316
   contentInset?: ContentInsetProp;
310 317
   contentInsetAdjustmentBehavior?: ContentInsetAdjustmentBehavior;
318
+  contentMode?: ContentMode;
311 319
   readonly dataDetectorTypes?: DataDetectorTypes | DataDetectorTypes[];
312 320
   decelerationRate?: number;
313 321
   directionalLockEnabled?: boolean;
@@ -405,6 +413,18 @@ export interface IOSWebViewProps extends WebViewSharedProps {
405 413
    */
406 414
   contentInset?: ContentInsetProp;
407 415
 
416
+  /**
417
+   * Defaults to `recommended`, which loads mobile content on iPhone
418
+   * and iPad Mini but desktop content on other iPads.
419
+   *
420
+   * Possible values are:
421
+   * - `'recommended'`
422
+   * - `'mobile'`
423
+   * - `'desktop'`
424
+   * @platform ios
425
+   */
426
+  contentMode?: ContentMode;
427
+
408 428
   /**
409 429
    * Determines the types of data converted to clickable URLs in the web view's content.
410 430
    * By default only phone numbers are detected.
@@ -479,6 +499,13 @@ export interface IOSWebViewProps extends WebViewSharedProps {
479 499
    */
480 500
   sharedCookiesEnabled?: boolean;
481 501
 
502
+  /**
503
+   * Set true if StatusBar should be light when user watch video fullscreen.
504
+   * The default value is `true`.
505
+   * @platform ios
506
+   */
507
+  autoManageStatusBarEnabled?: boolean;
508
+
482 509
   /**
483 510
    * A Boolean value that determines whether scrolling is disabled in a particular direction.
484 511
    * The default value is `true`.
@@ -530,6 +557,15 @@ export interface IOSWebViewProps extends WebViewSharedProps {
530 557
   */
531 558
   injectedJavaScriptBeforeContentLoadedForMainFrameOnly?: boolean;
532 559
 
560
+  /**
561
+   * Boolean value that determines whether a pull to refresh gesture is
562
+   * available in the `WebView`. The default value is `false`.
563
+   * If `true`, sets `bounces` automatically to `true`
564
+   * @platform ios
565
+   *
566
+  */
567
+  pullToRefreshEnabled?: boolean;
568
+
533 569
   /**
534 570
    * Function that is invoked when the client needs to download a file.
535 571
    *
@@ -779,6 +815,18 @@ export interface AndroidWebViewProps extends WebViewSharedProps {
779 815
    */
780 816
   androidHardwareAccelerationDisabled?: boolean;
781 817
 
818
+    /**
819
+   * https://developer.android.com/reference/android/webkit/WebView#setLayerType(int,%20android.graphics.Paint)
820
+   * Sets the layerType. Possible values are:
821
+   *
822
+   * - `'none'` (default)
823
+   * - `'software'`
824
+   * - `'hardware'`
825
+   *
826
+   * @platform android
827
+   */
828
+  androidLayerType?: AndroidLayerType;
829
+
782 830
   /**
783 831
    * Boolean value to enable third party cookies in the `WebView`. Used on
784 832
    * Android Lollipop and above only as third party cookies are enabled by