diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
index f743bbc..6ae5402 100644
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
@@ -5,6 +5,7 @@ import android.annotation.TargetApi;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
@@ -24,12 +25,17 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
import android.webkit.ConsoleMessage;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.GeolocationPermissions;
import android.webkit.JavascriptInterface;
+import android.webkit.JsPromptResult;
+import android.webkit.JsResult;
import android.webkit.RenderProcessGoneDetail;
+import android.webkit.ServiceWorkerClient;
+import android.webkit.ServiceWorkerController;
import android.webkit.SslErrorHandler;
import android.webkit.PermissionRequest;
import android.webkit.URLUtil;
@@ -88,18 +94,37 @@ import com.reactnativecommunity.webview.events.TopRenderProcessGoneEvent;
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
/**
* Manages instances of {@link WebView}
*
@@ -137,13 +162,19 @@ public class RNCWebViewManager extends SimpleViewManager {
public static final int COMMAND_LOAD_URL = 7;
public static final int COMMAND_FOCUS = 8;
+ protected static final String MIME_UNKNOWN = "application/octet-stream";
+ protected static final String HTML_ENCODING = "UTF-8";
+ protected static final long BYTES_IN_MEGABYTE = 1000000;
+
// android commands
public static final int COMMAND_CLEAR_FORM_DATA = 1000;
public static final int COMMAND_CLEAR_CACHE = 1001;
public static final int COMMAND_CLEAR_HISTORY = 1002;
protected static final String REACT_CLASS = "RNCWebView";
- protected static final String HTML_ENCODING = "UTF-8";
+
+ protected static final String HEADER_CONTENT_TYPE = "content-type";
+
protected static final String HTML_MIME_TYPE = "text/html";
protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView";
protected static final String HTTP_METHOD_POST = "POST";
@@ -158,11 +189,20 @@ public class RNCWebViewManager extends SimpleViewManager {
protected @Nullable String mUserAgent = null;
protected @Nullable String mUserAgentWithApplicationName = null;
+ protected static String userAgent;
+
+ protected static OkHttpClient httpClient;
+
public RNCWebViewManager() {
mWebViewConfig = new WebViewConfig() {
public void configWebView(WebView webView) {
}
};
+
+ httpClient = new OkHttpClient.Builder()
+ .followRedirects(false)
+ .followSslRedirects(false)
+ .build();
}
public RNCWebViewManager(WebViewConfig webViewConfig) {
@@ -182,6 +222,7 @@ public class RNCWebViewManager extends SimpleViewManager {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
protected WebView createViewInstance(ThemedReactContext reactContext) {
RNCWebView webView = createRNCWebViewInstance(reactContext);
+ userAgent = webView.getSettings().getUserAgentString();
setupWebChromeClient(reactContext, webView);
reactContext.addLifecycleEventListener(webView);
mWebViewConfig.configWebView(webView);
@@ -247,9 +288,95 @@ public class RNCWebViewManager extends SimpleViewManager {
}
});
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ ServiceWorkerController swController = ServiceWorkerController.getInstance();
+ swController.setServiceWorkerClient(new ServiceWorkerClient() {
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
+ WebResourceResponse response = RNCWebViewManager.this.shouldInterceptRequest(request, false, webView);
+ if (response != null) {
+ return response;
+ }
+
+ return super.shouldInterceptRequest(request);
+ }
+ });
+ }
+
return webView;
}
+ private Boolean urlStringLooksInvalid(String urlString) {
+ return urlString == null ||
+ urlString.trim().equals("") ||
+ !(urlString.startsWith("http") && !urlString.startsWith("www")) ||
+ urlString.contains("|");
+ }
+
+ public static Boolean responseRequiresJSInjection(Response response) {
+ if (response.isRedirect()) {
+ return false;
+ }
+ final String contentTypeAndCharset = response.header(HEADER_CONTENT_TYPE, MIME_UNKNOWN);
+ final int responseCode = response.code();
+
+ boolean contentTypeIsHtml = contentTypeAndCharset.startsWith(HTML_MIME_TYPE);
+ boolean responseCodeIsInjectible = responseCode == 200;
+ String responseBody = "";
+
+ if (contentTypeIsHtml && responseCodeIsInjectible) {
+ try {
+ assert response.body() != null;
+ responseBody = response.peekBody(BYTES_IN_MEGABYTE).string();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+
+ boolean responseBodyContainsHTMLLikeString = responseBody.matches("[\\S\\s]*<[a-z]+[\\S\\s]*>[\\S\\s]*");
+ return responseBodyContainsHTMLLikeString;
+ } else {
+ return false;
+ }
+ }
+
+ public WebResourceResponse shouldInterceptRequest(WebResourceRequest request, Boolean onlyMainFrame, RNCWebView webView) {
+ Uri url = request.getUrl();
+ String urlStr = url.toString();
+
+ if (onlyMainFrame && !request.isForMainFrame() ||
+ urlStringLooksInvalid(urlStr)) {
+ return null;//super.shouldInterceptRequest(webView, request);
+ }
+
+ try {
+ Request req = new Request.Builder()
+ .url(urlStr)
+ .header("User-Agent", userAgent)
+ .build();
+
+ Response response = httpClient.newCall(req).execute();
+
+ if (!responseRequiresJSInjection(response)) {
+ return null;
+ }
+
+ InputStream is = response.body().byteStream();
+ MediaType contentType = response.body().contentType();
+ Charset charset = contentType != null ? contentType.charset(StandardCharsets.UTF_8) : StandardCharsets.UTF_8;
+
+ RNCWebView reactWebView = (RNCWebView) webView;
+ if (response.code() == HttpURLConnection.HTTP_OK) {
+ is = new InputStreamWithInjectedJS(is, reactWebView.injectedJSBeforeContentLoaded, charset);
+ }
+
+ return new WebResourceResponse("text/html", charset.name(), is);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
@ReactProp(name = "javaScriptEnabled")
public void setJavaScriptEnabled(WebView view, boolean enabled) {
view.getSettings().setJavaScriptEnabled(enabled);
@@ -842,13 +969,116 @@ public class RNCWebViewManager extends SimpleViewManager {
}
}
- protected static class RNCWebViewClient extends WebViewClient {
+ public static class InputStreamWithInjectedJS extends InputStream {
+ private InputStream pageIS;
+ private InputStream scriptIS;
+ private Charset charset;
+ private static final String REACT_CLASS = "InputStreamWithInjectedJS";
+ private static Map script = new HashMap<>();
+
+ private boolean hasJS = false;
+ private boolean headWasFound = false;
+ private boolean scriptWasInjected = false;
+
+ private int lowercaseD = 100;
+ private int closingTag = 62;
+ private boolean hasClosingHead = false;
+
+ private StringBuffer contentBuffer = new StringBuffer();
+
+ @SuppressLint("LongLogTag")
+ private static Charset getCharset(String charsetName) {
+ Charset cs = StandardCharsets.UTF_8;
+ try {
+ if (charsetName != null) {
+ cs = Charset.forName(charsetName);
+ }
+ } catch (UnsupportedCharsetException e) {
+ Log.d(REACT_CLASS, "wrong charset: " + charsetName);
+ }
+
+ return cs;
+ }
+
+ private static InputStream getScript(Charset charset) {
+ String js = script.get(charset);
+ if (js == null) {
+ String defaultJs = script.get(StandardCharsets.UTF_8);
+ js = new String(defaultJs.getBytes(StandardCharsets.UTF_8), charset);
+ script.put(charset, js);
+ }
+
+ return new ByteArrayInputStream(js.getBytes(charset));
+ }
+
+ InputStreamWithInjectedJS(InputStream is, String js, Charset charset) {
+ if (js == null) {
+ this.pageIS = is;
+ } else {
+ this.hasJS = true;
+ this.charset = charset;
+ Charset cs = StandardCharsets.UTF_8;
+ String jsScript = "";
+ script.put(cs, jsScript);
+ this.pageIS = is;
+ }
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (scriptWasInjected || !hasJS) {
+ return pageIS.read();
+ }
+
+ if (!scriptWasInjected && headWasFound) {
+ int nextByte;
+ if (!hasClosingHead) {
+ nextByte = pageIS.read();
+ if (nextByte != closingTag) {
+ return nextByte;
+ }
+ hasClosingHead = true;
+ return nextByte;
+ }
+ nextByte = scriptIS.read();
+ if (nextByte == -1) {
+ scriptIS.close();
+ scriptWasInjected = true;
+ return pageIS.read();
+ } else {
+ return nextByte;
+ }
+ }
+
+ if (!headWasFound) {
+ int nextByte = pageIS.read();
+ contentBuffer.append((char) nextByte);
+ int bufferLength = contentBuffer.length();
+ if (nextByte == lowercaseD && bufferLength >= 5) {
+ if (contentBuffer.substring(bufferLength - 5).equals(" {
emitFinishEvent(webView, url);
}
+ if(Objects.nonNull(mWebChromeClient)) mWebChromeClient.blockJsDuringLoading = false;
}
+
+
@Override
public void onPageStarted(WebView webView, String url, Bitmap favicon) {
super.onPageStarted(webView, url, favicon);
+ if(Objects.nonNull(mWebChromeClient)) mWebChromeClient.blockJsDuringLoading = true;
mLastLoadFailed = false;
- RNCWebView reactWebView = (RNCWebView) webView;
- reactWebView.callInjectedJavaScriptBeforeContentLoaded();
-
((RNCWebView) webView).dispatchEvent(
webView,
new TopLoadingStartEvent(
@@ -882,6 +1113,17 @@ public class RNCWebViewManager extends SimpleViewManager {
createWebViewEvent(webView, url)));
}
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebView webView, WebResourceRequest request) {
+ WebResourceResponse response = RNCWebViewManager.this.shouldInterceptRequest(request, true, (RNCWebView)webView);
+
+ if (response != null) {
+ return response;
+ }
+
+ return super.shouldInterceptRequest(webView, request);
+ }
+
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
final RNCWebView rncWebView = (RNCWebView) view;
@@ -891,7 +1133,6 @@ public class RNCWebViewManager extends SimpleViewManager {
final Pair> lock = RNCWebViewModule.shouldOverrideUrlLoadingLock.getNewLock();
final int lockIdentifier = lock.first;
final AtomicReference lockObject = lock.second;
-
final WritableMap event = createWebViewEvent(view, url);
event.putInt("lockIdentifier", lockIdentifier);
rncWebView.sendDirectMessage("onShouldStartLoadWithRequest", event);
@@ -919,6 +1160,17 @@ public class RNCWebViewManager extends SimpleViewManager {
RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
return shouldOverride;
+ } else if (url != null && Arrays.asList(DEEPLINK_ALLOW_LIST).contains(url)) {
+ // This case is used to support deeplinking within the webview. We are limiting this but
+ // if more links are to be supported we should consider a more scaleable solution. That is
+ // secure and scaleable.
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(url));
+ if(intent.resolveActivity(view.getContext().getPackageManager()) != null) {
+ view.getContext().startActivity(intent);
+ return true;
+ } else
+ return false;
} else {
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.");
progressChangedFilter.setWaitingForCommandLoadUrl(true);
@@ -934,10 +1186,29 @@ public class RNCWebViewManager extends SimpleViewManager {
@TargetApi(Build.VERSION_CODES.N)
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+ if(Objects.nonNull(mWebChromeClient)) mWebChromeClient.blockJsDuringLoading = true;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+
+ /*
+ * In order to follow redirects properly, we return null in interceptRequest().
+ * Doing this breaks the web3 injection on the resulting page, so we have to reload to
+ * make sure web3 is available.
+ * */
+
+ if (request.isForMainFrame() && request.isRedirect()) {
+ view.loadUrl(request.getUrl().toString());
+ return true;
+ }
+ }
+
final String url = request.getUrl().toString();
+
return this.shouldOverrideUrlLoading(view, url);
}
+
+
@Override
public void onReceivedSslError(final WebView webView, final SslErrorHandler handler, final SslError error) {
// onReceivedSslError is called for most requests, per Android docs: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%2520android.webkit.SslErrorHandler,%2520android.net.http.SslError)
@@ -1138,6 +1409,7 @@ public class RNCWebViewManager extends SimpleViewManager {
protected View mVideoView;
protected WebChromeClient.CustomViewCallback mCustomViewCallback;
+ protected boolean blockJsDuringLoading = true; //This boolean block JS prompts and alerts from displaying during loading
/*
* - Permissions -
@@ -1402,6 +1674,15 @@ public class RNCWebViewManager extends SimpleViewManager {
}
}
+ @Override
+ public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
+ if(blockJsDuringLoading) {
+ result.cancel();
+ return true;
+ } else
+ return super.onJsPrompt(view, url, message, defaultValue, result);
+ }
+
@Override
public void onHostPause() { }
@@ -1447,6 +1728,13 @@ public class RNCWebViewManager extends SimpleViewManager {
protected boolean nestedScrollEnabled = false;
protected ProgressChangedFilter progressChangedFilter;
+ /**
+ * Taken from EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING We can't use that
+ * value directly as it was only added on Oreo, but we can apply the value
+ * anyway.
+ */
+ private static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 0x1000000;
+
/**
* WebView must be created with an context of the current activity
*
@@ -1475,6 +1763,42 @@ public class RNCWebViewManager extends SimpleViewManager {
this.nestedScrollEnabled = nestedScrollEnabled;
}
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ InputConnection inputConnection;
+ if (!usingGoogleKeyboard()) {
+ inputConnection = super.onCreateInputConnection(outAttrs);
+ } else {
+ inputConnection = new BaseInputConnection(this, false);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING;
+ } else {
+ // Cover OS versions below Oreo
+ outAttrs.imeOptions = IME_FLAG_NO_PERSONALIZED_LEARNING;
+ }
+ }
+
+ return inputConnection;
+ }
+
+ public boolean usingGoogleKeyboard() {
+ final InputMethodManager richImm =
+ (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ boolean isKeyboard = false;
+
+ final Field field;
+ try {
+ field = richImm.getClass().getDeclaredField("mCurId");
+ field.setAccessible(true);
+ Object value = field.get(richImm);
+ isKeyboard = Objects.equals(value, "com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME");
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ return false;
+ }
+ return isKeyboard;
+ }
+
@Override
public void onHostResume() {
// do nothing
@@ -1533,6 +1857,8 @@ public class RNCWebViewManager extends SimpleViewManager {
}
}
+
+
public @Nullable
RNCWebViewClient getRNCWebViewClient() {
return mRNCWebViewClient;
diff --git a/node_modules/react-native-webview/apple/RNCWebView.m b/node_modules/react-native-webview/apple/RNCWebView.m
index 28c078a..b1cb2f2 100644
--- a/node_modules/react-native-webview/apple/RNCWebView.m
+++ b/node_modules/react-native-webview/apple/RNCWebView.m
@@ -105,6 +105,7 @@ @implementation RNCWebView
UIStatusBarStyle _savedStatusBarStyle;
#endif // !TARGET_OS_OSX
BOOL _savedStatusBarHidden;
+ BOOL _disablePromptDuringLoading; //Disables the display of prompts during site navigation/loading
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
UIScrollViewContentInsetAdjustmentBehavior _savedContentInsetAdjustmentBehavior;
@@ -139,6 +140,7 @@ - (instancetype)initWithFrame:(CGRect)frame
_injectedJavaScriptForMainFrameOnly = YES;
_injectedJavaScriptBeforeContentLoaded = nil;
_injectedJavaScriptBeforeContentLoadedForMainFrameOnly = YES;
+ _disablePromptDuringLoading = YES;
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
_savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
@@ -417,6 +419,7 @@ -(void)keyboardDisplacementFix
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
if(_onLoadingProgress){
+ _disablePromptDuringLoading = YES;
NSMutableDictionary *event = [self baseEvent];
[event addEntriesFromDictionary:@{@"progress":[NSNumber numberWithDouble:self.webView.estimatedProgress]}];
_onLoadingProgress(event);
@@ -492,6 +495,7 @@ - (void)userContentController:(WKUserContentController *)userContentController
NSMutableDictionary *event = [self baseEvent];
[event addEntriesFromDictionary: @{@"navigationType": message.body}];
_onLoadingFinish(event);
+ _disablePromptDuringLoading = NO;
}
} else if ([message.name isEqualToString:MessageHandlerName]) {
if (_onMessage) {
@@ -851,11 +855,13 @@ - (void) webView:(WKWebView *)webView
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
#if !TARGET_OS_OSX
- UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
- [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
- completionHandler();
- }]];
- [[self topViewController] presentViewController:alert animated:YES completion:NULL];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
+ [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
+ completionHandler();
+ }]];
+ [[self topViewController] presentViewController:alert animated:YES completion:NULL];
+ });
#else
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:message];
@@ -894,44 +900,49 @@ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSStr
* prompt
*/
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler{
-#if !TARGET_OS_OSX
- UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
- [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
- textField.text = defaultText;
- }];
- UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
- completionHandler([[alert.textFields lastObject] text]);
- }];
- [alert addAction:okAction];
- UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
- completionHandler(nil);
- }];
- [alert addAction:cancelAction];
- alert.preferredAction = okAction;
- [[self topViewController] presentViewController:alert animated:YES completion:NULL];
-#else
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:prompt];
-
- const NSRect RCTSingleTextFieldFrame = NSMakeRect(0.0, 0.0, 275.0, 22.0);
- NSTextField *textField = [[NSTextField alloc] initWithFrame:RCTSingleTextFieldFrame];
- textField.cell.scrollable = YES;
- if (@available(macOS 10.11, *)) {
- textField.maximumNumberOfLines = 1;
- }
- textField.stringValue = defaultText;
- [alert setAccessoryView:textField];
- [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")];
- [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button")];
- [alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:^(NSModalResponse response) {
- if (response == NSAlertFirstButtonReturn) {
- completionHandler([textField stringValue]);
+ if(!_disablePromptDuringLoading) {
+ #if !TARGET_OS_OSX
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
+ [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+ textField.text = defaultText;
+ }];
+ UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
+ completionHandler([[alert.textFields lastObject] text]);
+ }];
+ [alert addAction:okAction];
+ UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
+ completionHandler(nil);
+ }];
+ [alert addAction:cancelAction];
+ alert.preferredAction = okAction;
+ [[self topViewController] presentViewController:alert animated:YES completion:NULL];
+ #else
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:prompt];
+
+ const NSRect RCTSingleTextFieldFrame = NSMakeRect(0.0, 0.0, 275.0, 22.0);
+ NSTextField *textField = [[NSTextField alloc] initWithFrame:RCTSingleTextFieldFrame];
+ textField.cell.scrollable = YES;
+ if (@available(macOS 10.11, *)) {
+ textField.maximumNumberOfLines = 1;
+ }
+ textField.stringValue = defaultText;
+ [alert setAccessoryView:textField];
+
+ [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button")];
+ [alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:^(NSModalResponse response) {
+ if (response == NSAlertFirstButtonReturn) {
+ completionHandler([textField stringValue]);
+ } else {
+ completionHandler(nil);
+ }
+ }];
+ #endif // !TARGET_OS_OSX
} else {
- completionHandler(nil);
+ completionHandler(nil);
}
- }];
-#endif // !TARGET_OS_OSX
}
#if !TARGET_OS_OSX
@@ -1157,6 +1168,7 @@ - (void)webView:(WKWebView *)webView
}
if (_onLoadingFinish) {
+ _disablePromptDuringLoading = NO;
_onLoadingFinish([self baseEvent]);
}
}