Skip to content
This repository has been archived by the owner on Jan 12, 2023. It is now read-only.

WebView sends X-requested-with header #1196

Closed
pocmo opened this issue Aug 23, 2017 · 16 comments
Closed

WebView sends X-requested-with header #1196

pocmo opened this issue Aug 23, 2017 · 16 comments

Comments

@pocmo
Copy link
Contributor

pocmo commented Aug 23, 2017

WebView seems to send an HTTP header with the application id:

X-requested-with: org.mozilla.focus

For Klar:

X-requested-with: org.mozilla.klar

Or Dev/Beta builds:

X-requested-with: org.mozilla.focus.beta
X-requested-with: org.mozilla.focus.debug

I wonder if this is problematic. We already use "Focus"/"Klar" in the user agent. But this additionally shows the build type.

@bbinto
Copy link
Contributor

bbinto commented Aug 28, 2017

Could be replaced with an empty string

@mcomella
Copy link
Contributor

mcomella commented Nov 6, 2017

Could be replaced with an empty string

SO agrees: https://stackoverflow.com/a/29395509/2219998

For this bug, please first investigate if it's possible to remove the header entirely (e.g. can we pass in null?), then fallback on sending an empty String.

@bbinto bbinto added the P4 label Nov 10, 2017
@bbinto bbinto modified the milestone: Reserve Backlog Nov 10, 2017
@JordanShaak
Copy link
Contributor

JordanShaak commented Nov 10, 2017

I've been looking into this for most of today, so I'm going to write down some of my findings for anyone who might look at this later. I've personally come to the conclusion that you can't use Android's WebView system as is and remove the X-Requested-With header from all outbound HTTP headers. At least this is what my research leads me to believe, and also because I've tried a few such implementations that should achieve this but they don't actually squelch that header.

You can get rid of it at SystemWebView.loadUrl() (quite easily I might add), but it doesn't matter because if the page loads any other assets like a stylesheet you still have the X-Requested-With header present in that request (and every other request except the initial one). Trying to manually add the header to the WebSourceRequest in TrackingProtectionWebViewClient.shouldInterceptRequest() doesn't stop the header from being present with the package name.

My research into some other Android browsers that would also likely want to remove this header currently don't successfully do it either. Some have had this same issue open for ~18 months.

Stopping "android.webkit.WebStorage" from getting the correct package name causes a complete app crash, though I haven't been able to discern if stopping "android.webkit.WebViewFactory" from getting the correct package name actually impacts anything. But even if it does/doesn't it still doesn't stop the X-Requested-With header from showing up.

So that might remove the option of just not telling the webkit what the package name is so it can't use it. I think there might be a way to do it with HttpURLConnection but I'm not sure about that. In theory it means you'd need to be able to respond to every single type of content you might receive if I'm understanding it correctly. Would that even be a viable solution if it were the case?

@pocmo
Copy link
Contributor Author

pocmo commented Nov 13, 2017

Thank you @JordanShaak for your thorough research. That's great!

I've personally come to the conclusion that you can't use Android's WebView system as is and remove the X-Requested-With header from all outbound HTTP headers.

Back when I filed this issue I tried a bunch of things too and couldn't find a way. Did you look into the Chromium issue tracker whether there's an issue filed for that? If not then we should file it.

You can get rid of it at SystemWebView.loadUrl() (quite easily I might add), but it doesn't matter because if the page loads any other assets like a stylesheet you still have the X-Requested-With header present in that request

