| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 /* | 
|  | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|  | 3  * Copyright (C) 2006-2016 Eyeo GmbH | 
|  | 4  * | 
|  | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|  | 6  * it under the terms of the GNU General Public License version 3 as | 
|  | 7  * published by the Free Software Foundation. | 
|  | 8  * | 
|  | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
|  | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12  * GNU General Public License for more details. | 
|  | 13  * | 
|  | 14  * You should have received a copy of the GNU General Public License | 
|  | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 16  */ | 
|  | 17 | 
|  | 18 package org.adblockplus.android; | 
|  | 19 | 
|  | 20 import org.adblockplus.libadblockplus.AppInfo; | 
|  | 21 import org.adblockplus.libadblockplus.Filter; | 
|  | 22 import org.adblockplus.libadblockplus.FilterEngine; | 
|  | 23 import org.adblockplus.libadblockplus.JsEngine; | 
|  | 24 | 
|  | 25 import android.content.Context; | 
|  | 26 import android.content.pm.PackageInfo; | 
|  | 27 import android.content.pm.PackageManager; | 
|  | 28 import android.graphics.Bitmap; | 
|  | 29 import android.graphics.Canvas; | 
|  | 30 import android.graphics.Color; | 
|  | 31 import android.net.http.SslError; | 
|  | 32 import android.os.Build; | 
|  | 33 import android.os.Handler; | 
|  | 34 import android.os.Message; | 
|  | 35 import android.util.AttributeSet; | 
|  | 36 import android.util.Log; | 
|  | 37 import android.view.KeyEvent; | 
|  | 38 import android.view.View; | 
|  | 39 import android.webkit.ConsoleMessage; | 
|  | 40 import android.webkit.GeolocationPermissions; | 
|  | 41 import android.webkit.HttpAuthHandler; | 
|  | 42 import android.webkit.JsPromptResult; | 
|  | 43 import android.webkit.JsResult; | 
|  | 44 import android.webkit.SslErrorHandler; | 
|  | 45 import android.webkit.ValueCallback; | 
|  | 46 import android.webkit.WebChromeClient; | 
|  | 47 import android.webkit.WebResourceResponse; | 
|  | 48 import android.webkit.WebStorage; | 
|  | 49 import android.webkit.WebView; | 
|  | 50 import android.webkit.JavascriptInterface; // makes android min version to be 17 | 
|  | 51 import android.webkit.WebViewClient; | 
|  | 52 | 
|  | 53 import java.io.IOException; | 
|  | 54 import java.net.URISyntaxException; | 
|  | 55 import java.util.List; | 
|  | 56 import java.util.Locale; | 
|  | 57 import java.util.Map; | 
|  | 58 import java.util.concurrent.CountDownLatch; | 
|  | 59 import java.util.concurrent.atomic.AtomicBoolean; | 
|  | 60 import java.util.regex.Pattern; | 
|  | 61 | 
|  | 62 /** | 
|  | 63  * WebView with ad blocking | 
|  | 64  */ | 
|  | 65 public class AdblockWebView extends WebView | 
|  | 66 { | 
|  | 67   private static final String TAG = Utils.getTag(AdblockWebView.class); | 
|  | 68 | 
|  | 69   private volatile boolean addDomListener = true; | 
|  | 70 | 
|  | 71   /** | 
|  | 72    * Warning: do not rename (used in injected JS by method name) | 
|  | 73    * @param value set if one need to set DOM listener | 
|  | 74    */ | 
|  | 75   @JavascriptInterface | 
|  | 76   public void setAddDomListener(boolean value) { | 
|  | 77     this.addDomListener = value; | 
|  | 78   } | 
|  | 79 | 
|  | 80   @JavascriptInterface | 
|  | 81   public boolean getAddDomListener() | 
|  | 82   { | 
|  | 83     return addDomListener; | 
|  | 84   } | 
|  | 85 | 
|  | 86   private WebChromeClient extWebChromeClient; | 
|  | 87 | 
|  | 88   @Override | 
|  | 89   public void setWebChromeClient(WebChromeClient client) | 
|  | 90   { | 
|  | 91     extWebChromeClient = client; | 
|  | 92   } | 
|  | 93 | 
|  | 94   private boolean debugMode; | 
|  | 95 | 
|  | 96   public boolean isDebugMode() | 
|  | 97   { | 
|  | 98     return debugMode; | 
|  | 99   } | 
|  | 100 | 
|  | 101   /** | 
|  | 102    * Set to true to see debug log output int AdblockWebView and JS console | 
|  | 103    * @param debugMode is debug mode | 
|  | 104    */ | 
|  | 105   public void setDebugMode(boolean debugMode) | 
|  | 106   { | 
|  | 107     this.debugMode = debugMode; | 
|  | 108   } | 
|  | 109 | 
|  | 110   private void d(String message) | 
|  | 111   { | 
|  | 112     if (debugMode) | 
|  | 113     { | 
|  | 114       Log.d(TAG, message); | 
|  | 115     } | 
|  | 116   } | 
|  | 117 | 
|  | 118   private void w(String message) | 
|  | 119   { | 
|  | 120     if (debugMode) | 
|  | 121     { | 
|  | 122       Log.w(TAG, message); | 
|  | 123     } | 
|  | 124   } | 
|  | 125 | 
|  | 126   private void e(String message, Throwable t) | 
|  | 127   { | 
|  | 128     Log.e(TAG, message, t); | 
|  | 129   } | 
|  | 130 | 
|  | 131   private void e(String message) | 
|  | 132   { | 
|  | 133     Log.e(TAG, message); | 
|  | 134   } | 
|  | 135 | 
|  | 136   private static final String BRIDGE_TOKEN = "{{BRIDGE}}"; | 
|  | 137   private static final String DEBUG_TOKEN = "{{DEBUG}}"; | 
|  | 138   private static final String HIDE_TOKEN = "{{HIDE}}"; | 
|  | 139   private static final String BRIDGE = "jsBridge"; | 
|  | 140 | 
|  | 141   private String readScriptFile(String filename) throws IOException | 
|  | 142   { | 
|  | 143     return Utils | 
|  | 144       .readAssetAsString(getContext(), filename) | 
|  | 145       .replace(BRIDGE_TOKEN, BRIDGE) | 
|  | 146       .replace(DEBUG_TOKEN, (debugMode ? "" : "//")); | 
|  | 147   } | 
|  | 148 | 
|  | 149   private void runScript(String script) { | 
|  | 150     if (Build.VERSION.SDK_INT >= 19) | 
|  | 151     { | 
|  | 152       evaluateJavascript(script, null); | 
|  | 153     } | 
|  | 154     else | 
|  | 155     { | 
|  | 156       loadUrl("javascript:" + script); | 
|  | 157     } | 
|  | 158   } | 
|  | 159 | 
|  | 160   private boolean disposeFilterEngine; | 
|  | 161 | 
|  | 162   private JsEngine jsEngine; | 
|  | 163 | 
|  | 164   public JsEngine getJsEngine() | 
|  | 165   { | 
|  | 166     return jsEngine; | 
|  | 167   } | 
|  | 168 | 
|  | 169   private FilterEngine filterEngine; | 
|  | 170 | 
|  | 171   public FilterEngine getFilterEngine() | 
|  | 172   { | 
|  | 173     return filterEngine; | 
|  | 174   } | 
|  | 175 | 
|  | 176   private Integer loadError; | 
|  | 177 | 
|  | 178   /** | 
|  | 179    * Set external filter engine. A new (internal) is created automatically if no
      t set | 
|  | 180    * Don't forget to invoke {@link #dispose()} if not using external filter engi
      ne | 
|  | 181    * @param newFilterEngine external filter engine | 
|  | 182    */ | 
|  | 183   public void setFilterEngine(FilterEngine newFilterEngine) | 
|  | 184   { | 
|  | 185     if (filterEngine != null && newFilterEngine != null && newFilterEngine == fi
      lterEngine) | 
|  | 186     { | 
|  | 187       return; | 
|  | 188     } | 
|  | 189 | 
|  | 190     if (filterEngine != null && disposeFilterEngine) | 
|  | 191     { | 
|  | 192       filterEngine.dispose(); | 
|  | 193     } | 
|  | 194 | 
|  | 195     filterEngine = newFilterEngine; | 
|  | 196     disposeFilterEngine = false; | 
|  | 197 | 
|  | 198     if (newFilterEngine != null && jsEngine != null) | 
|  | 199     { | 
|  | 200       jsEngine.dispose(); | 
|  | 201     } | 
|  | 202     jsEngine = null; | 
|  | 203   } | 
|  | 204 | 
|  | 205   private WebChromeClient intWebChromeClient = new WebChromeClient() | 
|  | 206   { | 
|  | 207     @Override | 
|  | 208     public void onReceivedTitle(WebView view, String title) | 
|  | 209     { | 
|  | 210       if (extWebChromeClient != null) | 
|  | 211       { | 
|  | 212         extWebChromeClient.onReceivedTitle(view, title); | 
|  | 213       } | 
|  | 214     } | 
|  | 215 | 
|  | 216     @Override | 
|  | 217     public void onReceivedIcon(WebView view, Bitmap icon) | 
|  | 218     { | 
|  | 219       if (extWebChromeClient != null) | 
|  | 220       { | 
|  | 221         extWebChromeClient.onReceivedIcon(view, icon); | 
|  | 222       } | 
|  | 223     } | 
|  | 224 | 
|  | 225     @Override | 
|  | 226     public void onReceivedTouchIconUrl(WebView view, String url, boolean precomp
      osed) | 
|  | 227     { | 
|  | 228       if (extWebChromeClient != null) | 
|  | 229       { | 
|  | 230         extWebChromeClient.onReceivedTouchIconUrl(view, url, precomposed); | 
|  | 231       } | 
|  | 232     } | 
|  | 233 | 
|  | 234     @Override | 
|  | 235     public void onShowCustomView(View view, CustomViewCallback callback) | 
|  | 236     { | 
|  | 237       if (extWebChromeClient != null) | 
|  | 238       { | 
|  | 239         extWebChromeClient.onShowCustomView(view, callback); | 
|  | 240       } | 
|  | 241     } | 
|  | 242 | 
|  | 243     @Override | 
|  | 244     public void onShowCustomView(View view, int requestedOrientation, CustomView
      Callback callback) | 
|  | 245     { | 
|  | 246       if (extWebChromeClient != null) | 
|  | 247       { | 
|  | 248         extWebChromeClient.onShowCustomView(view, requestedOrientation, callback
      ); | 
|  | 249       } | 
|  | 250     } | 
|  | 251 | 
|  | 252     @Override | 
|  | 253     public void onHideCustomView() | 
|  | 254     { | 
|  | 255       if (extWebChromeClient != null) | 
|  | 256       { | 
|  | 257         extWebChromeClient.onHideCustomView(); | 
|  | 258       } | 
|  | 259     } | 
|  | 260 | 
|  | 261     @Override | 
|  | 262     public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUser
      Gesture, | 
|  | 263                                   Message resultMsg) | 
|  | 264     { | 
|  | 265       if (extWebChromeClient != null) | 
|  | 266       { | 
|  | 267         return extWebChromeClient.onCreateWindow(view, isDialog, isUserGesture, 
      resultMsg); | 
|  | 268       } | 
|  | 269       else | 
|  | 270       { | 
|  | 271         return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg); | 
|  | 272       } | 
|  | 273     } | 
|  | 274 | 
|  | 275     @Override | 
|  | 276     public void onRequestFocus(WebView view) | 
|  | 277     { | 
|  | 278       if (extWebChromeClient != null) | 
|  | 279       { | 
|  | 280         extWebChromeClient.onRequestFocus(view); | 
|  | 281       } | 
|  | 282     } | 
|  | 283 | 
|  | 284     @Override | 
|  | 285     public void onCloseWindow(WebView window) | 
|  | 286     { | 
|  | 287       if (extWebChromeClient != null) | 
|  | 288       { | 
|  | 289         extWebChromeClient.onCloseWindow(window); | 
|  | 290       } | 
|  | 291     } | 
|  | 292 | 
|  | 293     @Override | 
|  | 294     public boolean onJsAlert(WebView view, String url, String message, JsResult 
      result) | 
