Android Web-View:Inject Local JavaScript File to Remote Webpage

Android Web-View : Inject local Javascript file to Remote Webpage

There is a way to 'force' the injection of your local Javascript files from local assets (e.g., assets/js/script.js), and to circumvent the 'Not allowed to load local resource : file:///android_assets/js/script.js ...' issue.

It is similar to what described in another thread (Android webview, loading javascript file in assets folder), with additional BASE64 encoding/decoding for representing your Javascript file as a printable string.

I am using an Android 4.4.2, API level 19 Virtual Device.

Here are some code snippets:

[assets/js/script.js]:

    'use strict';

function test() {
// ... do something
}

// more Javascript

[MainActivity.java]:

    ...

WebView myWebView = (WebView) findViewById(R.id.webView);
WebSettings webSettings = myWebView.getSettings();

webSettings.setJavaScriptEnabled(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
myWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}

@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);

injectScriptFile(view, "js/script.js"); // see below ...

// test if the script was loaded
view.loadUrl("javascript:setTimeout(test(), 500)");
}

private void injectScriptFile(WebView view, String scriptFile) {
InputStream input;
try {
input = getAssets().open(scriptFile);
byte[] buffer = new byte[input.available()];
input.read(buffer);
input.close();

// String-ify the script byte-array using BASE64 encoding !!!
String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
view.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
// Tell the browser to BASE64-decode the string into your script !!!
"script.innerHTML = window.atob('" + encoded + "');" +
"parent.appendChild(script)" +
"})()");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});

myWebView.loadUrl("http://www.example.com");

...

Inject javascript file to my site with webview in android

Add a new method to inject javascript file.

 private void injectJS() {
try {
InputStream inputStream = getAssets().open("jscript.js");
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
inputStream.close();
String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
webView.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
"script.innerHTML = window.atob('" + encoded + "');" +
"parent.appendChild(script)" +
"})()");
} catch (Exception e) {
e.printStackTrace();
}
}

Call both methods: injectCSS() and injectJS() after page finishes loading.

webView.setWebViewClient(new WebViewClient() {

@Override
public void onPageFinished(WebView view, String url) {
injectCSS();
injectJS();
super.onPageFinished(view, url);
}
});

I hope this solves the problem.

Be wary of how onload events defined inside inject js file would behave.

Inject external JS file in Android WebView and call it

Why not just read in the file and execute it directly via loadUrl("javascript:...)?

How to load a Javascript in WebView

You can do it like this :

private static class WC extends WebViewClient {
Handler handler1 = null;
WebView webView = null;
byte[] b = null;

public WC(Handler handler, WebView webView) {
this.handler1 = handler;
this.webView = webView;
}

@Override
public void onPageFinished(final WebView view, String url) {
super.onPageFinished(view, url);
new Thread(new Runnable() {
@Override
public void run() {
try {
injectScriptFromNetWork("http://192.168.216.254:4000/test.js");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();

}

private void injectScriptFromNetWork(String urlStr) throws IOException {

URL url = new URL(urlStr);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
int code = urlConnection.getResponseCode();
if (code == 200) {
InputStream inputStream = urlConnection.getInputStream();
b = new byte[inputStream.available()];
inputStream.read(b);
}

handler1.post(new Runnable() {
@Override
public void run() {
String jsSource = Base64.encodeToString(b, Base64.NO_WRAP);
webView.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
"script.innerHTML = window.atob('" + jsSource + "');" +
"parent.appendChild(script)" +
"})()");
}
});
}
}

MainActivity.java code like this:

handler = new Handler(getMainLooper());
webView.setWebViewClient(new WC(handler, webView));
webView.loadUrl("file:///android_asset/index.html");

Forgave me, this is my first time answering questions on stackoverflow.

Android webview, loading javascript file in assets folder

I tried the same thing, loading a bookmarklet (the javascript code in your loadUrl() call) into a third-party page. My bookmarklet also depends on other assets (javascript and css files) which would not load with a file:///android_asset URL.

That's because the security context of the page is still that of, e.g., http://www.google.com, and that's not allowed access to file: URLs. You should be able to see the errors if you supply/override a WebChromeClient.onConsoleMessage().

I ended up with a kludge where I changed the bookmarklet's asset references to a bogus URL scheme, like:

asset:foo/bar/baz.js

and added a WebViewClient.shouldInterceptRequest() override which looks for those and loads them from assets using AssetManager.open().

One thing I don't like about this kludge is that the asset: scheme is open to any third-party HTML/Javascript on any page my view loads, giving them access to my app's assets.

One alternative, which I didn't try, would be to embed the sub-assets in the bookmarklet using data: URLs, but that can get unwieldy.

I'd much prefer it if there was a way to manipulate the security context of just the JS bookmarklet I'm loading in loadUrl(), but I can't find anything like that.

Here's a snippet:

import android.webkit.WebResourceResponse;
...
private final class FooViewClient extends WebViewClient
{
private final String bookmarklet;
private final String scheme;

private FooViewClient(String bookmarklet, String scheme)
{
this.bookmarklet = bookmarklet;
this.scheme = scheme;
}

@Override
public void onPageFinished(WebView view, String url)
{
view.loadUrl(bookmarklet);
}

@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url)
{
if (url.startsWith(scheme))
try
{
return new WebResourceResponse(url.endsWith("js") ? "text/javascript" : "text/css", "utf-8",
Foo.this.getAssets().open(url.substring(scheme.length())));
}
catch (IOException e)
{
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}

return null;
}
}


Related Topics



Leave a reply



Submit