Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/huge-onions-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@phantom/react-native-webview': minor
---

Added a new prop that allows marking webviews as active or inactive.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import android.view.ViewGroup;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
Expand Down Expand Up @@ -479,4 +481,34 @@ public void setAllowsProtectedMedia(boolean enabled) {
public void setHasOnOpenWindowEvent(boolean hasEvent) {
mHasOnOpenWindowEvent = hasEvent;
}
}

/**
* Security: Prevent dialogs from being presented when webview is inactive.
*/
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
if (!mWebView.isActive()) {
result.cancel();
return true; // Consumed - don't show default dialog
}
return false; // Show default system dialog
}

@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
if (!mWebView.isActive()) {
result.cancel();
return true;
}
return false;
}

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
if (!mWebView.isActive()) {
result.cancel();
return true;
}
return false;
}
Comment thread
jkadamczyk marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
protected boolean hasScrollEvent = false;
protected boolean nestedScrollEnabled = false;
protected ProgressChangedFilter progressChangedFilter;
protected boolean mActive = true;

/**
* WebView must be created with an context of the current activity
Expand Down Expand Up @@ -107,6 +108,14 @@ public void setNestedScrollEnabled(boolean nestedScrollEnabled) {
this.nestedScrollEnabled = nestedScrollEnabled;
}

public void setActive(boolean active) {
this.mActive = active;
}

public boolean isActive() {
return this.mActive;
}

@Override
public void onHostResume() {
// do nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,4 +681,8 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
WebSettingsCompat.setPaymentRequestEnabled(view.settings, enabled)
}
}

fun setActive(viewWrapper: RNCWebViewWrapper, value: Boolean) {
viewWrapper.webView.setActive(value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ public void setMediaCapturePermissionGrantType(RNCWebViewWrapper view, @Nullable
public void setFraudulentWebsiteWarningEnabled(RNCWebViewWrapper view, boolean value) {}
/* !iOS PROPS - no implemented here */

@Override
@ReactProp(name = "active")
public void setActive(RNCWebViewWrapper view, boolean value) {
mRNCWebViewManagerImpl.setActive(view, value);
}

@Override
@ReactProp(name = "userAgent")
public void setUserAgent(RNCWebViewWrapper view, @Nullable String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ public void setPaymentRequestEnabled(RNCWebViewWrapper view, boolean value) {
mRNCWebViewManagerImpl.setPaymentRequestEnabled(view, value);
}

@ReactProp(name = "active")
public void setActive(RNCWebViewWrapper view, boolean value) {
mRNCWebViewManagerImpl.setActive(view, value);
}

@Override
protected void addEventEmitters(@NonNull ThemedReactContext reactContext, RNCWebViewWrapper viewWrapper) {
// Do not register default touch emitter and let WebView implementation handle touches
Expand Down
1 change: 1 addition & 0 deletions apple/RNCWebViewImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
@property (nonatomic, copy) NSArray<NSDictionary *> * _Nullable menuItems;
@property (nonatomic, copy) NSArray<NSString *> * _Nullable suppressMenuItems;
@property (nonatomic, copy) RCTDirectEventBlock onCustomMenuSelection;
@property (nonatomic, assign) BOOL active;
#if !TARGET_OS_OSX
@property (nonatomic, assign) WKDataDetectorTypes dataDetectorTypes;
@property (nonatomic, weak) UIRefreshControl * _Nullable refreshControl;
Expand Down
16 changes: 16 additions & 0 deletions apple/RNCWebViewImpl.m
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ - (instancetype)initWithFrame:(CGRect)frame
_autoManageStatusBarEnabled = YES;
_contentInset = UIEdgeInsetsZero;
_savedKeyboardDisplayRequiresUserAction = YES;
_active = YES;
_injectedJavaScript = nil;
_injectedJavaScriptForMainFrameOnly = YES;
_injectedJavaScriptBeforeContentLoaded = nil;
Expand Down Expand Up @@ -1203,6 +1204,11 @@ - (void) webView:(WKWebView *)webView
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
// Security: Suppress dialogs from non-active tabs to prevent cross-tab spoofing
if (!_active) {
completionHandler();
return;
}
#if !TARGET_OS_OSX
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) {
Expand All @@ -1222,6 +1228,11 @@ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSStrin
* confirm
*/
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
// Security: Suppress dialogs from non-active tabs to prevent cross-tab spoofing
if (!_active) {
completionHandler(NO);
return;
}
#if !TARGET_OS_OSX
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) {
Expand All @@ -1247,6 +1258,11 @@ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSStr
* prompt
*/
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler{
// Security: Suppress dialogs from non-active tabs to prevent cross-tab spoofing
if (!_active) {
completionHandler(nil);
return;
}
#if !TARGET_OS_OSX
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
Expand Down
3 changes: 3 additions & 0 deletions apple/RNCWebViewManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ - (RNCView *)view
RCT_CUSTOM_VIEW_PROPERTY(pullToRefreshEnabled, BOOL, RNCWebViewImpl) {
view.pullToRefreshEnabled = json == nil ? false : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(active, BOOL, RNCWebViewImpl) {
view.active = json == nil ? YES : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(refreshControlLightMode, BOOL, RNCWebViewImpl) {
view.refreshControlLightMode = json == nil ? false : [RCTConvert BOOL: json];
}
Expand Down
1 change: 1 addition & 0 deletions src/RNCWebViewNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ export interface NativeProps extends ViewProps {
userAgent?: string;
injectedJavaScriptObject?: string;
paymentRequestEnabled?: boolean;
active?: WithDefault<boolean, true>;
}

export interface NativeCommands {
Expand Down
9 changes: 9 additions & 0 deletions src/WebViewTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1358,4 +1358,13 @@ export interface WebViewSharedProps extends ViewProps {
* Enables support for the Payment Request API for the WebView
*/
paymentRequestEnabled?: boolean;

/**
* Allows marking the WebView as active or inactive.
* When a WebView is marked inactive, JS dialogs and alerts will be blocked and won't be shown to the user
* @platform android
* @platform ios
* @default true
*/
active?: boolean;
}
Loading