|  | 295     { | 
|  | 296       if (extWebChromeClient != null) | 
|  | 297       { | 
|  | 298         return extWebChromeClient.onJsAlert(view, url, message, result); | 
|  | 299       } | 
|  | 300       else | 
|  | 301       { | 
|  | 302         return super.onJsAlert(view, url, message, result); | 
|  | 303       } | 
|  | 304     } | 
|  | 305 | 
|  | 306     @Override | 
|  | 307     public boolean onJsConfirm(WebView view, String url, String message, JsResul
      t result) | 
|  | 308     { | 
|  | 309       if (extWebChromeClient != null) | 
|  | 310       { | 
|  | 311         return extWebChromeClient.onJsConfirm(view, url, message, result); | 
|  | 312       } | 
|  | 313       else | 
|  | 314       { | 
|  | 315         return super.onJsConfirm(view, url, message, result); | 
|  | 316       } | 
|  | 317     } | 
|  | 318 | 
|  | 319     @Override | 
|  | 320     public boolean onJsPrompt(WebView view, String url, String message, String d
      efaultValue, | 
|  | 321                               JsPromptResult result) | 
|  | 322     { | 
|  | 323       if (extWebChromeClient != null) | 
|  | 324       { | 
|  | 325         return extWebChromeClient.onJsPrompt(view, url, message, defaultValue, r
      esult); | 
|  | 326       } | 
|  | 327       else | 
|  | 328       { | 
|  | 329         return super.onJsPrompt(view, url, message, defaultValue, result); | 
|  | 330       } | 
|  | 331     } | 
|  | 332 | 
|  | 333     @Override | 
|  | 334     public boolean onJsBeforeUnload(WebView view, String url, String message, Js
      Result result) | 