We have the same issue with the DNT header (#446). We can add it to the first request but not to all of them. However I think it would be worth removing X-Requested-With from the initial request - even though we cannot remove it from all yet. Again I wonder if this is tracked in the Chromium bug tracker. If not we should file an issue for this one too.

Stopping "android.webkit.WebStorage" from getting the correct package name causes a complete app crash, though I haven't been able to discern if stopping "android.webkit.WebViewFactory" from getting the correct package name actually impacts anything.

That's interesting. Haven't tried this one. Too bad it doesn't work.

I think there might be a way to do it with HttpURLConnection but I'm not sure about that. In theory it means you'd need to be able to respond to every single type of content you might receive if I'm understanding it correctly. Would that even be a viable solution if it were the case?

Does this mean intercepting all requests and performing them manually with HttpURLConnection? So far I do not want to go this route. The risk of introducing a lot of bugs is just too high. Doing all this manually is definitely not trivial and I expect a bunch of edge cases.

@JordanShaak
Copy link
Contributor

Did you look into the Chromium issue tracker whether there's an issue filed for that?

I took a look recently and didn't find anything, but it's very possible I missed it/am not using the correct search terms in the issue tracker. Since this isn't the first time a project has had this issue I feel as though someone must have submitted the issue before, but I can't find any proof of it.

I think it would be worth removing X-Requested-With from the initial request - even though we cannot remove it from all yet.

If I can manage to get my local version cleaned up and not mangle the pull request for that I'll submit one later.

Does this mean intercepting all requests and performing them manually with HttpURLConnection?

Pretty much exactly what I was trying to say, and...

The risk of introducing a lot of bugs is just too high. Doing all this manually is definitely not trivial and I expect a bunch of edge cases.

... the same conclusion my research was leading me to.

@pocmo
Copy link
Contributor Author

pocmo commented Nov 17, 2017

Merged the patch that clears the "X-Requested-With" header for the initial request. That's everything we can do here.

@pocmo pocmo closed this as completed Nov 17, 2017
@pocmo
Copy link
Contributor Author

pocmo commented Nov 17, 2017

Re-opening again: We could check the chrome issue tracker and file issues if needed.

@3c
Copy link

3c commented Apr 4, 2019

the value of X-requested-with is from getAll() method in org.chromium.base.BuildInfo

getAll()[8] is the key

String hostPackageName = ContextUtils.getApplicationContext().getPackageName();

so if we can change the value , X-requested-with is resolved

@geekmario
Copy link

geekmario commented May 28, 2019

the value of X-requested-with is from getAll() method in org.chromium.base.BuildInfo

getAll()[8] is the key

String hostPackageName = ContextUtils.getApplicationContext().getPackageName();

so if we can change the value , X-requested-with is resolved

// xxApplication.java

   @Override
    public String getPackageName() {
        try {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            for (StackTraceElement element : stackTrace) {
                if ("org.chromium.base.BuildInfo".equalsIgnoreCase(element.getClassName())) {
                    if ("getAll".equalsIgnoreCase(element.getMethodName())) {
                        String customPackageName = "com.tencent.qq";
                        return customPackageName;
                    }
                    break;
                }
            }
        } catch (Exception e) { }

        return super.getPackageName();
    }

@pocmo
Copy link
Contributor Author

pocmo commented May 28, 2019

Overriding the header is possible and we already do that:

additionalHeaders["X-Requested-With"] = ""

However getting completely rid of it is not possible.

@melihakalan
Copy link

This is my hacked webview example that not send x-requested-with

import android.app.*;
import android.content.*;
import android.content.pm.*;
import android.graphics.*;
import android.os.*;
import android.util.*;
import android.webkit.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import android.content.pm.PackageManager.*;

public class HackedWebView extends WebView {
	
	private static String mAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
	mTypes = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
	mLangs = "tr-TR,en-US;q=0.8";
	private static List<String[]> mParams = null;
	private static List<String> history = new ArrayList<String>();
	
	public HackedWebView(Context c){
		super(c);
		setWebViewClient(new WebClient());
		setWebChromeClient(new ChromeClient());
		WebSettings s = getSettings();
		s.setUserAgentString(mAgent);
		accessToMethod(s,boolean.class,"setJavaScriptEnabled",true);
		Class z = getFromName("android.webkit.WebSettings$ZoomDensity");
		if(z != null) accessToMethod(s,z,"setDefaultZoom",getDeclaredField(z,"FAR"));
		accessToMethod(s,boolean.class,"setBuiltInZoomControls",true);
		accessToMethod(s,boolean.class,"setSupportZoom",true);
		accessToMethod(s,boolean.class,"setDisplayZoomControls",false);
		z = getFromName("android.webkit.WebSettings$RenderPriority");
		if(z != null) accessToMethod(s,z,"setRenderPriority",getDeclaredField(z,"HIGH"));
		accessToMethod(s,boolean.class,"setAllowFileAccess",true);
		accessToMethod(s,boolean.class,"setDomStorageEnabled",true);
		accessToMethod(s,boolean.class,"setDatabaseEnabled",true);
		accessToMethod(s,String.class,"setDatabasePath",c.getCacheDir().toString().replace("cache","databases"));
		accessToMethod(s,long.class,"setAppCacheMaxSize",1024*1024*8);
		accessToMethod(s,String.class,"setAppCachePath",c.getCacheDir());
		accessToMethod(s,boolean.class,"setAppCacheEnabled",true);
		accessToMethod(s,boolean.class,"setUseWideViewPort",true);
	}
	
	public void setUserAgentString(String userAgent){
		mAgent = userAgent;
		getSettings().setUserAgentString(userAgent);
	}
	
	public void setAcceptTypes(String types){
		mTypes = types;
	}
	
	public void setAcceptLangs(String langs){
		mLangs = langs;
	}
	
	public void setCustomRequestParameters(List<String[]> params){
		mParams = params;
	}
	
	@Override
	public void loadUrl(String s){
		try {
			new WebkitHelper().execute(s);
		} catch (Exception | Error e){}
	}

	@Override
	public void loadUrl(String url, Map<String, String> additionalHttpHeaders){
		loadUrl(url);
	}

	private void loadData(WebViewHolder h){
		if(h != null){
			stopLoading();
			h.p = createNavigatorInjector() + h.p;
			loadDataWithBaseURL(h.b,h.p,h.t,h.e,history.size() > 0 ? history.get(history.size() - 1) : "");
			history.add(h.f);
		}
	}

	private class WebkitHelper extends AsyncTask<String,String,WebViewHolder>{

		@Override
		protected void onPreExecute(){
			super.onPreExecute();
			((Activity)getContext()).setTitle("Buffering...");
		}
		
		@Override
		protected WebViewHolder doInBackground(String[] p1){
			try {
				if(check(p1[0],true)){
					if(p1[0].startsWith("error<>")){
						String[] x = p1[0].split("<>");
						return getWebError(x[1],x[2]);
					} else {
						while(true){
							WebViewHolder wh = setConnection(p1[0]);
							if(wh != null){
								return wh;
							} else if(rc >= mr){
								rc = 0;
								return getWebError(p1[0],"net::ERR_TOO_MANY_REDIRECTS");
							}
						}
					}
					//return getWebError(p1[0],"net::EER_GOOGLE_SHIT");
				}
				
			} catch(Exception | Error e){}
			return null;
		}

		@Override
		public void onPostExecute(WebViewHolder result){
			loadData(result);
		}
		
		int rc = 0,mr = 30;
		
		private WebViewHolder getWebError(String url, String err){
			WebViewHolder h = new WebViewHolder();
			h.b = "about:blank";
			h.t = "text/html";
			h.e = "UTF-8";
			/*String s = "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\"/>";
			s += "<title>Webpage not available</title>\n<style type=\"text/css\">";
			s += "body { margin-top: 0px; padding-top: 0px; }\nh2 { margin-top: 5px; padding-top: 0px; }</style>\n<body>\n";
            s += "<img src=\"file:///android_asset/webkit/android-weberror.png\" align=\"top\" />";
            s += "<h2>Webpage not available</h2>";
            s += "<p>The webpage at <a href=\"%s\">%s</a> could not be loaded because:</p>".replaceAll("%s",url);
            s += "<p>%e</p></body></head></html>".replaceAll("%e",err);*/
			String s = "<html><head><style>";
			s += "html { background-color: lightgrey; padding: 16px; } body { padding: 24px; border-radius: 16px; box-shadow: 0px 0px 16px red; background-color: white; }";
			s += " h3, h5 { word-wrap: break-word; text-align: center; font-family: \"Lucida Console\", Monaco, monospace; }";
			s += " h5 { font-weight: 300; } ";
			s += " img { width: 100%; height: 15%; } </style>";
			s += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\"/>";
			s += "<title>%t</title></head><body>".replace("%t","Hoop!");
			s += "<img src=\"data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3J";
			s += "nLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgaGVpZ2h0PSI1MTJweCIgdmlld0JveD0iMCAw";
			s += "IDQ5NiA0OTYiIHdpZHRoPSI1MTJweCI+PGxpbmVhckdyYWRpZW50IGlkPSJhIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9I";
			s += "jI0OCIgeDI9IjI0OCIgeTE9IjQ5NiIgeTI9IjAiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzlmMmZmZiIvPjxzdG9wIG9mZnNldD";
			s += "0iMSIgc3RvcC1jb2xvcj0iIzBiYjFkMyIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZD0ibTI0OCAyNTZjMTMuMjMwNDY5IDAgMjQtMTAuNzY";
			s += "5NTMxIDI0LTI0cy0xMC43Njk1MzEtMjQtMjQtMjQtMjQgMTAuNzY5NTMxLTI0IDI0IDEwLjc2OTUzMSAyNCAyNCAyNHptMC0zMmM0LjQwNjI1";
			s += "IDAgOCAzLjU4NTkzOCA4IDhzLTMuNTkzNzUgOC04IDgtOC0zLjU4NTkzOC04LTggMy41OTM3NS04IDgtOHptLTE2LjU1MDc4MS00Ny40MDYyN";
			s += "WMuNjEzMjgxIDguNjM2NzE5IDcuODg2NzE5IDE1LjQwNjI1IDE2LjU1MDc4MSAxNS40MDYyNXMxNS45Mzc1LTYuNzY5NTMxIDE2LjU1MDc4MS";
			s += "0xNS40MDYyNWw3LjM4NjcxOS0xMDMuNDU3MDMxYy4wNDY4NzUtLjU2MjUuMDYyNS0xLjEyODkwNy4wNjI1LTEuNjg3NSAwLTEyLjkzNzUtMTA";
			s += "uNTE5NTMxLTIzLjQ0OTIxOS0yMy40NDkyMTktMjMuNDQ5MjE5aC0xLjEwMTU2MmMtMTIuOTI5Njg4IDAtMjMuNDQ5MjE5IDEwLjUxMTcxOS0y";
			s += "My40NDkyMTkgMjMuNDQ5MjE5IDAgLjU1ODU5My4wMTU2MjUgMS4xMzY3MTkuMDU0Njg4IDEuNjcxODc1em0xNi0xMTIuNTkzNzVoMS4xMDE1N";
			s += "jJjNC4xMTMyODEgMCA3LjQ0OTIxOSAzLjM0Mzc1IDcuNDMzNTk0IDcuOTc2NTYybC03LjM5MDYyNSAxMDMuNDgwNDY5Yy0uMDQyOTY5LjYwNT";
			s += "Q2OS0xLjE0NDUzMS42MDU0NjktMS4xNzk2ODggMGwtNy40MTQwNjItMTA0LjAwNzgxMmMwLTQuMTA1NDY5IDMuMzM1OTM4LTcuNDQ5MjE5IDc";
			s += "uNDQ5MjE5LTcuNDQ5MjE5em0yMjQuNTUwNzgxIDE1MmgtODcuMDU0Njg4bC05NS4wNTg1OTMtMTkwLjExMzI4MWMtNy45ODQzNzUtMTUuOTY0";
			s += "ODQ0LTI0LjAzOTA2My0yNS44ODY3MTktNDEuODg2NzE5LTI1Ljg4NjcxOXMtMzMuOTAyMzQ0IDkuOTIxODc1LTQxLjg4NjcxOSAyNS44ODY3M";
			s += "TlsLTk1LjA1ODU5MyAxOTAuMTEzMjgxaC04Ny4wNTQ2ODhjLTEzLjIzMDQ2OSAwLTI0IDEwLjc2OTUzMS0yNCAyNHYxMTJjMCAxMy4yMzA0Nj";
			s += "kgMTAuNzY5NTMxIDI0IDI0IDI0aDI0LjUxOTUzMWw2LjkzNzUgMTA0aC0yMy40NTcwMzF2MTZoNDMydi0xNmgtMjMuNDQ5MjE5bDYuOTM3NS0";
			s += "xMDRoMjQuNTExNzE5YzEzLjIzMDQ2OSAwIDI0LTEwLjc2OTUzMSAyNC0yNHYtMTEyYzAtMTMuMjMwNDY5LTEwLjc2OTUzMS0yNC0yNC0yNHpt";
			s += "OCAyNHY0MC4zOTg0MzhsLTQ2LjA5NzY1Ni00OC4zOTg0MzhoMzguMDk3NjU2YzQuNDA2MjUgMCA4IDMuNTg1OTM4IDggOHptLTI1OS41NzQyM";
			s += "TktMjA2Ljk1MzEyNWM1LjI1MzkwNy0xMC41MTk1MzEgMTUuODIwMzEzLTE3LjA0Njg3NSAyNy41NzQyMTktMTcuMDQ2ODc1czIyLjMyMDMxMi";
			s += "A2LjUyNzM0NCAyNy41NzQyMTkgMTcuMDQ2ODc1bDk3LjE2Nzk2OSAxOTQuMzI4MTI1YzIuMTI4OTA2IDQuMjU3ODEyIDMuMjU3ODEyIDkuMDM";
			s += "xMjUgMy4yNTc4MTIgMTMuNzkyOTY5IDAgMTctMTMuODMyMDMxIDMwLjgzMjAzMS0zMC44MzIwMzEgMzAuODMyMDMxaC0xOTQuMzM1OTM4Yy0x";
			s += "NyAwLTMwLjgzMjAzMS0xMy44MzIwMzEtMzAuODMyMDMxLTMwLjgzMjAzMSAwLTQuNzYxNzE5IDEuMTI4OTA2LTkuNTI3MzQ0IDMuMjU3ODEyL";
			s += "TEzLjc5Mjk2OXptMTQ2LjQyOTY4OCAzMjYuOTUzMTI1LTY4LjU3NDIxOS03Mmg0Ni44ODY3MTljNy40ODA0NjkgMCAxNC41MjczNDMtMS44MD";
			s += "g1OTQgMjAuODAwNzgxLTQuOTM3NWw3My4yNjk1MzEgNzYuOTM3NXptMS42NjQwNjIgMTYgNi45Mzc1IDEwNGgtMjU0LjkwNjI1bDYuOTM3NS0";
			s += "xMDR6bS0yMTcuNjg3NS04OGgzMC44ODY3MTlsNjguNTcwMzEyIDcyaC03Mi4zODY3MThsLTEyMS45MDIzNDQtMTI4aDQ4Ljk4NDM3NWMtLjYw";
			s += "OTM3NSAzLjAyMzQzOC0uOTg0Mzc1IDYuMDg5ODQ0LS45ODQzNzUgOS4xNjc5NjkgMCAyNS44MjQyMTkgMjEuMDA3ODEyIDQ2LjgzMjAzMSA0N";
			s += "i44MzIwMzEgNDYuODMyMDMxem0xMjUuMzU5Mzc1IDAgNjguNTc4MTI1IDcyaC03Mi4zODY3MTlsLTY4LjU2NjQwNi03MnptLTI1Mi4xOTE0MD";
			s += "YtNTZoOS45MDIzNDRsMTIxLjkwNjI1IDEyOGgtNzIuMzgyODEzbC02Ny40MjU3ODEtNzAuODAwNzgxdi00OS4xOTkyMTljMC00LjQxNDA2MiA";
			s += "zLjU5Mzc1LTggOC04em0tOCAxMjB2LTM5LjYwMTU2Mmw0NS4zMzU5MzggNDcuNjAxNTYyaC0zNy4zMzU5MzhjLTQuNDA2MjUgMC04LTMuNTg1";
			s += "OTM4LTgtOHptNDguNTUwNzgxIDI0aDQ2LjkwNjI1bC02LjkzNzUgMTA0aC0zMy4wMzkwNjJ6bTM1OS45Njg3NSAxMDRoLTMzLjAzMTI1bC02L";
			s += "jkzNzUtMTA0aDQ2LjkwNjI1em00Ny40ODA0NjktMTIwaC0xMC42NjQwNjJsLTgyLjM3NS04Ni40ODgyODFjOC4wNTQ2ODctOC40MTQwNjMgMT";
			s += "MuMDM5MDYyLTE5LjgwMDc4MSAxMy4wMzkwNjItMzIuMzQzNzUgMC0zLjA3ODEyNS0uMzc1LTYuMTQ0NTMxLS45NzY1NjItOS4xNjc5NjloMjA";
			s += "uNzkyOTY4bDY4LjE4MzU5NCA3MS42MDE1NjJ2NDguMzk4NDM4YzAgNC40MTQwNjItMy41OTM3NSA4LTggOHptOCAxMjBoMTZ2MTZoLTE2em0t";
			s += "NDgwIDBoMTZ2MTZoLTE2em0wIDAiIGZpbGw9InVybCgjYSkiLz48L3N2Zz4K\" />\n";
			s += "<h3>%t</h3>\n<h5><a href=\"%d\">%d</a> %u</h5></body></html>".
					replace("%t","Web sayfası yüklenemedi").
					replace("%d",url).
					replace("%u","adresine giriş <b>%e</b> yüzünden başarısız oldu").
					replace("%e",err);
			h.p = s;
			return h;
		}
		
		private WebViewHolder setConnection(String url) throws IOException {
			URL u = new URL(url);
			HttpURLConnection uc = (HttpURLConnection) u.openConnection();
			uc.setDoInput(true);
			uc.setRequestProperty("User-Agent",mAgent);
			uc.setRequestProperty("Accept",mTypes);
			uc.setRequestProperty("Accept-Language",mLangs);
			//uc.setRequestProperty("X-Frame-Options","");
			uc.setConnectTimeout(Integer.MAX_VALUE);
			uc.setReadTimeout(Integer.MAX_VALUE);
			if(mParams != null){
				for(String[] p : mParams){
					uc.setRequestProperty(p[0],p[1]);
				}
			}
			uc.setFollowRedirects(true);
			uc.connect();
			switch(uc.getResponseCode()){
				case HttpURLConnection.HTTP_MOVED_PERM:
				case HttpURLConnection.HTTP_MOVED_TEMP:
					if(rc < mr){
						uc.disconnect();
						u = new URL(u,URLDecoder.decode(uc.getHeaderField("Location")));
						rc++;
						return setConnection(u.toExternalForm());
					} else {
						return null;
					}
				default:
					Scanner sc = new Scanner(uc.getInputStream());
					String s = "";
					while(sc.hasNext()){
						s += sc.nextLine()+"\n";
					}
					WebViewHolder h = new WebViewHolder();
					h.e = uc.getContentEncoding();
					h.t = uc.getContentType();
					h.b = u.getProtocol()+"://"+u.getHost();
					h.p = s;
					h.f = url;
					rc = 0;
					return h;
			}
		}
	}

	private class WebViewHolder {
		String e,t,p,b,f;
	}
	
	private static Object getDeclaredField(Object o, String field){
		try {
			Field f = null;
			f = o.getClass().getDeclaredField(field);
			f.setAccessible(true);
			return f.get(o);
		} catch(Exception | Error e){
			Log.e(MainActivity.class.getName(),e.getMessage());
			return null;
		}
	}

	private static void accessToMethod(Object o, Class arg, String name, Object v){
		try {
			Method m = null;
			if(arg != null) m = o.getClass().getMethod(name, arg);
			else m = o.getClass().getMethod(name);
			m.setAccessible(true);
			if(arg != null) m.invoke(o,v);
			else m.invoke(o);
		} catch(Exception | Error e){
			Log.e(MainActivity.class.getName(),e.getMessage());
		}
	}

	private static Class getFromName(String name){
		try {
			return Class.forName(name);
		} catch(Exception | Error e){
			Log.e(MainActivity.class.getName(),e.getMessage());
			return null;
		}
	}
	
	private class WebClient extends WebViewClient{

		public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
			if(failingUrl == history.get(history.size()-1)){
				loadUrl("error<>"+failingUrl+"<>"+description);
			}
			//goBack();
			/*if(check(failingUrl,true)){
				loadUrl("error::"+failingUrl+"::"+description);
			} else {*/
				//super.onReceivedError(view,errorCode,description,failingUrl);
			//}
		}

		public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error){
			onReceivedError(view,error.getErrorCode(),error.getDescription().toString(),request.getUrl().toString());
		}

		@Override
		public void onPageStarted(WebView view, String url, Bitmap favicon){
			if(check(url,false)){
				super.onPageStarted(view,url,favicon);
			} else {
				stopLoading();
			}
		}

		@Override
		public void onLoadResource(WebView view, String url){
			if(check(url,true)){
				super.onLoadResource(view,url);
				Log.d("LoadRes: ACCEPTED - "+history.get(history.size()-1),url);
			} else {
				Log.d("LoadRes: REJECTED - "+history.get(history.size()-1),url);
			}
		}

		public boolean shouldOverrideUrlLoading(WebView view, String url){
			if(check(url,false)){
				loadUrl(url);
			}
			return true;
		}

		public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request){
			return shouldOverrideUrlLoading(view,request.getUrl().toString());
		}
		
	}
	
	
	String[] bannedRes = {"1e100.net","doubleclick.net","accounts.google.com/",
							"www.googleadservices.com/","googlesyndication.com/",
							"/adservice.google.","/id.google.","fonts.googleapis.com/",
							"/pagead/","gstatic.com/","ggpht.com/","google-analytics.com/",
							"l.google.com/","el=adunit","video_masthead","adformat=",
							"ad_block=1","yt_ad=1","key=yt","embed-player.js","adnohost",
							"player-webp","remote.js","log_event?","player-sprite-mode",
							"player_ias","player_remote_ux","widgetapi.js","browse_ajax",
							"service_ajax","endscreen.js","annotations_module.js",
							"JOYx3W5PzoQFRZAOzhrT8YIZJDIx1URDMPi7CeVLUwM.js","videomasthead",
							"/stats/?qoe","clients1.google.com","clients2.google.com",
							"clients3.google.com"};

	public boolean checkRes(String url){
		for(String s : bannedRes){
			if(url.contains(s)){
				return false;
			}
		}
		return true;
	}

	public boolean check(String url,boolean res){
		if(checkRes(url)){
			return true;
		}
		if(!res){
			AlertDialog.Builder b = new AlertDialog.Builder(getContext());
			b.setTitle("Hoop! Ufak bir sorun var");
			b.setMessage("Bazı Google sitelerine girmeyi deneseniz bile bu modifiye edilmiş webkitle başarısız olacaksınız. Bu yüzden bu webkitle "+url+" adresine bağlanmayı aklınızdan bile geçirmeyin. Bu uyarıyı görmek istemiyorsanız ve çöplerinizle mutluysanız lütfen bu webkiti kullanmak yerine standart webkiti kullanın.");
			b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
					@Override
					public void onClick(DialogInterface p1, int p2){
						p1.cancel();
					}
				});
			b.create().show();
		}
		return false;
	}
	
	private String createNavigatorInjector(){
		String s = "<script>\n";
		s += defineGetter("vendor", "Unknown Company");
		s += defineGetter("maxTouchPoints",-1);
		s += defineGetter("hardwareConcurrency",-1);
		s += defineGetter("appVersion",mAgent.substring(8,mAgent.length()));
		s += defineGetter("platform","Linux x86_64");
		s += defineGetter("userAgent",mAgent);
		s += defineGetter("language",mLangs.split(",")[0]);
		s += defineGetter("languages",mLangs.split(";")[0]);
		s += defineGetter("onLine",true);
		s += defineGetter("doNotTrack",true);
		s += "</script>";
		return s;
	}
	
	private String defineGetter(String key, Object value){
		return "navigator.__defineGetter__('"+key+"', function(){ return '"+value+"' });\n";
	}
	
	private class ChromeClient extends WebChromeClient{

		@Override
		public void onProgressChanged(WebView view, int newProgress){
			super.onProgressChanged(view, newProgress);
			Activity a = (Activity) getContext();
			PackageManager p = a.getPackageManager();
			if(newProgress > 98){
				try {
					a.setTitle(p.getApplicationInfo(a.getPackageName(), 0).loadLabel(p));
				} catch(PackageManager.NameNotFoundException e){}
			} else {
				a.setTitle("Progress: "+newProgress);
			}
		}
		/*@Override
		public void onConsoleMessage(String message, int lineNumber, String sourceID){
			Log.e(getClass().getName(),message+" on "+lineNumber+" "+sourceID);
			Toast.makeText(getContext(),message+" on "+lineNumber+" "+sourceID,Toast.LENGTH_LONG).show();
			super.onConsoleMessage(message, lineNumber, sourceID);
		}*/
	}

	@Override
	public boolean canGoBack(){
		return history.size() > 1;
	}

	@Override
	public void goBack(){
		loadUrl(history.get(history.size()-2));
		history.remove(history.size()-1);
	}
	
	public boolean handleBackButton(){
		if(canGoBack()){
			goBack();
			return true;
		}
		return false;
	}
	
}

still failing on some pages allowing only chrome. i don't get what they are tracking anything except X-Requested-With. There must be some .js checks.

@pocmo
Copy link
Contributor Author

pocmo commented May 4, 2020

I am going to close this issue: All variants of Focus that we ship are using GeckoView now and therefore do not send this header anymore.

@pocmo pocmo closed this as completed May 4, 2020
@easyonebd
Copy link

the value of X-requested-with is from getAll() method in org.chromium.base.BuildInfo
getAll()[8] is the key

String hostPackageName = ContextUtils.getApplicationContext().getPackageName();

so if we can change the value , X-requested-with is resolved

// xxApplication.java

   @Override
    public String getPackageName() {
        try {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            for (StackTraceElement element : stackTrace) {
                if ("org.chromium.base.BuildInfo".equalsIgnoreCase(element.getClassName())) {
                    if ("getAll".equalsIgnoreCase(element.getMethodName())) {
                        String customPackageName = "com.tencent.qq";
                        return customPackageName;
                    }
                    break;
                }
            }
        } catch (Exception e) { }

        return super.getPackageName();
    }

this worked for me

@ChenShiChao

This comment has been minimized.

@mozilla-mobile mozilla-mobile locked as resolved and limited conversation to collaborators Jan 5, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants