Skip to content

Commit a1144d9

Browse files
authored
Prevent JS prompts during page loading (#20)
Block JS prompts during page loading. Patch ported from here MetaMask/metamask-mobile@5ee210a
1 parent 0b60588 commit a1144d9

File tree

3 files changed

+73
-36
lines changed

3 files changed

+73
-36
lines changed

android/src/main/java/com/reactnativecommunity/webview/RNCWebChromeClient.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import android.view.ViewGroup;
1616
import android.webkit.ConsoleMessage;
1717
import android.webkit.GeolocationPermissions;
18+
import android.webkit.JsPromptResult;
1819
import android.webkit.PermissionRequest;
1920
import android.webkit.ValueCallback;
2021
import android.webkit.WebChromeClient;
@@ -59,6 +60,9 @@ public class RNCWebChromeClient extends WebChromeClient implements LifecycleEven
5960
protected View mVideoView;
6061
protected WebChromeClient.CustomViewCallback mCustomViewCallback;
6162

63+
// This boolean block JS prompts and alerts from displaying during loading
64+
protected boolean blockJsDuringLoading = true;
65+
6266
/*
6367
* - Permissions -
6468
* As native permissions are asynchronously handled by the PermissionListener, many fields have
@@ -383,6 +387,16 @@ public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathC
383387
return this.mWebView.getThemedReactContext().getNativeModule(RNCWebViewModule.class).startPhotoPickerIntent(filePathCallback, acceptTypes, allowMultiple, fileChooserParams.isCaptureEnabled());
384388
}
385389

390+
@Override
391+
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
392+
if (blockJsDuringLoading) {
393+
result.cancel();
394+
return true;
395+
} else {
396+
return super.onJsPrompt(view, url, message, defaultValue, result);
397+
}
398+
}
399+
386400
@Override
387401
public void onHostResume() {
388402
if (mVideoView != null && mVideoView.getSystemUiVisibility() != FULLSCREEN_SYSTEM_UI_VISIBILITY) {

android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import android.webkit.HttpAuthHandler;
1010
import android.webkit.RenderProcessGoneDetail;
1111
import android.webkit.SslErrorHandler;
12+
import android.webkit.WebChromeClient;
1213
import android.webkit.WebResourceRequest;
1314
import android.webkit.WebResourceResponse;
1415
import android.webkit.WebView;
@@ -32,6 +33,7 @@
3233
import com.reactnativecommunity.webview.events.TopRenderProcessGoneEvent;
3334
import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
3435

36+
import java.util.Objects;
3537
import java.util.concurrent.atomic.AtomicReference;
3638

3739
public class RNCWebViewClient extends WebViewClient {
@@ -58,6 +60,9 @@ public void onPageFinished(WebView webView, String url) {
5860
if (!mLastLoadFailed) {
5961
RNCWebView reactWebView = (RNCWebView) webView;
6062

63+
RNCWebChromeClient webChromeClient = (RNCWebChromeClient) reactWebView.getWebChromeClient();
64+
if(Objects.nonNull(webChromeClient)) webChromeClient.blockJsDuringLoading = false;
65+
6166
reactWebView.callInjectedJavaScript();
6267

6368
emitFinishEvent(webView, url);
@@ -81,12 +86,20 @@ public void onPageStarted(WebView webView, String url, Bitmap favicon) {
8186
mLastLoadFailed = false;
8287

8388
RNCWebView reactWebView = (RNCWebView) webView;
89+
90+
RNCWebChromeClient webChromeClient = (RNCWebChromeClient) reactWebView.getWebChromeClient();
91+
if(Objects.nonNull(webChromeClient)) webChromeClient.blockJsDuringLoading = true;
92+
8493
reactWebView.callInjectedJavaScriptBeforeContentLoaded();
8594
}
8695

8796
@Override
8897
public boolean shouldOverrideUrlLoading(WebView view, String url) {
8998
final RNCWebView rncWebView = (RNCWebView) view;
99+
100+
RNCWebChromeClient webChromeClient = (RNCWebChromeClient) rncWebView.getWebChromeClient();
101+
if(Objects.nonNull(webChromeClient)) webChromeClient.blockJsDuringLoading = true;
102+
90103
final boolean isJsDebugging = ((ReactContext) view.getContext()).getJavaScriptContextHolder().get() == 0;
91104

92105
if (!isJsDebugging && rncWebView.mCatalystInstance != null) {

apple/RNCWebViewImpl.m

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ - (NSString *)stringFromAction:(SEL) action {
7474
@"toggleUnderline:": @"underline",
7575
@"_share:": @"share",
7676
};
77-
77+
7878
return map[sel] ?: sel;
7979
}
8080

@@ -86,7 +86,7 @@ - (BOOL)canPerformAction:(SEL)action
8686
return NO;
8787
}
8888
}
89-
89+
9090
if (!self.menuItems) {
9191
return [super canPerformAction:action withSender:sender];
9292
}
@@ -143,6 +143,8 @@ @implementation RNCWebViewImpl
143143
UIStatusBarStyle _savedStatusBarStyle;
144144
#endif // !TARGET_OS_OSX
145145
BOOL _savedStatusBarHidden;
146+
//Disables the display of prompts during site navigation/loading
147+
BOOL _disablePromptDuringLoading;
146148

147149
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
148150
UIScrollViewContentInsetAdjustmentBehavior _savedContentInsetAdjustmentBehavior;
@@ -177,6 +179,7 @@ - (instancetype)initWithFrame:(CGRect)frame
177179
_injectedJavaScriptForMainFrameOnly = YES;
178180
_injectedJavaScriptBeforeContentLoaded = nil;
179181
_injectedJavaScriptBeforeContentLoadedForMainFrameOnly = YES;
182+
_disablePromptDuringLoading = YES;
180183

181184
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
182185
_savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
@@ -649,6 +652,7 @@ -(void)keyboardDisplacementFix
649652
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
650653
if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
651654
if(_onLoadingProgress){
655+
_disablePromptDuringLoading = YES;
652656
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
653657
[event addEntriesFromDictionary:@{@"progress":[NSNumber numberWithDouble:self.webView.estimatedProgress]}];
654658
_onLoadingProgress(event);
@@ -726,6 +730,7 @@ - (void)userContentController:(WKUserContentController *)userContentController
726730
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
727731
[event addEntriesFromDictionary: @{@"navigationType": message.body}];
728732
_onLoadingFinish(event);
733+
_disablePromptDuringLoading = NO;
729734
}
730735
} else if ([message.name isEqualToString:MessageHandlerName]) {
731736
if (_onMessage) {
@@ -1150,44 +1155,48 @@ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSStr
11501155
* prompt
11511156
*/
11521157
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler{
1158+
if (!_disablePromptDuringLoading) {
11531159
#if !TARGET_OS_OSX
1154-
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
1155-
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
1156-
textField.text = defaultText;
1157-
}];
1158-
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
1159-
completionHandler([[alert.textFields lastObject] text]);
1160-
}];
1161-
[alert addAction:okAction];
1162-
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
1163-
completionHandler(nil);
1164-
}];
1165-
[alert addAction:cancelAction];
1166-
alert.preferredAction = okAction;
1167-
[[self topViewController] presentViewController:alert animated:YES completion:NULL];
1168-
#else
1169-
NSAlert *alert = [[NSAlert alloc] init];
1170-
[alert setMessageText:prompt];
1171-
1172-
const NSRect RCTSingleTextFieldFrame = NSMakeRect(0.0, 0.0, 275.0, 22.0);
1173-
NSTextField *textField = [[NSTextField alloc] initWithFrame:RCTSingleTextFieldFrame];
1174-
textField.cell.scrollable = YES;
1175-
if (@available(macOS 10.11, *)) {
1176-
textField.maximumNumberOfLines = 1;
1177-
}
1178-
textField.stringValue = defaultText;
1179-
[alert setAccessoryView:textField];
1180-
1181-
[alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")];
1182-
[alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button")];
1183-
[alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:^(NSModalResponse response) {
1184-
if (response == NSAlertFirstButtonReturn) {
1185-
completionHandler([textField stringValue]);
1186-
} else {
1160+
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
1161+
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
1162+
textField.text = defaultText;
1163+
}];
1164+
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
1165+
completionHandler([[alert.textFields lastObject] text]);
1166+
}];
1167+
[alert addAction:okAction];
1168+
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
11871169
completionHandler(nil);
1170+
}];
1171+
[alert addAction:cancelAction];
1172+
alert.preferredAction = okAction;
1173+
[[self topViewController] presentViewController:alert animated:YES completion:NULL];
1174+
#else
1175+
NSAlert *alert = [[NSAlert alloc] init];
1176+
[alert setMessageText:prompt];
1177+
1178+
const NSRect RCTSingleTextFieldFrame = NSMakeRect(0.0, 0.0, 275.0, 22.0);
1179+
NSTextField *textField = [[NSTextField alloc] initWithFrame:RCTSingleTextFieldFrame];
1180+
textField.cell.scrollable = YES;
1181+
if (@available(macOS 10.11, *)) {
1182+
textField.maximumNumberOfLines = 1;
11881183
}
1189-
}];
1184+
textField.stringValue = defaultText;
1185+
[alert setAccessoryView:textField];
1186+
1187+
[alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")];
1188+
[alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button")];
1189+
[alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:^(NSModalResponse response) {
1190+
if (response == NSAlertFirstButtonReturn) {
1191+
completionHandler([textField stringValue]);
1192+
} else {
1193+
completionHandler(nil);
1194+
}
1195+
}];
11901196
#endif // !TARGET_OS_OSX
1197+
} else {
1198+
completionHandler(nil);
1199+
}
11911200
}
11921201

11931202
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 /* iOS 15 */
@@ -1480,6 +1489,7 @@ - (void)webView:(WKWebView *)webView
14801489
}
14811490

14821491
if (_onLoadingFinish) {
1492+
_disablePromptDuringLoading = NO;
14831493
_onLoadingFinish([self baseEvent]);
14841494
}
14851495
}

0 commit comments

Comments
 (0)