Permalink
Browse files

android: allow whitelisting urls to bypass default webview loading

Summary:
This is a workaround for missing PDF url support in Android WebView, which is a general known issue: when tapping a PDF url within WebView, instead of doing nothing, we just let android default intent handle it (e.g. it will open Chrome to load it).

This is basically to trick `shouldOverrideUrlLoading()` to return true for the specific url. The drawback is that product code needs to provide the whitelist.

The proper fix would be to use PdfRenderer in that method, but it seems like it's only for API >= 21...

Differential Revision: D5619383

fbshipit-source-id: f86b930f970dab9a5f57999df69ce94b9508edc9
  • Loading branch information...
fkgozali authored and facebook-github-bot committed Aug 17, 2017
1 parent 1cc7ae2 commit 40a2885847780d8b0a54d8ab8ba1fba2fa6aa275
@@ -195,6 +195,15 @@ class WebView extends React.Component {
* @platform android
*/
saveFormDataDisabled: PropTypes.bool,
/**
* Used on Android only, controls whether the given list of URL prefixes should
* make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a
* default activity intent for those URL instead of loading it within the webview.
* Use this to list URLs that WebView cannot handle, e.g. a PDF url.
* @platform android
*/
urlPrefixesForDefaultIntent: PropTypes.arrayOf(PropTypes.string),
};
static defaultProps = {
@@ -276,6 +285,7 @@ class WebView extends React.Component {
allowUniversalAccessFromFileURLs={this.props.allowUniversalAccessFromFileURLs}
mixedContentMode={this.props.mixedContentMode}
saveFormDataDisabled={this.props.saveFormDataDisabled}
urlPrefixesForDefaultIntent={this.props.urlPrefixesForDefaultIntent}
/>;
return (
@@ -12,6 +12,7 @@
import javax.annotation.Nullable;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -110,6 +111,7 @@
protected static class ReactWebViewClient extends WebViewClient {
protected boolean mLastLoadFailed = false;
protected @Nullable ReadableArray mUrlPrefixesForDefaultIntent;
@Override
public void onPageFinished(WebView webView, String url) {
@@ -137,8 +139,21 @@ public void onPageStarted(WebView webView, String url, Bitmap favicon) {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http://") || url.startsWith("https://") ||
url.startsWith("file://") || url.equals("about:blank")) {
boolean useDefaultIntent = false;
if (mUrlPrefixesForDefaultIntent != null && mUrlPrefixesForDefaultIntent.size() > 0) {
ArrayList<Object> urlPrefixesForDefaultIntent =
mUrlPrefixesForDefaultIntent.toArrayList();
for (Object urlPrefix : urlPrefixesForDefaultIntent) {
if (url.startsWith((String) urlPrefix)) {
useDefaultIntent = true;
break;
}
}
}
if (!useDefaultIntent &&
(url.startsWith("http://") || url.startsWith("https://") ||
url.startsWith("file://") || url.equals("about:blank"))) {
return false;
} else {
try {
@@ -205,6 +220,10 @@ protected WritableMap createWebViewEvent(WebView webView, String url) {
event.putBoolean("canGoForward", webView.canGoForward());
return event;
}
public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
mUrlPrefixesForDefaultIntent = specialUrls;
}
}
/**
@@ -214,6 +233,7 @@ protected WritableMap createWebViewEvent(WebView webView, String url) {
protected static class ReactWebView extends WebView implements LifecycleEventListener {
protected @Nullable String injectedJS;
protected boolean messagingEnabled = false;
protected @Nullable ReactWebViewClient mReactWebViewClient;
protected class ReactWebViewBridge {
ReactWebView mContext;
@@ -254,6 +274,16 @@ public void onHostDestroy() {
cleanupCallbacksAndDestroy();
}
@Override
public void setWebViewClient(WebViewClient client) {
super.setWebViewClient(client);
mReactWebViewClient = (ReactWebViewClient)client;
}
public @Nullable ReactWebViewClient getReactWebViewClient() {
return mReactWebViewClient;
}
public void setInjectedJavaScript(@Nullable String js) {
injectedJS = js;
}
@@ -413,7 +443,7 @@ public void setMediaPlaybackRequiresUserAction(WebView view, boolean requires) {
public void setAllowUniversalAccessFromFileURLs(WebView view, boolean allow) {
view.getSettings().setAllowUniversalAccessFromFileURLs(allow);
}
@ReactProp(name = "saveFormDataDisabled")
public void setSaveFormDataDisabled(WebView view, boolean disable) {
view.getSettings().setSaveFormData(!disable);
@@ -507,7 +537,17 @@ public void setMixedContentMode(WebView view, @Nullable String mixedContentMode)
view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
} else if ("compatibility".equals(mixedContentMode)) {
view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}
}
}
}
@ReactProp(name = "urlPrefixesForDefaultIntent")
public void setUrlPrefixesForDefaultIntent(
WebView view,
@Nullable ReadableArray urlPrefixesForDefaultIntent) {
ReactWebViewClient client = ((ReactWebView) view).getReactWebViewClient();
if (client != null && urlPrefixesForDefaultIntent != null) {
client.setUrlPrefixesForDefaultIntent(urlPrefixesForDefaultIntent);
}
}

0 comments on commit 40a2885

Please sign in to comment.