I have a html string containing seveal img tags which I am passing to webview's loadDataWithBaseURL method like
String data = "some html with <img> and <link>.....";
wview.loadDataWithBaseURL("http://dummy.baseurl/", data, "text/html", "UTF-8", null);
if I dont pass the first parameter html can be displayed but subsequent requests for or css files are not triggered thats why I am passing a dummy baseUrl.
Running the code when I try to look what requests were made under shouldInterceptRequest() like below
wview.setWebViewClient(new WebViewClient() {
#Override
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
Log.d("url="+url, "resources");
....
}
});
the I can see outputs like
http://dummy.host.name/images/face.jpg
etc
but my original html contains ".." in img src like <img src="../images/face.jpg"> trouble is parent directory (..) part is ignored by webview
this ".." is important for me I cannot figure out why it is skipping that part
EDIT
I am loading images from a zip file so inside shouldInterceptRequest() I can put necessary logic but first I should have correct src.
I also noticed that if path is appended to baseUrl, they are also ignored for example
http://dummy.baseUrl/one/two/
becomes (looking from request logs)
http://dummy.baseurl/
I suspect if these two are related !
Where do you want to load the images from? If you're trying to load them from the phone you need to use the assets folder.
https://stackoverflow.com/a/7268695/642161
I resolved my problem using a hack, not sure a better solution exist!
It looks like baseUrl and src are merged to create final request urls so if any ".." present in src it will be used to recalculate final url and they disappear no matter if top level directory exist.
I changed baseUrl to
file:///android_asset/x123_/x123_/x123_/x123_/
Here "x123_" is any random character sequence one can take but should be least likely to appear in "src"
Now count the mumber of "x123_" in request url inside shouldInterceptRequest() if it is 4 (as in this example) there were no ".." in src otherwise number of ".." = 4 - count
Related
I need to load some HTML that I construct at runtime into a WebView and apply a CSS file to it placed in the assets directory. And I already have a base URL that I need to provide to the webview's loadDataWithBaseURL function.
Code Snippet (not applying CSS file):
StringBuffer buff = new StringBuffer();
buff.append("<head>");
buff.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"file:///android_asset/my_css_file.css\"/>");
buff.append("</head>");
buff.append("<div class=\"announcement-header\">");
buff.append("HEADER");
buff.append("</div>");
buff.append("<div class=\"announcement-separator\"></div>");
buff.append("<div class=\"announcement\">");
buff.append("CONTENT");
buff.append("</div>")
WebView.loadDataWithBaseURL(MY_BASE_URL, buff.toString(), "text/html", HTTP.UTF_8, null);
I've looked at these 2 similar issues Issue 1 & Issue 2, but my case is slightly different as I cant give file:///android_asset/ as the base url to the loadDataWithBaseURL function.
Any ideas how I can apply the CSS file in this case ?
If you want to intercept some of the requests from WebView you can do so by overriding shouldInterceptRequest() in WebViewClient like this:
public WebResourceResponse shouldInterceptRequest (final WebView view, String url) {
if (URLUtil.isFileUrl(url) && url.contains(".css")) {
return getCssWebResourceResponseFromAsset();
} else {
return super.shouldInterceptRequest(view, url);
}
}
There is already an excellent detailed answer here https://stackoverflow.com/a/8274881/1112882
HOWEVER
You can not access local file if your base url is not local because of security reasons. So give relative path to your css and then intercept the request.
I would suggest using a pattern to differentiate b/w actual relative and custom relative paths. e.g. use android_asset/my_css_file.css instead of my_css_file.css and modify shouldInterceptRequest() accordingly to always intercept requests starting from android_asset.
I have an app that heavily uses the Android WebView to display my custom HTML content. The latest Android update (4.4/Kit-Kat/SDK-19) featured a redesigned WebView.
One of my users with a Nexus 5 reported a problem where some links cause the app to crash. I ran in the 4.4 emulator and debug into my WebViewClient's shouldOverrideUrlLoading() method. On all previously tested Android versions (2.2-4.3) the url String passed into the method had my custom url with "/" characters in it. In 4.4 the exact same link now has "\" characters in their place.
This doesn't make any sense to me. I load the HTML exactly the same, so somehow the new WebView converted all my slashes into backslashes.
Why does the new WebView do this?
Changes in URL handling are a known issue. Please see the migration guide for more detail.
The behaviour in this particular case will depend on what your base URL's scheme is, from what you're describing I'm guessing your base URL's scheme is "http(s)://" in which case the Chromium WebView performs URL normalization.
You might want to consider using the URI class to handle the discrepancy between the Classic and Chromium WebViews in this case.
I did more debugging and discovered I actually have the question reversed. Turns out the older versions of WebView did conversions of the URL, not the new one.
I load HTML with a format similar to this into a WebView:
link
I use the double back slashes as delimiters and parse the data later when the link is clicked. In older versions of WebView it converted my double backslash characters into forward slashes. It had been so long since I was in that code, I forgot I adjusted my code to use forward slashes rather than the backslashes in the original HTML.
The new version of WebView leaves my custom URL intact, giving me the exact same string as my original HTML. So turns out the old WebView is the problem not the new one.
The new WebView applies additional restrictions when requesting resources and resolving links that use a custom URL scheme. For example, if you implement callbacks such as shouldOverrideUrlLoading() or shouldInterceptRequest(), then WebView invokes them only for valid URLs.
If you are using a custom URL scheme or a base URL and notice that your app is receiving fewer calls to these callbacks or failing to load resources on Android 4.4, ensure that the requests specify valid URLs that conform to RFC 3986.
For example, the new WebView may not call your shouldOverrideUrlLoading() method for links like this:
Show Profile
The result of the user clicking such a link can vary:
If you loaded the page by calling loadData() or loadDataWithBaseURL() with an invalid or null base URL, then you will not receive the shouldOverrideUrlLoading() callback for this type of link on the page.
Note: When you use loadDataWithBaseURL() and the base URL is invalid or set null, all links in the content you are loading must be absolute.
If you loaded the page by calling loadUrl() or provided a valid base URL with loadDataWithBaseURL(), then you will receive the shouldOverrideUrlLoading() callback for this type of link on the page, but the URL you receive will be absolute, relative to the current page. For example, the URL you receive will be "http://www.example.com/showProfile" instead of just "showProfile".
Instead of using a simple string in a link as shown above, you can use a custom scheme such as the following:
Show Profile
You can then handle this URL in your shouldOverrideUrlLoading() method like this:
// The URL scheme should be non-hierarchical (no trailing slashes)
private static final String APP_SCHEME = "example-app:";
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith(APP_SCHEME)) {
urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
respondToData(urlData);
return true;
}
return false;
}
If you can't alter the HTML then you may be able to use loadDataWithBaseURL() and set a base URL consisting of a custom scheme and a valid host, such as "example-app:///". For example:
webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA,
null, "UTF-8", null);
The valid host name should conform to RFC 3986 and it's important to include the trailing slash at the end, otherwise, any requests from the loaded page may be dropped.
to avoid webview below 4.4 convert backslash to forward slash, I just escape my url, then in Java code, use URI.decode to get the real url.That works for me.
I have an html page that's built dynamically. Some of the images come from an internet url and others i want to load from the Assets folder. The pages correctly renders the internet images, however, it just refuses to load local images. I'm tried many variations including the following and none will load the local images. If everything is loaded from the internet url, the entire pages renders correctly but I really don't want to go to the internet for static content that won't change. Any ideas?
String pageData="a bunch of html markup...";
//-- i had to remove the < from the img tag below to get by the spam filter on this forum-->
//--try 1-->
pageData += "img src='file:///data/data/(my package)/files/image1.png' alt='my image'>";
//--try 2-->
pageData += "img src='/data/data/(my package)/image1.png' alt='my image'>";
//--try 3-->
pageData += "img src=' src='data:image/jpeg;base64,'" + readAssetFileAsBase64("image1.png") + "' alt='my image'>";
//--try 4-->
explosives!
The page is rendered using..
webview.loadData(pageData,"text/html",null);
but I've also tried
webview.loadDataWithBaseURL("file://", s, "text/html","windows-1252" ,null);
I can't use .loadUrl() since the page is built dynamically. I suppose I could write the dynamic page out to disk the call .loadUrl() but that doesn't seem so efficient.
You need to make sure you load the html with a base url, although that base url doesn't need to be anything pertinent
webView.loadDataWithBaseURL("fake://not/needed", htmlString, mimeType, encoding, "");
and then you can reference things in the assets folder using something like this
file:///android_asset/image1.jpg
the "android_asset" path seems to be the thing you haven't tried yet. If your files are not in your asset folder then you need to serve them with a ContentProvider, see this tutorial from my site: http://responsiveandroid.com/2012/02/20/serving-android-webview-resources-with-content-providers.html
I would like to know if it is possible to load a local HTML file into a WebView loading everything but the body innerHTML. That is, the resulting DOM will have head, scripts and CSS's but the body will be empty. I tried emptying the body just after loadUrl call but the WebViews goes on loading the body. I want the body is not loaded at all so to speed up loading, that is, no rendering has to be done by the WebView.
It's a bit of a hack by why not try this:
String html = loadHtmlFromFile();
String newHtml = html.replaceFirst("<body>.*<\body>", "<body><\body>");
This will replace the body with just the body tags.
Then either save newHtml as an html file and open that or, if possible, pass the HTML directly to the WebView (although I'm not sure you can do that)
You'll also have to write the loadHtmlFromFile() method to get your HTML for you.
Please help me solve this file path conflict:
As you know, many HTML pages use relative paths starting with a "/" for the href attribute of link tags. For example: link href="/links/style.css".
In my code, I'm using loadDataWithBaseURL for a WebView to set a relative path name. If I give like this:
String webContent=//whole html page;
mWebView.loadDataWithBaseURL("file:///sdcard/mibook/pg90/",new String(webContent), "text/html", "UTF-8","" );
Result: No effect in WebView because (I felt) it takes two '/' while appending to pathname.
If I edit my HTML page by removing the first "/" from the href tag, then the WebView renders properly.
But my problem is that I don't want to edit HTML content as above. Any solution?
Javadoc of loadDataWithBaseURL states:
Note for post 1.0. Due to the change in the WebKit, the access to asset files through
"file:///android_asset/" for the sub resources is more restricted. If you provide null
or empty string as baseUrl, you won't be able to access asset files. If the baseUrl is
anything other than http(s)/ftp(s)/about/javascript as scheme, you can access asset
files for sub resources
So basically only allowed URL for file:/// scheme is file:///android_asset/ where files are in your asset folder.