|  | 335     { | 
|  | 336       if (extWebChromeClient != null) | 
|  | 337       { | 
|  | 338         return extWebChromeClient.onJsBeforeUnload(view, url, message, result); | 
|  | 339       } | 
|  | 340       else | 
|  | 341       { | 
|  | 342         return super.onJsBeforeUnload(view, url, message, result); | 
|  | 343       } | 
|  | 344     } | 
|  | 345 | 
|  | 346     @Override | 
|  | 347     public void onExceededDatabaseQuota(String url, String databaseIdentifier, l
      ong quota, | 
|  | 348                                         long estimatedDatabaseSize, long totalQu
      ota, | 
|  | 349                                         WebStorage.QuotaUpdater quotaUpdater) | 
|  | 350     { | 
|  | 351       if (extWebChromeClient != null) | 
|  | 352       { | 
|  | 353         extWebChromeClient.onExceededDatabaseQuota(url, databaseIdentifier, quot
      a, | 
|  | 354           estimatedDatabaseSize, totalQuota, quotaUpdater); | 
|  | 355       } | 
|  | 356       else | 
|  | 357       { | 
|  | 358         super.onExceededDatabaseQuota(url, databaseIdentifier, quota, | 
|  | 359           estimatedDatabaseSize, totalQuota, quotaUpdater); | 
|  | 360       } | 
|  | 361     } | 
|  | 362 | 
|  | 363     @Override | 
|  | 364     public void onReachedMaxAppCacheSize(long requiredStorage, long quota, | 
|  | 365                                          WebStorage.QuotaUpdater quotaUpdater) | 
|  | 366     { | 
|  | 367       if (extWebChromeClient != null) | 
|  | 368       { | 
|  | 369         extWebChromeClient.onReachedMaxAppCacheSize(requiredStorage, quota, quot
      aUpdater); | 
|  | 370       } | 
|  | 371       else | 
|  | 372       { | 
|  | 373         super.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater); | 
|  | 374       } | 
|  | 375     } | 
|  | 376 | 
|  | 377     @Override | 
|  | 378     public void onGeolocationPermissionsShowPrompt(String origin, | 
|  | 379                                                    GeolocationPermissions.Callba
      ck callback) | 
|  | 380     { | 
|  | 381       if (extWebChromeClient != null) | 
|  | 382       { | 
|  | 383         extWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback); | 
|  | 384       } | 
|  | 385       else | 
|  | 386       { | 
|  | 387         super.onGeolocationPermissionsShowPrompt(origin, callback); | 
|  | 388       } | 
|  | 389     } | 
|  | 390 | 
|  | 391     @Override | 
|  | 392     public void onGeolocationPermissionsHidePrompt() | 
|  | 393     { | 
|  | 394       if (extWebChromeClient != null) | 
|  | 395       { | 
|  | 396         extWebChromeClient.onGeolocationPermissionsHidePrompt(); | 
|  | 397       } | 
|  | 398       else | 
|  | 399       { | 
|  | 400         super.onGeolocationPermissionsHidePrompt(); | 
|  | 401       } | 
|  | 402     } | 
|  | 403 | 
|  | 404     @Override | 
|  | 405     public boolean onJsTimeout() | 
|  | 406     { | 
|  | 407       if (extWebChromeClient != null) | 
|  | 408       { | 
|  | 409         return extWebChromeClient.onJsTimeout(); | 
|  | 410       } | 
|  | 411       else | 
|  | 412       { | 
|  | 413         return super.onJsTimeout(); | 
|  | 414       } | 
|  | 415     } | 
|  | 416 | 
|  | 417     @Override | 
|  | 418     public void onConsoleMessage(String message, int lineNumber, String sourceID
      ) | 
