Skip to content

Commit

Permalink
android: allow whitelisting urls to bypass default webview loading
Browse files Browse the repository at this point in the history
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 40a2885
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 4 deletions.
10 changes: 10 additions & 0 deletions Libraries/Components/WebView/WebView.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -110,6 +111,7 @@ public class ReactWebViewManager extends SimpleViewManager<WebView> {
protected static class ReactWebViewClient extends WebViewClient {

protected boolean mLastLoadFailed = false;
protected @Nullable ReadableArray mUrlPrefixesForDefaultIntent;

@Override
public void onPageFinished(WebView webView, String url) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
}

/**
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand Down

0 comments on commit 40a2885

Please sign in to comment.