| 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(); |
| - } |
| - } |
| - } |
| -} |