|  | 419     { | 
|  | 420       if (extWebChromeClient != null) | 
|  | 421       { | 
|  | 422         extWebChromeClient.onConsoleMessage(message, lineNumber, sourceID); | 
|  | 423       } | 
|  | 424       else | 
|  | 425       { | 
|  | 426         super.onConsoleMessage(message, lineNumber, sourceID); | 
|  | 427       } | 
|  | 428     } | 
|  | 429 | 
|  | 430     @Override | 
|  | 431     public boolean onConsoleMessage(ConsoleMessage consoleMessage) | 
|  | 432     { | 
|  | 433       if (extWebChromeClient != null) | 
|  | 434       { | 
|  | 435         return extWebChromeClient.onConsoleMessage(consoleMessage); | 
|  | 436       } | 
|  | 437       else | 
|  | 438       { | 
|  | 439         return super.onConsoleMessage(consoleMessage); | 
|  | 440       } | 
|  | 441     } | 
|  | 442 | 
|  | 443     @Override | 
|  | 444     public Bitmap getDefaultVideoPoster() | 
|  | 445     { | 
|  | 446       if (extWebChromeClient != null) | 
|  | 447       { | 
|  | 448         return extWebChromeClient.getDefaultVideoPoster(); | 
|  | 449       } | 
|  | 450       else | 
|  | 451       { | 
|  | 452         return super.getDefaultVideoPoster(); | 
|  | 453       } | 
|  | 454     } | 
|  | 455 | 
|  | 456     @Override | 
|  | 457     public View getVideoLoadingProgressView() | 
|  | 458     { | 
|  | 459       if (extWebChromeClient != null) | 
|  | 460       { | 
|  | 461         return extWebChromeClient.getVideoLoadingProgressView(); | 
|  | 462       } | 
|  | 463       else | 
|  | 464       { | 
|  | 465         return super.getVideoLoadingProgressView(); | 
|  | 466       } | 
|  | 467     } | 
|  | 468 | 
|  | 469     @Override | 
|  | 470     public void getVisitedHistory(ValueCallback<String[]> callback) | 
|  | 471     { | 
|  | 472       if (extWebChromeClient != null) | 
|  | 473       { | 
|  | 474         extWebChromeClient.getVisitedHistory(callback); | 
|  | 475       } | 
|  | 476       else | 
|  | 477       { | 
|  | 478         super.getVisitedHistory(callback); | 
|  | 479       } | 
|  | 480     } | 
|  | 481 | 
|  | 482     @Override | 
|  | 483     public void onProgressChanged(WebView view, int newProgress) | 
|  | 484     { | 
|  | 485       d("Loading progress=" + newProgress + "%"); | 
|  | 486 | 
|  | 487       // addDomListener is changed to 'false' in `setAddDomListener` invoked fro
      m injected JS | 
|  | 488       if (getAddDomListener() && loadError == null && injectJs != null) | 
|  | 489       { | 
|  | 490         runScript(injectJs); | 
|  | 491 | 
|  | 492         if (allowDraw && loading) | 
|  | 493         { | 
|  | 494           startPreventDrawing(); | 
|  | 495         } | 
|  | 496       } | 
|  | 497 | 
|  | 498       if (extWebChromeClient != null) | 
|  | 499       { | 
|  | 500         extWebChromeClient.onProgressChanged(view, newProgress); | 
|  | 501       } | 
|  | 502     } | 
|  | 503   }; | 
|  | 504 | 
|  | 505   /** | 
|  | 506    * Default (in some conditions) start redraw delay after DOM modified with inj
      ected JS (millis) | 
|  | 507    */ | 
|  | 508   public static final int ALLOW_DRAW_DELAY = 200; | 
|  | 509   /* | 
|  | 510      The value could be different for devices and completely unclear why we need
       it and | 
|  | 511      how to measure actual value | 
|  | 512   */ | 
|  | 513 | 
|  | 514   private int allowDrawDelay = ALLOW_DRAW_DELAY; | 
|  | 515 | 
|  | 516   public int getAllowDrawDelay() | 
|  | 517   { | 
|  | 518     return allowDrawDelay; | 
|  | 519   } | 
|  | 520 | 
|  | 521   /** | 
|  | 522    * Set start redraw delay after DOM modified with injected JS | 
|  | 523    * (used to prevent flickering after 'DOM ready') | 
|  | 524    * @param allowDrawDelay delay (in millis) | 
|  | 525    */ | 
|  | 526   public void setAllowDrawDelay(int allowDrawDelay) | 
|  | 527   { | 
|  | 528     if (allowDrawDelay < 0) | 
|  | 529       throw new IllegalArgumentException("Negative value is not allowed"); | 
|  | 530 | 
|  | 531     this.allowDrawDelay = allowDrawDelay; | 
|  | 532   } | 
|  | 533 | 
|  | 534   private WebViewClient extWebViewClient; | 
|  | 535 | 
|  | 536   @Override | 
|  | 537   public void setWebViewClient(WebViewClient client) | 
|  | 538   { | 
|  | 539     extWebViewClient = client; | 
|  | 540   } | 
|  | 541 | 
|  | 542   private static final Pattern RE_JS = Pattern.compile("\\.js$", Pattern.CASE_IN
      SENSITIVE); | 
|  | 543   private static final Pattern RE_CSS = Pattern.compile("\\.css$", Pattern.CASE_
      INSENSITIVE); | 
|  | 544   private static final Pattern RE_IMAGE = Pattern.compile("\\.(?:gif|png|jpe?g|b
      mp|ico)$", Pattern.CASE_INSENSITIVE); | 
|  | 545   private static final Pattern RE_FONT = Pattern.compile("\\.(?:ttf|woff)$", Pat
      tern.CASE_INSENSITIVE); | 
|  | 546   private static final Pattern RE_HTML = Pattern.compile("\\.html?$", Pattern.CA
      SE_INSENSITIVE); | 
|  | 547 | 
|  | 548   private WebViewClient intWebViewClient = new WebViewClient() | 
|  | 549   { | 
|  | 550     @Override | 
|  | 551     public boolean shouldOverrideUrlLoading(WebView view, String url) | 
|  | 552     { | 
|  | 553       if (extWebViewClient != null) | 
|  | 554       { | 
|  | 555         return extWebViewClient.shouldOverrideUrlLoading(view, url); | 
|  | 556       } | 
|  | 557       else | 
|  | 558       { | 
|  | 559         return super.shouldOverrideUrlLoading(view, url); | 
|  | 560       } | 
|  | 561     } | 
|  | 562 | 
|  | 563     @Override | 
|  | 564     public void onPageStarted(WebView view, String url, Bitmap favicon) | 
|  | 565     { | 
|  | 566       if (extWebViewClient != null) | 
|  | 567       { | 
|  | 568         extWebViewClient.onPageStarted(view, url, favicon); | 
|  | 569       } | 
|  | 570       else | 
|  | 571       { | 
|  | 572         super.onPageStarted(view, url, favicon); | 
|  | 573       } | 
|  | 574     } | 
|  | 575 | 
|  | 576     @Override | 
|  | 577     public void onPageFinished(WebView view, String url) | 
|  | 578     { | 
|  | 579       loading = false; | 
|  | 580       if (extWebViewClient != null) | 
|  | 581       { | 
|  | 582         extWebViewClient.onPageFinished(view, url); | 
|  | 583       } | 
|  | 584       else | 
|  | 585       { | 
|  | 586         super.onPageFinished(view, url); | 
|  | 587       } | 
|  | 588     } | 
|  | 589 | 
|  | 590     @Override | 
|  | 591     public void onLoadResource(WebView view, String url) | 
|  | 592     { | 
|  | 593       if (extWebViewClient != null) | 
|  | 594       { | 
|  | 595         extWebViewClient.onLoadResource(view, url); | 
|  | 596       } | 
|  | 597       else | 
|  | 598       { | 
|  | 599         super.onLoadResource(view, url); | 
|  | 600       } | 
|  | 601     } | 
|  | 602 | 
|  | 603     @Override | 
|  | 604     public void onTooManyRedirects(WebView view, Message cancelMsg, Message cont
      inueMsg) | 
|  | 605     { | 
|  | 606       if (extWebViewClient != null) | 
|  | 607       { | 
|  | 608         extWebViewClient.onTooManyRedirects(view, cancelMsg, continueMsg); | 
|  | 609       } | 
|  | 610       else | 
|  | 611       { | 
|  | 612         super.onTooManyRedirects(view, cancelMsg, continueMsg); | 
|  | 613       } | 
|  | 614     } | 
|  | 615 | 
|  | 616     @Override | 
|  | 617     public void onReceivedError(WebView view, int errorCode, String description,
       String failingUrl) | 
|  | 618     { | 
|  | 619       e("Load error:" + | 
|  | 620         " code=" + errorCode + | 
|  | 621         " with description=" + description + | 
|  | 622         " for url=" + failingUrl); | 
|  | 623       loadError = errorCode; | 
|  | 624 | 
|  | 625       stopAbpLoading(); | 
|  | 626 | 
|  | 627       if (extWebViewClient != null) | 
|  | 628       { | 
|  | 629         extWebViewClient.onReceivedError(view, errorCode, description, failingUr
      l); | 
|  | 630       } | 
|  | 631       else | 
|  | 632       { | 
|  | 633         super.onReceivedError(view, errorCode, description, failingUrl); | 
|  | 634       } | 
|  | 635     } | 
|  | 636 | 
|  | 637     @Override | 
|  | 638     public void onFormResubmission(WebView view, Message dontResend, Message res
      end) | 
|  | 639     { | 
|  | 640       if (extWebViewClient != null) | 
|  | 641       { | 
|  | 642         extWebViewClient.onFormResubmission(view, dontResend, resend); | 
|  | 643       } | 
|  | 644       else | 
|  | 645       { | 
|  | 646         super.onFormResubmission(view, dontResend, resend); | 
|  | 647       } | 
|  | 648     } | 
|  | 649 | 
|  | 650     @Override | 
|  | 651     public void doUpdateVisitedHistory(WebView view, String url, boolean isReloa
      d) | 
|  | 652     { | 
|  | 653       if (extWebViewClient != null) | 
|  | 654       { | 
|  | 655         extWebViewClient.doUpdateVisitedHistory(view, url, isReload); | 
|  | 656       } | 
|  | 657       else | 
|  | 658       { | 
|  | 659         super.doUpdateVisitedHistory(view, url, isReload); | 
|  | 660       } | 
|  | 661     } | 
|  | 662 | 
|  | 663     @Override | 
|  | 664     public void onReceivedSslError(WebView view, SslErrorHandler handler, SslErr
      or error) | 
|  | 665     { | 
|  | 666       if (extWebViewClient != null) | 
|  | 667       { | 
|  | 668         extWebViewClient.onReceivedSslError(view, handler, error); | 
|  | 669       } | 
|  | 670       else | 
|  | 671       { | 
|  | 672         super.onReceivedSslError(view, handler, error); | 
|  | 673       } | 
|  | 674     } | 
|  | 675 | 
|  | 676     @Override | 
|  | 677     public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
       String host, String realm) | 
|  | 678     { | 
|  | 679       if (extWebViewClient != null) | 
|  | 680       { | 
|  | 681         extWebViewClient.onReceivedHttpAuthRequest(view, handler, host, realm); | 
|  | 682       } | 
|  | 683       else | 
|  | 684       { | 
|  | 685         super.onReceivedHttpAuthRequest(view, handler, host, realm); | 
|  | 686       } | 
|  | 687     } | 
|  | 688 | 
|  | 689     @Override | 
|  | 690     public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) | 
|  | 691     { | 
|  | 692       if (extWebViewClient != null) | 
|  | 693       { | 
|  | 694         return extWebViewClient.shouldOverrideKeyEvent(view, event); | 
|  | 695       } | 
|  | 696       else | 
|  | 697       { | 
|  | 698         return super.shouldOverrideKeyEvent(view, event); | 
|  | 699       } | 
|  | 700     } | 
|  | 701 | 
|  | 702     @Override | 
|  | 703     public void onUnhandledKeyEvent(WebView view, KeyEvent event) | 
|  | 704     { | 
|  | 705       if (extWebViewClient != null) | 
|  | 706       { | 
|  | 707         extWebViewClient.onUnhandledKeyEvent(view, event); | 
|  | 708       } | 
|  | 709       else | 
|  | 710       { | 
|  | 711         super.onUnhandledKeyEvent(view, event); | 
|  | 712       } | 
|  | 713     } | 
|  | 714 | 
|  | 715     @Override | 
|  | 716     public void onScaleChanged(WebView view, float oldScale, float newScale) | 
|  | 717     { | 
|  | 718       if (extWebViewClient != null) | 
|  | 719       { | 
|  | 720         extWebViewClient.onScaleChanged(view, oldScale, newScale); | 
|  | 721       } | 
|  | 722       else | 
|  | 723       { | 
|  | 724         super.onScaleChanged(view, oldScale, newScale); | 
|  | 725       } | 
|  | 726     } | 
|  | 727 | 
|  | 728     @Override | 
|  | 729     public void onReceivedLoginRequest(WebView view, String realm, String accoun
      t, String args) | 
|  | 730     { | 
|  | 731       if (extWebViewClient != null) | 
|  | 732       { | 
|  | 733         extWebViewClient.onReceivedLoginRequest(view, realm, account, args); | 
|  | 734       } | 
|  | 735       else | 
|  | 736       { | 
|  | 737         super.onReceivedLoginRequest(view, realm, account, args); | 
|  | 738       } | 
|  | 739     } | 
|  | 740 | 
|  | 741     @Override | 
|  | 742     public WebResourceResponse shouldInterceptRequest(WebView view, String url) 
      { | 
|  | 743       // if dispose() was invoke, but the page is still loading then just let it
       go | 
|  | 744       if (filterEngine == null) | 
|  | 745       { | 
|  | 746         e("FilterEngine already disposed"); | 
|  | 747         return null; | 
|  | 748       } | 
|  | 749 | 
|  | 750       // Determine the content | 
|  | 751       FilterEngine.ContentType contentType = null; | 
|  | 752       if (RE_JS.matcher(url).find()) | 
|  | 753       { | 
|  | 754         contentType = FilterEngine.ContentType.SCRIPT; | 
|  | 755       } | 
|  | 756       else if (RE_CSS.matcher(url).find()) | 
|  | 757       { | 
|  | 758         contentType = FilterEngine.ContentType.STYLESHEET; | 
|  | 759       } | 
|  | 760       else if (RE_IMAGE.matcher(url).find()) | 
|  | 761       { | 
|  | 762         contentType = FilterEngine.ContentType.IMAGE; | 
|  | 763       } | 
|  | 764       else if (RE_FONT.matcher(url).find()) | 
|  | 765       { | 
|  | 766         contentType = FilterEngine.ContentType.FONT; | 
|  | 767       } | 
|  | 768       else if (RE_HTML.matcher(url).find()) | 
|  | 769       { | 
|  | 770         contentType = FilterEngine.ContentType.SUBDOCUMENT; | 
|  | 771       } | 
|  | 772       else | 
|  | 773       { | 
|  | 774         contentType = FilterEngine.ContentType.OTHER; | 
|  | 775       } | 
|  | 776       // Check if we should block ... we sadly do not have the referrer chain he
      re, | 
|  | 777       // might also be hard to get as we do not have HTTP headers here | 
|  | 778       Filter filter = filterEngine.matches(url, contentType, new String[0]); | 
|  | 779       if (filter != null && filter.getType().equals(Filter.Type.BLOCKING)) | 
|  | 780       { | 
|  | 781         w("Blocked loading " + url); | 
|  | 782         // If we should block, return empty response which results in a 404 | 
|  | 783         return new WebResourceResponse("text/plain", "UTF-8", null); | 
|  | 784       } | 
|  | 785 | 
|  | 786       d("Allowed loading " + url); | 
|  | 787 | 
|  | 788       // Otherwise, continue by returning null | 
|  | 789       return null; | 
|  | 790     } | 
|  | 791   }; | 
|  | 792 | 
|  | 793   private void initAbp() | 
|  | 794   { | 
|  | 795     addJavascriptInterface(this, BRIDGE); | 
|  | 796 | 
|  | 797     super.setWebChromeClient(intWebChromeClient); | 
|  | 798     super.setWebViewClient(intWebViewClient); | 
|  | 799   } | 
|  | 800 | 
|  | 801   /** | 
|  | 802    * Build app info using Android package information | 
|  | 803    * @param context context | 
|  | 804    * @param developmentBuild if it's dev build | 
|  | 805    * @return app info required to build JsEngine | 
|  | 806    */ | 
|  | 807   public static AppInfo buildAppInfo(final Context context, boolean developmentB
      uild) | 
