Chrome custom tabs and headers, a happy open-source story

When Chrome custom tabs got released last year I got really excited. The WebView widget is one of the most unstable component on Android and clearly painful when building apps. Chrome custom tabs bring a lot of stability to this messy world with pages loading twice as fast as in a WebView and cool features like password, cookies, payment pre-fills and so on. But this post is not about how awesome Chrome custom tabs are or how to integrate it in your app, there are already many good articles around.

I want to talk about the one thing Chrome custom tabs are missing : header injection. In an Android app that has a WebView, it is pretty common to inject headers in order to keep the session or to do other fancy stuff. Using a WebView, it’s very simple. You just need to attach a custom WebViewClient to your WebView and implement it this way:

class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url, headers);
return true;
}
}

I was quite disappointed when I read that Chrome custom tabs don’t have a similar way of injecting headers.

But yesterday, while I was efficiently procrastinating, I found this answer on StackOverflow (yes, the only one vote is mine). I found the link to the Chromium source and found this line. If you’re too lazy to click on the link, here is the interesting bit: IntentHandler.getExtraHeadersFromIntent(getIntent()). So there is a way to pass extra headers through the initial intent. Reading the source of the method indicates that we can pass headers via the extra Browser.EXTRA_HEADERS as a Bundle. Easy Peasy!

So I gave it a go:

CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder().build();
Bundle headers = new Bundle();
headers.putString("header key", "Charmander");
headers.putString("second header key", "Pikachu");
customTabsIntent.intent.putExtra(Browser.EXTRA_HEADERS, headers);
CustomTabActivityHelper.openCustomTab(activity, customTabsIntent, Uri.parse("http://www.pokewiki.de/"), new CustomTabActivityHelper.CustomTabFallback() {
@Override
public void openUri(Activity activity, Uri uri) {
Intent intent = new Intent(activity, WebViewActivity.class);
intent.putExtra(EXTRA_URL, uri.toString());
activity.startActivity(intent);
}
});

After running the app, I launched Chrome remote debugging (pretty awesome tool by the way) and found my lovely headers next to this mobile first website:

Now the problem is, when I open another tab from this page, I lose my headers. While investigating, I had a feeling that this class could be helpful. onNavigationEvent() gets called whenever a navigation or a tab event happens. So if everything is as expected, I just have to pass my headers in extras and the pokemons will flow!

After playing with it, I found out that unfortunately the parameter extras is always null. And in fact, “@param extras Reserved for future use.”. Chromium always passes null to onNavigationEvent().

Conclusion

I can’t use Chrome custom tabs with extra headers yet but that experience was a good reminder that reading the sources is a real advantage. Use it whenever you have a doubt, sometimes the documentation doesn’t say everything.

If you know more about that specific issue, please let me know!