Index: libadblockplus-android-webview/src/org/adblockplus/libadblockplus/android/webview/AdblockWebView.java |
diff --git a/libadblockplus-android-webview/src/org/adblockplus/libadblockplus/android/webview/AdblockWebView.java b/libadblockplus-android-webview/src/org/adblockplus/libadblockplus/android/webview/AdblockWebView.java |
deleted file mode 100644 |
index 73d88b377fa1b9f16ce3ed49441b1fc46231fe94..0000000000000000000000000000000000000000 |
--- a/libadblockplus-android-webview/src/org/adblockplus/libadblockplus/android/webview/AdblockWebView.java |
+++ /dev/null |
@@ -1,1441 +0,0 @@ |
-/* |
- * This file is part of Adblock Plus <https://adblockplus.org/>, |
- * Copyright (C) 2006-present eyeo GmbH |
- * |
- * Adblock Plus is free software: you can redistribute it and/or modify |
- * it under the terms of the GNU General Public License version 3 as |
- * published by the Free Software Foundation. |
- * |
- * Adblock Plus is distributed in the hope that it will be useful, |
- * but WITHOUT ANY WARRANTY; without even the implied warranty of |
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
- * GNU General Public License for more details. |
- * |
- * You should have received a copy of the GNU General Public License |
- * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
- */ |
- |
-package org.adblockplus.libadblockplus.android.webview; |
- |
-import android.annotation.TargetApi; |
-import android.content.Context; |
-import android.graphics.Bitmap; |
-import android.graphics.Canvas; |
-import android.graphics.Color; |
-import android.net.http.SslError; |
-import android.os.Build; |
-import android.os.Handler; |
-import android.os.Message; |
-import android.util.AttributeSet; |
-import android.util.Log; |
-import android.view.KeyEvent; |
-import android.view.View; |
-import android.webkit.ConsoleMessage; |
-import android.webkit.GeolocationPermissions; |
-import android.webkit.HttpAuthHandler; |
-import android.webkit.JavascriptInterface; |
-import android.webkit.JsPromptResult; |
-import android.webkit.JsResult; |
-import android.webkit.SslErrorHandler; |
-import android.webkit.ValueCallback; |
-import android.webkit.WebChromeClient; |
-import android.webkit.WebResourceRequest; // makes android min version to be 21 |
-import android.webkit.WebResourceResponse; |
-import android.webkit.WebStorage; |
-import android.webkit.WebView; |
-import android.webkit.WebViewClient; |
- |
-import org.adblockplus.libadblockplus.FilterEngine; |
-import org.adblockplus.libadblockplus.Subscription; |
-import org.adblockplus.libadblockplus.android.AdblockEngine; |
-import org.adblockplus.libadblockplus.android.AdblockEngineProvider; |
-import org.adblockplus.libadblockplus.android.SingleInstanceEngineProvider; |
-import org.adblockplus.libadblockplus.android.Utils; |
- |
-import java.io.IOException; |
-import java.util.Collections; |
-import java.util.HashMap; |
-import java.util.List; |
-import java.util.Map; |
-import java.util.concurrent.CountDownLatch; |
-import java.util.concurrent.atomic.AtomicBoolean; |
-import java.util.regex.Pattern; |
- |
-/** |
- * WebView with ad blocking |
- */ |
-public class AdblockWebView extends WebView |
-{ |
- private static final String TAG = Utils.getTag(AdblockWebView.class); |
- |
- /** |
- * Default (in some conditions) start redraw delay after DOM modified with injected JS (millis) |
- */ |
- public static final int ALLOW_DRAW_DELAY = 200; |
- /* |
- The value could be different for devices and completely unclear why we need it and |
- how to measure actual value |
- */ |
- |
- protected static final String HEADER_REFERRER = "Referer"; |
- protected static final String HEADER_REQUESTED_WITH = "X-Requested-With"; |
- protected static final String HEADER_REQUESTED_WITH_XMLHTTPREQUEST = "XMLHttpRequest"; |
- |
- private static final String BRIDGE_TOKEN = "{{BRIDGE}}"; |
- private static final String DEBUG_TOKEN = "{{DEBUG}}"; |
- private static final String HIDE_TOKEN = "{{HIDE}}"; |
- private static final String BRIDGE = "jsBridge"; |
- private static final String[] EMPTY_ARRAY = {}; |
- private static final String EMPTY_ELEMHIDE_ARRAY_STRING = "[]"; |
- |
- private static final Pattern RE_JS = Pattern.compile("\\.js$", Pattern.CASE_INSENSITIVE); |
- private static final Pattern RE_CSS = Pattern.compile("\\.css$", Pattern.CASE_INSENSITIVE); |
- private static final Pattern RE_IMAGE = Pattern.compile("\\.(?:gif|png|jpe?g|bmp|ico)$", Pattern.CASE_INSENSITIVE); |
- private static final Pattern RE_FONT = Pattern.compile("\\.(?:ttf|woff)$", Pattern.CASE_INSENSITIVE); |
- private static final Pattern RE_HTML = Pattern.compile("\\.html?$", Pattern.CASE_INSENSITIVE); |
- |
- private volatile boolean addDomListener = true; |
- private boolean adblockEnabled = true; |
- private boolean debugMode; |
- private AdblockEngineProvider provider; |
- private Integer loadError; |
- private int allowDrawDelay = ALLOW_DRAW_DELAY; |
- private WebChromeClient extWebChromeClient; |
- private WebViewClient extWebViewClient; |
- private WebViewClient intWebViewClient; |
- private Map<String, String> url2Referrer = Collections.synchronizedMap(new HashMap<String, String>()); |
- private String url; |
- private String injectJs; |
- private CountDownLatch elemHideLatch; |
- private String elemHideSelectorsString; |
- private Object elemHideThreadLockObject = new Object(); |
- private ElemHideThread elemHideThread; |
- private boolean loading; |
- private volatile boolean elementsHidden = false; |
- private final Handler handler = new Handler(); |
- |
- // used to prevent user see flickering for elements to hide |
- // for some reason it's rendered even if element is hidden on 'dom ready' event |
- private volatile boolean allowDraw = true; |
- |
- public AdblockWebView(Context context) |
- { |
- super(context); |
- initAbp(); |
- } |
- |
- public AdblockWebView(Context context, AttributeSet attrs) |
- { |
- super(context, attrs); |
- initAbp(); |
- } |
- |
- public AdblockWebView(Context context, AttributeSet attrs, int defStyle) |
- { |
- super(context, attrs, defStyle); |
- initAbp(); |
- } |
- |
- /** |
- * Warning: do not rename (used in injected JS by method name) |
- * @param value set if one need to set DOM listener |
- */ |
- @JavascriptInterface |
- public void setAddDomListener(boolean value) |
- { |
- d("addDomListener=" + value); |
- this.addDomListener = value; |
- } |
- |
- @JavascriptInterface |
- public boolean getAddDomListener() |
- { |
- return addDomListener; |
- } |
- |
- public boolean isAdblockEnabled() |
- { |
- return adblockEnabled; |
- } |
- |
- private void applyAdblockEnabled() |
- { |
- super.setWebViewClient(adblockEnabled ? intWebViewClient : extWebViewClient); |
- super.setWebChromeClient(adblockEnabled ? intWebChromeClient : extWebChromeClient); |
- } |
- |
- public void setAdblockEnabled(boolean adblockEnabled) |
- { |
- this.adblockEnabled = adblockEnabled; |
- applyAdblockEnabled(); |
- } |
- |
- @Override |
- public void setWebChromeClient(WebChromeClient client) |
- { |
- extWebChromeClient = client; |
- applyAdblockEnabled(); |
- } |
- |
- public boolean isDebugMode() |
- { |
- return debugMode; |
- } |
- |
- /** |
- * Set to true to see debug log output int AdblockWebView and JS console |
- * Should be set before first URL loading if using internal AdblockEngineProvider |
- * @param debugMode is debug mode |
- */ |
- public void setDebugMode(boolean debugMode) |
- { |
- this.debugMode = debugMode; |
- } |
- |
- private void d(String message) |
- { |
- if (debugMode) |
- { |
- Log.d(TAG, message); |
- } |
- } |
- |
- private void w(String message) |
- { |
- if (debugMode) |
- { |
- Log.w(TAG, message); |
- } |
- } |
- |
- private void e(String message, Throwable t) |
- { |
- Log.e(TAG, message, t); |
- } |
- |
- private void e(String message) |
- { |
- Log.e(TAG, message); |
- } |
- |
- private String readScriptFile(String filename) throws IOException |
- { |
- return Utils |
- .readAssetAsString(getContext(), filename) |
- .replace(BRIDGE_TOKEN, BRIDGE) |
- .replace(DEBUG_TOKEN, (debugMode ? "" : "//")); |
- } |
- |
- private void runScript(String script) |
- { |
- d("runScript started"); |
- evaluateJavascript(script, null); |
- d("runScript finished"); |
- } |
- |
- public void setProvider(final AdblockEngineProvider provider) |
- { |
- if (this.provider != null && provider != null && this.provider == provider) |
- { |
- return; |
- } |
- |
- final Runnable setRunnable = new Runnable() |
- { |
- @Override |
- public void run() |
- { |
- AdblockWebView.this.provider = provider; |
- if (AdblockWebView.this.provider != null) |
- { |
- AdblockWebView.this.provider.retain(true); // asynchronously |
- } |
- } |
- }; |
- |
- if (this.provider != null) |
- { |
- // as adblockEngine can be busy with elemhide thread we need to use callback |
- this.dispose(setRunnable); |
- } |
- else |
- { |
- setRunnable.run(); |
- } |
- } |
- |
- private WebChromeClient intWebChromeClient = new WebChromeClient() |
- { |
- @Override |
- public void onReceivedTitle(WebView view, String title) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onReceivedTitle(view, title); |
- } |
- } |
- |
- @Override |
- public void onReceivedIcon(WebView view, Bitmap icon) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onReceivedIcon(view, icon); |
- } |
- } |
- |
- @Override |
- public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onReceivedTouchIconUrl(view, url, precomposed); |
- } |
- } |
- |
- @Override |
- public void onShowCustomView(View view, CustomViewCallback callback) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onShowCustomView(view, callback); |
- } |
- } |
- |
- @Override |
- public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onShowCustomView(view, requestedOrientation, callback); |
- } |
- } |
- |
- @Override |
- public void onHideCustomView() |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onHideCustomView(); |
- } |
- } |
- |
- @Override |
- public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, |
- Message resultMsg) |
- { |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.onCreateWindow(view, isDialog, isUserGesture, resultMsg); |
- } |
- else |
- { |
- return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg); |
- } |
- } |
- |
- @Override |
- public void onRequestFocus(WebView view) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onRequestFocus(view); |
- } |
- } |
- |
- @Override |
- public void onCloseWindow(WebView window) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onCloseWindow(window); |
- } |
- } |
- |
- @Override |
- public boolean onJsAlert(WebView view, String url, String message, JsResult result) |
- { |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.onJsAlert(view, url, message, result); |
- } |
- else |
- { |
- return super.onJsAlert(view, url, message, result); |
- } |
- } |
- |
- @Override |
- public boolean onJsConfirm(WebView view, String url, String message, JsResult result) |
- { |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.onJsConfirm(view, url, message, result); |
- } |
- else |
- { |
- return super.onJsConfirm(view, url, message, result); |
- } |
- } |
- |
- @Override |
- public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, |
- JsPromptResult result) |
- { |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.onJsPrompt(view, url, message, defaultValue, result); |
- } |
- else |
- { |
- return super.onJsPrompt(view, url, message, defaultValue, result); |
- } |
- } |
- |
- @Override |
- public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) |
- { |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.onJsBeforeUnload(view, url, message, result); |
- } |
- else |
- { |
- return super.onJsBeforeUnload(view, url, message, result); |
- } |
- } |
- |
- @Override |
- public void onExceededDatabaseQuota(String url, String databaseIdentifier, long quota, |
- long estimatedDatabaseSize, long totalQuota, |
- WebStorage.QuotaUpdater quotaUpdater) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onExceededDatabaseQuota(url, databaseIdentifier, quota, |
- estimatedDatabaseSize, totalQuota, quotaUpdater); |
- } |
- else |
- { |
- super.onExceededDatabaseQuota(url, databaseIdentifier, quota, |
- estimatedDatabaseSize, totalQuota, quotaUpdater); |
- } |
- } |
- |
- @Override |
- public void onReachedMaxAppCacheSize(long requiredStorage, long quota, |
- WebStorage.QuotaUpdater quotaUpdater) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater); |
- } |
- else |
- { |
- super.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater); |
- } |
- } |
- |
- @Override |
- public void onGeolocationPermissionsShowPrompt(String origin, |
- GeolocationPermissions.Callback callback) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback); |
- } |
- else |
- { |
- super.onGeolocationPermissionsShowPrompt(origin, callback); |
- } |
- } |
- |
- @Override |
- public void onGeolocationPermissionsHidePrompt() |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onGeolocationPermissionsHidePrompt(); |
- } |
- else |
- { |
- super.onGeolocationPermissionsHidePrompt(); |
- } |
- } |
- |
- @Override |
- public boolean onJsTimeout() |
- { |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.onJsTimeout(); |
- } |
- else |
- { |
- return super.onJsTimeout(); |
- } |
- } |
- |
- @Override |
- public void onConsoleMessage(String message, int lineNumber, String sourceID) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onConsoleMessage(message, lineNumber, sourceID); |
- } |
- else |
- { |
- super.onConsoleMessage(message, lineNumber, sourceID); |
- } |
- } |
- |
- @Override |
- public boolean onConsoleMessage(ConsoleMessage consoleMessage) |
- { |
- d("JS: level=" + consoleMessage.messageLevel() |
- + ", message=\"" + consoleMessage.message() + "\"" |
- + ", sourceId=\"" + consoleMessage.sourceId() + "\"" |
- + ", line=" + consoleMessage.lineNumber()); |
- |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.onConsoleMessage(consoleMessage); |
- } |
- else |
- { |
- return super.onConsoleMessage(consoleMessage); |
- } |
- } |
- |
- @Override |
- public Bitmap getDefaultVideoPoster() |
- { |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.getDefaultVideoPoster(); |
- } |
- else |
- { |
- return super.getDefaultVideoPoster(); |
- } |
- } |
- |
- @Override |
- public View getVideoLoadingProgressView() |
- { |
- if (extWebChromeClient != null) |
- { |
- return extWebChromeClient.getVideoLoadingProgressView(); |
- } |
- else |
- { |
- return super.getVideoLoadingProgressView(); |
- } |
- } |
- |
- @Override |
- public void getVisitedHistory(ValueCallback<String[]> callback) |
- { |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.getVisitedHistory(callback); |
- } |
- else |
- { |
- super.getVisitedHistory(callback); |
- } |
- } |
- |
- @Override |
- public void onProgressChanged(WebView view, int newProgress) |
- { |
- d("Loading progress=" + newProgress + "%"); |
- |
- // addDomListener is changed to 'false' in `setAddDomListener` invoked from injected JS |
- if (getAddDomListener() && loadError == null && injectJs != null) |
- { |
- d("Injecting script"); |
- runScript(injectJs); |
- |
- if (allowDraw && loading) |
- { |
- startPreventDrawing(); |
- } |
- } |
- |
- // workaround for the issue: https://issues.adblockplus.org/ticket/5303 |
- if (newProgress == 100 && !allowDraw) |
- { |
- w("Workaround for the issue #5303"); |
- stopPreventDrawing(); |
- } |
- |
- if (extWebChromeClient != null) |
- { |
- extWebChromeClient.onProgressChanged(view, newProgress); |
- } |
- } |
- }; |
- |
- public int getAllowDrawDelay() |
- { |
- return allowDrawDelay; |
- } |
- |
- /** |
- * Set start redraw delay after DOM modified with injected JS |
- * (used to prevent flickering after 'DOM ready') |
- * @param allowDrawDelay delay (in millis) |
- */ |
- public void setAllowDrawDelay(int allowDrawDelay) |
- { |
- if (allowDrawDelay < 0) |
- { |
- throw new IllegalArgumentException("Negative value is not allowed"); |
- } |
- |
- this.allowDrawDelay = allowDrawDelay; |
- } |
- |
- @Override |
- public void setWebViewClient(WebViewClient client) |
- { |
- extWebViewClient = client; |
- applyAdblockEnabled(); |
- } |
- |
- private void clearReferrers() |
- { |
- d("Clearing referrers"); |
- url2Referrer.clear(); |
- } |
- |
- /** |
- * WebViewClient for API 21 and newer |
- * (has Referrer since it overrides `shouldInterceptRequest(..., request)` with referrer) |
- */ |
- private class AdblockWebViewClient extends WebViewClient |
- { |
- @Override |
- public boolean shouldOverrideUrlLoading(WebView view, String url) |
- { |
- if (extWebViewClient != null) |
- { |
- return extWebViewClient.shouldOverrideUrlLoading(view, url); |
- } |
- else |
- { |
- return super.shouldOverrideUrlLoading(view, url); |
- } |
- } |
- |
- @Override |
- public void onPageStarted(WebView view, String url, Bitmap favicon) |
- { |
- if (loading) |
- { |
- stopAbpLoading(); |
- } |
- |
- startAbpLoading(url); |
- |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onPageStarted(view, url, favicon); |
- } |
- else |
- { |
- super.onPageStarted(view, url, favicon); |
- } |
- } |
- |
- @Override |
- public void onPageFinished(WebView view, String url) |
- { |
- loading = false; |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onPageFinished(view, url); |
- } |
- else |
- { |
- super.onPageFinished(view, url); |
- } |
- } |
- |
- @Override |
- public void onLoadResource(WebView view, String url) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onLoadResource(view, url); |
- } |
- else |
- { |
- super.onLoadResource(view, url); |
- } |
- } |
- |
- @Override |
- public void onTooManyRedirects(WebView view, Message cancelMsg, Message continueMsg) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onTooManyRedirects(view, cancelMsg, continueMsg); |
- } |
- else |
- { |
- super.onTooManyRedirects(view, cancelMsg, continueMsg); |
- } |
- } |
- |
- @Override |
- public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) |
- { |
- e("Load error:" + |
- " code=" + errorCode + |
- " with description=" + description + |
- " for url=" + failingUrl); |
- loadError = errorCode; |
- |
- stopAbpLoading(); |
- |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onReceivedError(view, errorCode, description, failingUrl); |
- } |
- else |
- { |
- super.onReceivedError(view, errorCode, description, failingUrl); |
- } |
- } |
- |
- @Override |
- public void onFormResubmission(WebView view, Message dontResend, Message resend) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onFormResubmission(view, dontResend, resend); |
- } |
- else |
- { |
- super.onFormResubmission(view, dontResend, resend); |
- } |
- } |
- |
- @Override |
- public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.doUpdateVisitedHistory(view, url, isReload); |
- } |
- else |
- { |
- super.doUpdateVisitedHistory(view, url, isReload); |
- } |
- } |
- |
- @Override |
- public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onReceivedSslError(view, handler, error); |
- } |
- else |
- { |
- super.onReceivedSslError(view, handler, error); |
- } |
- } |
- |
- @Override |
- public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onReceivedHttpAuthRequest(view, handler, host, realm); |
- } |
- else |
- { |
- super.onReceivedHttpAuthRequest(view, handler, host, realm); |
- } |
- } |
- |
- @Override |
- public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) |
- { |
- if (extWebViewClient != null) |
- { |
- return extWebViewClient.shouldOverrideKeyEvent(view, event); |
- } |
- else |
- { |
- return super.shouldOverrideKeyEvent(view, event); |
- } |
- } |
- |
- @Override |
- public void onUnhandledKeyEvent(WebView view, KeyEvent event) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onUnhandledKeyEvent(view, event); |
- } |
- else |
- { |
- super.onUnhandledKeyEvent(view, event); |
- } |
- } |
- |
- @Override |
- public void onScaleChanged(WebView view, float oldScale, float newScale) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onScaleChanged(view, oldScale, newScale); |
- } |
- else |
- { |
- super.onScaleChanged(view, oldScale, newScale); |
- } |
- } |
- |
- @Override |
- public void onReceivedLoginRequest(WebView view, String realm, String account, String args) |
- { |
- if (extWebViewClient != null) |
- { |
- extWebViewClient.onReceivedLoginRequest(view, realm, account, args); |
- } |
- else |
- { |
- super.onReceivedLoginRequest(view, realm, account, args); |
- } |
- } |
- |
- protected WebResourceResponse shouldInterceptRequest( |
- WebView webview, String url, boolean isMainFrame, |
- boolean isXmlHttpRequest, String[] referrerChainArray) |
- { |
- // if dispose() was invoke, but the page is still loading then just let it go |
- if (provider.getCounter() == 0) |
- { |
- e("FilterEngine already disposed, allow loading"); |
- |
- // allow loading by returning null |
- return null; |
- } |
- else |
- { |
- provider.waitForReady(); |
- } |
- |
- if (isMainFrame) |
- { |
- // never blocking main frame requests, just subrequests |
- w(url + " is main frame, allow loading"); |
- |
- // allow loading by returning null |
- return null; |
- } |
- |
- // whitelisted |
- if (provider.getEngine().isDomainWhitelisted(url, referrerChainArray)) |
- { |
- w(url + " domain is whitelisted, allow loading"); |
- |
- // allow loading by returning null |
- return null; |
- } |
- |
- if (provider.getEngine().isDocumentWhitelisted(url, referrerChainArray)) |
- { |
- w(url + " document is whitelisted, allow loading"); |
- |
- // allow loading by returning null |
- return null; |
- } |
- |
- // determine the content |
- FilterEngine.ContentType contentType; |
- if (isXmlHttpRequest) |
- { |
- contentType = FilterEngine.ContentType.XMLHTTPREQUEST; |
- } |
- else |
- { |
- if (RE_JS.matcher(url).find()) |
- { |
- contentType = FilterEngine.ContentType.SCRIPT; |
- } |
- else if (RE_CSS.matcher(url).find()) |
- { |
- contentType = FilterEngine.ContentType.STYLESHEET; |
- } |
- else if (RE_IMAGE.matcher(url).find()) |
- { |
- contentType = FilterEngine.ContentType.IMAGE; |
- } |
- else if (RE_FONT.matcher(url).find()) |
- { |
- contentType = FilterEngine.ContentType.FONT; |
- } |
- else if (RE_HTML.matcher(url).find()) |
- { |
- contentType = FilterEngine.ContentType.SUBDOCUMENT; |
- } |
- else |
- { |
- contentType = FilterEngine.ContentType.OTHER; |
- } |
- } |
- |
- // check if we should block |
- if (provider.getEngine().matches(url, contentType, referrerChainArray)) |
- { |
- w("Blocked loading " + url); |
- |
- // if we should block, return empty response which results in 'errorLoading' callback |
- return new WebResourceResponse("text/plain", "UTF-8", null); |
- } |
- |
- d("Allowed loading " + url); |
- |
- // continue by returning null |
- return null; |
- } |
- |
- @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
- @Override |
- public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) |
- { |
- // here we just trying to fill url -> referrer map |
- // blocking/allowing loading will happen in `shouldInterceptRequest(WebView,String)` |
- String url = request.getUrl().toString(); |
- |
- boolean isXmlHttpRequest = |
- request.getRequestHeaders().containsKey(HEADER_REQUESTED_WITH) && |
- HEADER_REQUESTED_WITH_XMLHTTPREQUEST.equals( |
- request.getRequestHeaders().get(HEADER_REQUESTED_WITH)); |
- |
- String referrer = request.getRequestHeaders().get(HEADER_REFERRER); |
- String[] referrers; |
- |
- if (referrer != null) |
- { |
- d("Header referrer for " + url + " is " + referrer); |
- url2Referrer.put(url, referrer); |
- |
- referrers = new String[] |
- { |
- referrer |
- }; |
- } |
- else |
- { |
- w("No referrer header for " + url); |
- referrers = EMPTY_ARRAY; |
- } |
- |
- return shouldInterceptRequest(view, url, request.isForMainFrame(), isXmlHttpRequest, referrers); |
- } |
- } |
- |
- private void initAbp() |
- { |
- addJavascriptInterface(this, BRIDGE); |
- initClients(); |
- } |
- |
- private void initClients() |
- { |
- intWebViewClient = new AdblockWebViewClient(); |
- applyAdblockEnabled(); |
- } |
- |
- private class ElemHideThread extends Thread |
- { |
- private String selectorsString; |
- private CountDownLatch finishedLatch; |
- private AtomicBoolean isFinished; |
- private AtomicBoolean isCancelled; |
- |
- public ElemHideThread(CountDownLatch finishedLatch) |
- { |
- this.finishedLatch = finishedLatch; |
- isFinished = new AtomicBoolean(false); |
- isCancelled = new AtomicBoolean(false); |
- } |
- |
- @Override |
- public void run() |
- { |
- try |
- { |
- if (provider.getCounter() == 0) |
- { |
- w("FilterEngine already disposed"); |
- selectorsString = EMPTY_ELEMHIDE_ARRAY_STRING; |
- } |
- else |
- { |
- provider.waitForReady(); |
- String[] referrers = new String[] |
- { |
- url |
- }; |
- |
- List<Subscription> subscriptions = provider |
- .getEngine() |
- .getFilterEngine() |
- .getListedSubscriptions(); |
- |
- try |
- { |
- d("Listed subscriptions: " + subscriptions.size()); |
- if (debugMode) |
- { |
- for (Subscription eachSubscription : subscriptions) |
- { |
- d("Subscribed to " |
- + (eachSubscription.isDisabled() ? "disabled" : "enabled") |
- + " " + eachSubscription); |
- } |
- } |
- } |
- finally |
- { |
- for (Subscription eachSubscription : subscriptions) |
- { |
- eachSubscription.dispose(); |
- } |
- } |
- |
- final String domain = provider.getEngine().getFilterEngine().getHostFromURL(url); |
- if (domain == null) |
- { |
- e("Failed to extract domain from " + url); |
- selectorsString = EMPTY_ELEMHIDE_ARRAY_STRING; |
- } |
- else |
- { |
- d("Requesting elemhide selectors from AdblockEngine for " + url + " in " + this); |
- List<String> selectors = provider |
- .getEngine() |
- .getElementHidingSelectors(url, domain, referrers); |
- |
- d("Finished requesting elemhide selectors, got " + selectors.size() + " in " + this); |
- selectorsString = Utils.stringListToJsonArray(selectors); |
- } |
- } |
- } |
- finally |
- { |
- if (isCancelled.get()) |
- { |
- w("This thread is cancelled, exiting silently " + this); |
- } |
- else |
- { |
- finish(selectorsString); |
- } |
- } |
- } |
- |
- private void onFinished() |
- { |
- finishedLatch.countDown(); |
- synchronized (finishedRunnableLockObject) |
- { |
- if (finishedRunnable != null) |
- { |
- finishedRunnable.run(); |
- } |
- } |
- } |
- |
- private void finish(String result) |
- { |
- isFinished.set(true); |
- d("Setting elemhide string " + result.length() + " bytes"); |
- elemHideSelectorsString = result; |
- onFinished(); |
- } |
- |
- private final Object finishedRunnableLockObject = new Object(); |
- private Runnable finishedRunnable; |
- |
- public void setFinishedRunnable(Runnable runnable) |
- { |
- synchronized (finishedRunnableLockObject) |
- { |
- this.finishedRunnable = runnable; |
- } |
- } |
- |
- public void cancel() |
- { |
- w("Cancelling elemhide thread " + this); |
- if (isFinished.get()) |
- { |
- w("This thread is finished, exiting silently " + this); |
- } |
- else |
- { |
- isCancelled.set(true); |
- finish(EMPTY_ELEMHIDE_ARRAY_STRING); |
- } |
- } |
- } |
- |
- private Runnable elemHideThreadFinishedRunnable = new Runnable() |
- { |
- @Override |
- public void run() |
- { |
- synchronized (elemHideThreadLockObject) |
- { |
- w("elemHideThread set to null"); |
- elemHideThread = null; |
- } |
- } |
- }; |
- |
- private void initAbpLoading() |
- { |
- getSettings().setJavaScriptEnabled(true); |
- buildInjectJs(); |
- ensureProvider(); |
- } |
- |
- private void ensureProvider() |
- { |
- // if AdblockWebView works as drop-in replacement for WebView 'provider' is not set. |
- // Thus AdblockWebView is using SingleInstanceEngineProvider instance |
- if (provider == null) |
- { |
- setProvider(new SingleInstanceEngineProvider( |
- getContext(), AdblockEngine.BASE_PATH_DIRECTORY, debugMode)); |
- } |
- } |
- |
- private void startAbpLoading(String newUrl) |
- { |
- d("Start loading " + newUrl); |
- |
- loading = true; |
- addDomListener = true; |
- elementsHidden = false; |
- loadError = null; |
- url = newUrl; |
- |
- if (url != null) |
- { |
- elemHideLatch = new CountDownLatch(1); |
- elemHideThread = new ElemHideThread(elemHideLatch); |
- elemHideThread.setFinishedRunnable(elemHideThreadFinishedRunnable); |
- elemHideThread.start(); |
- } |
- else |
- { |
- elemHideLatch = null; |
- } |
- } |
- |
- private void buildInjectJs() |
- { |
- try |
- { |
- if (injectJs == null) |
- { |
- injectJs = readScriptFile("inject.js").replace(HIDE_TOKEN, readScriptFile("css.js")); |
- } |
- } |
- catch (IOException e) |
- { |
- e("Failed to read script", e); |
- } |
- } |
- |
- @Override |
- public void goBack() |
- { |
- if (loading) |
- { |
- stopAbpLoading(); |
- } |
- |
- super.goBack(); |
- } |
- |
- @Override |
- public void goForward() |
- { |
- if (loading) |
- { |
- stopAbpLoading(); |
- } |
- |
- super.goForward(); |
- } |
- |
- @Override |
- public void reload() |
- { |
- initAbpLoading(); |
- |
- if (loading) |
- { |
- stopAbpLoading(); |
- } |
- |
- super.reload(); |
- } |
- |
- @Override |
- public void loadUrl(String url) |
- { |
- initAbpLoading(); |
- |
- if (loading) |
- { |
- stopAbpLoading(); |
- } |
- |
- super.loadUrl(url); |
- } |
- |
- @Override |
- public void loadUrl(String url, Map<String, String> additionalHttpHeaders) |
- { |
- initAbpLoading(); |
- |
- if (loading) |
- { |
- stopAbpLoading(); |
- } |
- |
- super.loadUrl(url, additionalHttpHeaders); |
- } |
- |
- @Override |
- public void loadData(String data, String mimeType, String encoding) |
- { |
- initAbpLoading(); |
- |
- if (loading) |
- { |
- stopAbpLoading(); |
- } |
- |
- super.loadData(data, mimeType, encoding); |
- } |
- |
- @Override |
- public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, |
- String historyUrl) |
- { |
- initAbpLoading(); |
- |
- if (loading) |
- { |
- stopAbpLoading(); |
- } |
- |
- super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); |
- } |
- |
- @Override |
- public void stopLoading() |
- { |
- stopAbpLoading(); |
- super.stopLoading(); |
- } |
- |
- private void stopAbpLoading() |
- { |
- d("Stop abp loading"); |
- |
- loading = false; |
- stopPreventDrawing(); |
- clearReferrers(); |
- |
- synchronized (elemHideThreadLockObject) |
- { |
- if (elemHideThread != null) |
- { |
- elemHideThread.cancel(); |
- } |
- } |
- } |
- |
- // warning: do not rename (used in injected JS by method name) |
- @JavascriptInterface |
- public void setElementsHidden(boolean value) |
- { |
- // invoked with 'true' by JS callback when DOM is loaded |
- elementsHidden = value; |
- |
- // fired on worker thread, but needs to be invoked on main thread |
- if (value) |
- { |
-// handler.post(allowDrawRunnable); |
-// should work, but it's not working: |
-// the user can see element visible even though it was hidden on dom event |
- |
- if (allowDrawDelay > 0) |
- { |
- d("Scheduled 'allow drawing' invocation in " + allowDrawDelay + " ms"); |
- } |
- handler.postDelayed(allowDrawRunnable, allowDrawDelay); |
- } |
- } |
- |
- // warning: do not rename (used in injected JS by method name) |
- @JavascriptInterface |
- public boolean isElementsHidden() |
- { |
- return elementsHidden; |
- } |
- |
- @Override |
- public void onPause() |
- { |
- handler.removeCallbacks(allowDrawRunnable); |
- super.onPause(); |
- } |
- |
- @Override |
- protected void onDraw(Canvas canvas) |
- { |
- if (allowDraw) |
- { |
- super.onDraw(canvas); |
- } |
- else |
- { |
- w("Prevent drawing"); |
- drawEmptyPage(canvas); |
- } |
- } |
- |
- private void drawEmptyPage(Canvas canvas) |
- { |
- // assuming default color is WHITE |
- canvas.drawColor(Color.WHITE); |
- } |
- |
- protected void startPreventDrawing() |
- { |
- w("Start prevent drawing"); |
- |
- allowDraw = false; |
- } |
- |
- protected void stopPreventDrawing() |
- { |
- d("Stop prevent drawing, invalidating"); |
- |
- allowDraw = true; |
- invalidate(); |
- } |
- |
- private Runnable allowDrawRunnable = new Runnable() |
- { |
- @Override |
- public void run() |
- { |
- stopPreventDrawing(); |
- } |
- }; |
- |
- // warning: do not rename (used in injected JS by method name) |
- @JavascriptInterface |
- public String getElemhideSelectors() |
- { |
- if (elemHideLatch == null) |
- { |
- return EMPTY_ELEMHIDE_ARRAY_STRING; |
- } |
- else |
- { |
- try |
- { |
- // elemhide selectors list getting is started in startAbpLoad() in background thread |
- d("Waiting for elemhide selectors to be ready"); |
- elemHideLatch.await(); |
- d("Elemhide selectors ready, " + elemHideSelectorsString.length() + " bytes"); |
- |
- clearReferrers(); |
- |
- return elemHideSelectorsString; |
- } |
- catch (InterruptedException e) |
- { |
- w("Interrupted, returning empty selectors list"); |
- return EMPTY_ELEMHIDE_ARRAY_STRING; |
- } |
- } |
- } |
- |
- private void doDispose() |
- { |
- w("Disposing AdblockEngine"); |
- provider.release(); |
- } |
- |
- private class DisposeRunnable implements Runnable |
- { |
- private Runnable disposeFinished; |
- |
- private DisposeRunnable(Runnable disposeFinished) |
- { |
- this.disposeFinished = disposeFinished; |
- } |
- |
- @Override |
- public void run() |
- { |
- doDispose(); |
- |
- if (disposeFinished != null) |
- { |
- disposeFinished.run(); |
- } |
- } |
- } |
- |
- /** |
- * Dispose AdblockWebView and internal adblockEngine if it was created |
- * If external AdblockEngine was passed using `setAdblockEngine()` it should be disposed explicitly |
- * Warning: runnable can be invoked from background thread |
- * @param disposeFinished runnable to run when AdblockWebView is disposed |
- */ |
- public void dispose(final Runnable disposeFinished) |
- { |
- d("Dispose invoked"); |
- |
- if (provider == null) |
- { |
- d("No internal AdblockEngineProvider created"); |
- return; |
- } |
- |
- stopLoading(); |
- |
- DisposeRunnable disposeRunnable = new DisposeRunnable(disposeFinished); |
- synchronized (elemHideThreadLockObject) |
- { |
- if (elemHideThread != null) |
- { |
- w("Busy with elemhide selectors, delayed disposing scheduled"); |
- elemHideThread.setFinishedRunnable(disposeRunnable); |
- } |
- else |
- { |
- disposeRunnable.run(); |
- } |
- } |
- } |
-} |