|  | 808   { | 
|  | 809     String version = "0"; | 
|  | 810     try | 
|  | 811     { | 
|  | 812       final PackageInfo info = context.getPackageManager().getPackageInfo(contex
      t.getPackageName(), 0); | 
|  | 813       version = info.versionName; | 
|  | 814       if (developmentBuild) | 
|  | 815         version += "." + info.versionCode; | 
|  | 816     } | 
|  | 817     catch (final PackageManager.NameNotFoundException e) | 
|  | 818     { | 
|  | 819       Log.e(TAG, "Failed to get the application version number", e); | 
|  | 820     } | 
|  | 821     final String sdkVersion = String.valueOf(Build.VERSION.SDK_INT); | 
|  | 822     final String locale = Locale.getDefault().toString().replace('_', '-'); | 
|  | 823 | 
|  | 824     return AppInfo.builder() | 
|  | 825       .setVersion(version) | 
|  | 826       .setApplicationVersion(sdkVersion) | 
|  | 827       .setLocale(locale) | 
|  | 828       .setDevelopmentBuild(developmentBuild) | 
|  | 829       .build(); | 
|  | 830   } | 
|  | 831 | 
|  | 832   /** | 
|  | 833    * Build JsEngine required to build FilterEngine | 
|  | 834    * @param context context | 
|  | 835    * @param developmentBuild if it's dev build | 
|  | 836    * @return JsEngine | 
|  | 837    */ | 
|  | 838   public static JsEngine buildJsEngine(Context context, boolean developmentBuild
      ) | 
