PSA: WebView URL-Handling Regression

About 40 minutes ago, a developer
posted a question on Stack Overflow
that made my heart sink:




I noticed that with the last update of Google System WebView, all the links in my WebViews are opened in the view itself.




While we generally applauded the move to decouple WebView from firmware
updates, so security fixes can get rolled out quicker, that switch
has its downsides. One is that bugs or other regressions in WebView
behavior roll out to a lot of people before we as developers necessarily
have time to adjust.



In this case, WebView now behaves as noted above:





All links and redirected-to URLs open in the WebView by default




If you return false from a shouldOverrideUrlLoading() in your
WebViewClient, the URL will be loaded into the WebView





Before, both scenarios would trigger an ACTION_VIEW
Intent on the URL, typically bringing up a Web browser.



This runs counter to what is documented for shouldOverrideUrlLoading()
and past seven years��� of WebView behavior.



If your app is using a WebViewClient to keep all links within the WebView
anyway, you should see no net change in behavior.



However, if your app relies on WebView opening up some URLs in a browser on its own,
you have a problem.



Right now, though, we do not know if this is an accidental regression or
a permanent change in behavior. I filed a bug report on it,
but I have little hope that it will ever get addressed. I have had less
luck with bug reports on the Chromium project than I have on the Android
project, and that���s saying a lot.



Hence, my recommendation at the moment is:





Always attach a WebViewClient to your WebView




Always implement shouldOverrideUrlLoading() on the WebViewClient




Always return true to indicate that you are handling the event




Always do what your app needs to have done, whether that is loading
the URL into the WebView or launching a browser on the URL (rather
than returning false and relying on stock behavior)





Something like this static inner class appears to do the trick ���
create an instance and pass it to setWebViewClient() on your
WebView:



private static class URLHandler extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (shouldKeepInWebView(url)) {
view.loadUrl(url);
}
else {
Intent i=new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

view.getContext().startActivity(i);
}

return(true);
}

private boolean shouldKeepInWebView(String url) {
return(true); // or false, or use regex, or whatever
}
}


(where you would put your business logic in shouldKeepInWebView() to
determine whether or not a given URL should stay in the WebView
or launch a browser)



Unfortunately, since I do not see a getWebViewClient()
counterpart to setWebViewClient(), we cannot use a chaining pattern
here easily. If you already have a WebViewClient for other
reasons, you would want to blend in something akin to the code
shown above.



With luck, we will get more clarity on the situation in the coming days
(weeks? months?).

 •  0 comments  •  flag
Share on Twitter
Published on June 11, 2015 10:05
No comments have been added yet.