|  | 839   { | 
|  | 840     JsEngine jsEngine = new JsEngine(buildAppInfo(context, developmentBuild)); | 
|  | 841     jsEngine.setDefaultFileSystem(context.getCacheDir().getAbsolutePath()); | 
|  | 842     jsEngine.setWebRequest(new AndroidWebRequest(true)); // 'true' because we ne
      ed element hiding | 
|  | 843     return jsEngine; | 
|  | 844   } | 
|  | 845 | 
|  | 846   private void createFilterEngine() | 
|  | 847   { | 
|  | 848     w("Creating FilterEngine"); | 
|  | 849     jsEngine = buildJsEngine(getContext(), debugMode); | 
|  | 850     filterEngine = new FilterEngine(jsEngine); | 
|  | 851     d("FilterEngine created"); | 
|  | 852   } | 
|  | 853 | 
|  | 854   private String url; | 
|  | 855   private String domain; | 
|  | 856   private String injectJs; | 
|  | 857   private CountDownLatch elemHideLatch; | 
|  | 858   private String elemHideSelectorsString; | 
|  | 859   private Object elemHideThreadLockObject = new Object(); | 
|  | 860   private ElemHideThread elemHideThread; | 
|  | 861 | 
|  | 862   private class ElemHideThread extends Thread | 
|  | 863   { | 
|  | 864     private String selectorsString; | 
|  | 865     private CountDownLatch finishedLatch; | 
|  | 866     private AtomicBoolean isCancelled; | 
|  | 867 | 
|  | 868     public ElemHideThread(CountDownLatch finishedLatch) | 
|  | 869     { | 
|  | 870       this.finishedLatch = finishedLatch; | 
|  | 871       isCancelled = new AtomicBoolean(false); | 
|  | 872     } | 
|  | 873 | 
|  | 874     @Override | 
|  | 875     public void run() | 
|  | 876     { | 
|  | 877       try | 
|  | 878       { | 
|  | 879         if (filterEngine == null) | 
|  | 880         { | 
|  | 881           w("FilterEngine already disposed"); | 
|  | 882           selectorsString = EMPTY_ELEMHIDE_ARRAY_STRING; | 
|  | 883         } | 
|  | 884         else | 
|  | 885         { | 
|  | 886           if (filterEngine.isDocumentWhitelisted(url, null) || | 
|  | 887             filterEngine.isElemhideWhitelisted(url, null)) | 
|  | 888           { | 
|  | 889             d(url + " whitelisted"); | 
|  | 890             selectorsString = EMPTY_ELEMHIDE_ARRAY_STRING; | 
|  | 891           } | 
|  | 892           else | 
|  | 893           { | 
|  | 894             d("Requesting elemhide selectors from FilterEngine for " + domain); | 
|  | 895             List<String> selectors = filterEngine.getElementHidingSelectors(doma
      in); | 
|  | 896             d("Finished requesting elemhide selectors, got " + selectors.size())
      ; | 
|  | 897             selectorsString = Utils.stringListToJsonArray(selectors); | 
|  | 898           } | 
|  | 899         } | 
|  | 900       } | 
|  | 901       finally | 
|  | 902       { | 
|  | 903         if (!isCancelled.get()) | 
|  | 904         { | 
|  | 905           finish(selectorsString); | 
|  | 906         } | 
|  | 907       } | 
|  | 908     } | 
|  | 909 | 
|  | 910     private void onFinished() | 
|  | 911     { | 
|  | 912       finishedLatch.countDown(); | 
|  | 913       synchronized (finishedRunnableLockObject) | 
|  | 914       { | 
|  | 915         if (finishedRunnable != null) | 
|  | 916         { | 
|  | 917           finishedRunnable.run(); | 
|  | 918         } | 
|  | 919       } | 
|  | 920     } | 
|  | 921 | 
|  | 922     private void finish(String result) | 
|  | 923     { | 
|  | 924       elemHideSelectorsString = result; | 
|  | 925       onFinished(); | 
|  | 926     } | 
|  | 927 | 
|  | 928     private Object finishedRunnableLockObject = new Object(); | 
|  | 929     private Runnable finishedRunnable; | 
|  | 930 | 
|  | 931     public void setFinishedRunnable(Runnable runnable) | 
|  | 932     { | 
|  | 933       synchronized (finishedRunnableLockObject) | 
|  | 934       { | 
|  | 935         this.finishedRunnable = runnable; | 
|  | 936       } | 
|  | 937     } | 
|  | 938 | 
|  | 939     public void cancel() | 
|  | 940     { | 
|  | 941       w("Cancelling"); | 
|  | 942       isCancelled.set(true); | 
|  | 943 | 
|  | 944       finish(EMPTY_ELEMHIDE_ARRAY_STRING); | 
|  | 945     } | 
|  | 946   } | 
|  | 947 | 
|  | 948   private Runnable elemHideThreadFinishedRunnable = new Runnable() | 
|  | 949   { | 
|  | 950     @Override | 
|  | 951     public void run() | 
|  | 952     { | 
|  | 953       synchronized (elemHideThreadLockObject) | 
|  | 954       { | 
|  | 955         elemHideThread = null; | 
|  | 956       } | 
|  | 957     } | 
|  | 958   }; | 
|  | 959 | 
|  | 960   private boolean loading; | 
|  | 961 | 
|  | 962   private void startAbpLoading(String newUrl) | 
|  | 963   { | 
|  | 964     d("Start loading " + newUrl); | 
|  | 965 | 
|  | 966     loading = true; | 
|  | 967     addDomListener = true; | 
|  | 968     elementsHidden = false; | 
|  | 969     loadError = null; | 
|  | 970     url = newUrl; | 
|  | 971 | 
|  | 972     try | 
|  | 973     { | 
|  | 974       domain = Utils.getDomain(url); | 
|  | 975     } | 
|  | 976     catch (URISyntaxException e) | 
|  | 977     { | 
|  | 978       domain = null; | 
|  | 979       e("Failed to extract domain for " + url); | 
|  | 980     } | 
|  | 981 | 
|  | 982     if (filterEngine == null) | 
|  | 983     { | 
|  | 984       createFilterEngine(); | 
|  | 985       disposeFilterEngine = true; | 
|  | 986     } | 
|  | 987 | 
|  | 988     elemHideLatch = new CountDownLatch(1); | 
|  | 989     elemHideThread = new ElemHideThread(elemHideLatch); | 
|  | 990     elemHideThread.setFinishedRunnable(elemHideThreadFinishedRunnable); | 
|  | 991     elemHideThread.start(); | 
|  | 992 | 
|  | 993     getSettings().setJavaScriptEnabled(true); | 
|  | 994     buildInjectJs(); | 
|  | 995   } | 
|  | 996 | 
|  | 997   private void buildInjectJs() | 
|  | 998   { | 
|  | 999     try | 
|  | 1000     { | 
|  | 1001       injectJs = readScriptFile("inject.js").replace(HIDE_TOKEN, readScriptFile(
      "css.js")); | 
|  | 1002     } | 
|  | 1003     catch (IOException e) | 
|  | 1004     { | 
|  | 1005       e("Failed to read script", e); | 
|  | 1006     } | 
|  | 1007   } | 
|  | 1008 | 
|  | 1009   @Override | 
|  | 1010   public void loadUrl(String url) | 
|  | 1011   { | 
|  | 1012     if (loading) | 
|  | 1013     { | 
|  | 1014       stopAbpLoading(); | 
|  | 1015     } | 
|  | 1016     startAbpLoading(url); | 
|  | 1017 | 
|  | 1018     super.loadUrl(url); | 
|  | 1019   } | 
|  | 1020 | 
|  | 1021   @Override | 
|  | 1022   public void loadUrl(String url, Map<String, String> additionalHttpHeaders) | 
|  | 1023   { | 
|  | 1024     startAbpLoading(url); | 
|  | 1025     super.loadUrl(url, additionalHttpHeaders); | 
|  | 1026   } | 
|  | 1027 | 
|  | 1028   @Override | 
|  | 1029   public void loadData(String data, String mimeType, String encoding) | 
|  | 1030   { | 
|  | 1031     startAbpLoading(null); | 
|  | 1032     super.loadData(data, mimeType, encoding); | 
|  | 1033   } | 
|  | 1034 | 
|  | 1035   @Override | 
|  | 1036   public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, 
      String encoding, | 
|  | 1037                                   String historyUrl) | 
|  | 1038   { | 
|  | 1039     startAbpLoading(null); | 
|  | 1040     super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); | 
|  | 1041   } | 
|  | 1042 | 
|  | 1043   @Override | 
|  | 1044   public void stopLoading() | 
|  | 1045   { | 
|  | 1046     stopAbpLoading(); | 
|  | 1047     super.stopLoading(); | 
|  | 1048   } | 
|  | 1049 | 
|  | 1050   private void stopAbpLoading() | 
|  | 1051   { | 
|  | 1052     loading = false; | 
|  | 1053     stopPreventDrawing(); | 
|  | 1054 | 
|  | 1055     synchronized (elemHideThreadLockObject) | 
|  | 1056     { | 
|  | 1057       if (elemHideThread != null) | 
|  | 1058       { | 
|  | 1059         elemHideThread.cancel(); | 
|  | 1060       } | 
|  | 1061     } | 
|  | 1062   } | 
|  | 1063 | 
|  | 1064   private volatile boolean elementsHidden = false; | 
|  | 1065 | 
|  | 1066   // warning: do not rename (used in injected JS by method name) | 
|  | 1067   @JavascriptInterface | 
|  | 1068   public void setElementsHidden(boolean value) { | 
|  | 1069     // invoked with 'true' by JS callback when DOM is loaded | 
|  | 1070     elementsHidden = value; | 
|  | 1071 | 
|  | 1072     // fired on worker thread, but needs to be invoked on main thread | 
|  | 1073     if (value) { | 
|  | 1074 //     handler.post(allowDrawRunnable); | 
|  | 1075 //     should work, but it's not working: | 
|  | 1076 //     the user can see element visible even though it was hidden on dom event | 
|  | 1077 | 
|  | 1078       if (allowDrawDelay > 0) | 
|  | 1079       { | 
|  | 1080         d("Scheduled 'allow drawing' invocation in " + allowDrawDelay + " ms"); | 
|  | 1081       } | 
|  | 1082       handler.postDelayed(allowDrawRunnable, allowDrawDelay); | 
|  | 1083     } | 
|  | 1084   } | 
|  | 1085 | 
|  | 1086   // warning: do not rename (used in injected JS by method name) | 
|  | 1087   @JavascriptInterface | 
|  | 1088   public boolean isElementsHidden() { | 
|  | 1089     return elementsHidden; | 
|  | 1090   } | 
|  | 1091 | 
|  | 1092   @Override | 
|  | 1093   public void onPause() | 
|  | 1094   { | 
|  | 1095     handler.removeCallbacks(allowDrawRunnable); | 
|  | 1096     super.onPause(); | 
|  | 1097   } | 
|  | 1098 | 
|  | 1099   public AdblockWebView(Context context) | 
|  | 1100   { | 
|  | 1101     super(context); | 
|  | 1102     initAbp(); | 
|  | 1103   } | 
|  | 1104 | 
|  | 1105   public AdblockWebView(Context context, AttributeSet attrs) | 
|  | 1106   { | 
|  | 1107     super(context, attrs); | 
|  | 1108     initAbp(); | 
|  | 1109   } | 
|  | 1110 | 
|  | 1111   public AdblockWebView(Context context, AttributeSet attrs, int defStyle) | 
|  | 1112   { | 
|  | 1113     super(context, attrs, defStyle); | 
|  | 1114     initAbp(); | 
|  | 1115   } | 
|  | 1116 | 
|  | 1117   // used to prevent user see flickering for elements to hide | 
|  | 1118   // for some reason it's rendered even if element is hidden on 'dom ready' even
      t | 
|  | 1119   private volatile boolean allowDraw = true; | 
|  | 1120 | 
|  | 1121   @Override | 
|  | 1122   protected void onDraw(Canvas canvas) | 
|  | 1123   { | 
|  | 1124     if (allowDraw) | 
|  | 1125     { | 
|  | 1126       super.onDraw(canvas); | 
|  | 1127     } | 
|  | 1128     else | 
|  | 1129     { | 
|  | 1130       w("Prevent drawing"); | 
|  | 1131       drawEmptyPage(canvas); | 
|  | 1132     } | 
|  | 1133   } | 
|  | 1134 | 
|  | 1135   private void drawEmptyPage(Canvas canvas) | 
|  | 1136   { | 
|  | 1137     // assuming default color is WHITE | 
|  | 1138     canvas.drawColor(Color.WHITE); | 
|  | 1139   } | 
|  | 1140 | 
|  | 1141   private Handler handler = new Handler(); | 
|  | 1142 | 
|  | 1143   protected void startPreventDrawing() | 
|  | 1144   { | 
|  | 1145     w("Start prevent drawing"); | 
|  | 1146 | 
|  | 1147     allowDraw = false; | 
|  | 1148   } | 
|  | 1149 | 
|  | 1150   protected void stopPreventDrawing() | 
|  | 1151   { | 
|  | 1152     d("Stop prevent drawing, invalidating"); | 
|  | 1153 | 
|  | 1154     allowDraw = true; | 
|  | 1155     invalidate(); | 
|  | 1156   } | 
|  | 1157 | 
|  | 1158   private Runnable allowDrawRunnable = new Runnable() { | 
|  | 1159     @Override | 
|  | 1160     public void run() { | 
|  | 1161       stopPreventDrawing(); | 
|  | 1162     } | 
|  | 1163   }; | 
|  | 1164 | 
|  | 1165   private static final String EMPTY_ELEMHIDE_ARRAY_STRING = "[]"; | 
|  | 1166 | 
|  | 1167   // warning: do not rename (used in injected JS by method name) | 
|  | 1168   @JavascriptInterface | 
|  | 1169   public String getElemhideSelectors() | 
|  | 1170   { | 
|  | 1171     try | 
|  | 1172     { | 
|  | 1173       // elemhide selectors list getting is started in startAbpLoad() in backgro
      und thread | 
|  | 1174       d("Waiting for elemhide selectors to be ready"); | 
|  | 1175       elemHideLatch.await(); | 
|  | 1176       d("Elemhide selectors ready"); | 
|  | 1177       return elemHideSelectorsString; | 
|  | 1178     } | 
|  | 1179     catch (InterruptedException e) | 
|  | 1180     { | 
|  | 1181       w("Interrupted, returning empty selectors list"); | 
|  | 1182       return EMPTY_ELEMHIDE_ARRAY_STRING; | 
|  | 1183     } | 
|  | 1184   } | 
|  | 1185 | 
|  | 1186   private void doDispose() | 
|  | 1187   { | 
|  | 1188     w("Disposing jsEngine"); | 
|  | 1189     jsEngine.dispose(); | 
|  | 1190     jsEngine = null; | 
|  | 1191 | 
|  | 1192     w("Disposing filterEngine"); | 
|  | 1193     filterEngine.dispose(); | 
|  | 1194     filterEngine = null; | 
|  | 1195 | 
|  | 1196     disposeFilterEngine = false; | 
|  | 1197   } | 
|  | 1198 | 
|  | 1199   public void dispose() { | 
|  | 1200     d("Dispose invoked"); | 
|  | 1201 | 
|  | 1202     removeJavascriptInterface(BRIDGE); | 
|  | 1203 | 
|  | 1204     if (disposeFilterEngine) | 
|  | 1205     { | 
|  | 1206       synchronized (elemHideThreadLockObject) | 
|  | 1207       { | 
|  | 1208         if (elemHideThread != null) | 
|  | 1209         { | 
|  | 1210           w("Busy with elemhide selectors, delayed disposing scheduled"); | 
|  | 1211           elemHideThread.setFinishedRunnable(new Runnable() | 
|  | 1212           { | 
|  | 1213             @Override | 
|  | 1214             public void run() | 
|  | 1215             { | 
|  | 1216               doDispose(); | 
|  | 1217             } | 
|  | 1218           }); | 
|  | 1219         } | 
|  | 1220         else | 
|  | 1221         { | 
|  | 1222           doDispose(); | 
|  | 1223         } | 
|  | 1224       } | 
|  | 1225     } | 
|  | 1226   } | 
|  | 1227 } | 
| OLD | NEW | 
|---|