Left: | ||
Right: |
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.libadblockplus.android.webview; | |
19 | |
20 import android.annotation.TargetApi; | |
21 import android.content.Context; | |
22 import android.graphics.Bitmap; | |
23 import android.graphics.Canvas; | |
24 import android.graphics.Color; | |
25 import android.net.http.SslError; | |
26 import android.os.Build; | |
27 import android.os.Handler; | |
28 import android.os.Message; | |
29 import android.util.AttributeSet; | |
30 import android.util.Log; | |
31 import android.view.KeyEvent; | |
32 import android.view.View; | |
33 import android.webkit.ConsoleMessage; | |
34 import android.webkit.GeolocationPermissions; | |
35 import android.webkit.HttpAuthHandler; | |
36 import android.webkit.JavascriptInterface; // makes android min version to be 1 7 | |
37 import android.webkit.JsPromptResult; | |
38 import android.webkit.JsResult; | |
39 import android.webkit.SslErrorHandler; | |
40 import android.webkit.ValueCallback; | |
41 import android.webkit.WebChromeClient; | |
42 import android.webkit.WebResourceRequest; // makes android min version to be 21 | |
43 import android.webkit.WebResourceResponse; | |
44 import android.webkit.WebStorage; | |
45 import android.webkit.WebView; | |
46 import android.webkit.WebViewClient; | |
47 | |
48 import org.adblockplus.libadblockplus.FilterEngine; | |
49 import org.adblockplus.libadblockplus.Subscription; | |
50 import org.adblockplus.libadblockplus.android.AdblockEngine; | |
51 import org.adblockplus.libadblockplus.android.Utils; | |
52 | |
53 import java.io.IOException; | |
54 import java.lang.reflect.Proxy; | |
55 import java.util.ArrayList; | |
56 import java.util.Arrays; | |
diegocarloslima
2016/11/08 16:30:46
ArrayList and Arrays import seems to not being use
anton
2016/11/09 12:30:40
Acknowledged.
| |
57 import java.util.Collections; | |
58 import java.util.HashMap; | |
59 import java.util.List; | |
60 import java.util.Map; | |
61 import java.util.concurrent.CountDownLatch; | |
62 import java.util.concurrent.atomic.AtomicBoolean; | |
63 import java.util.regex.Pattern; | |
64 | |
65 /** | |
66 * WebView with ad blocking | |
67 */ | |
68 public class AdblockWebView extends WebView | |
69 { | |
70 private static final String TAG = Utils.getTag(AdblockWebView.class); | |
71 | |
72 public AdblockWebView(Context context) | |
73 { | |
74 super(context); | |
75 initAbp(); | |
76 } | |
77 | |
78 public AdblockWebView(Context context, AttributeSet attrs) | |
79 { | |
80 super(context, attrs); | |
81 initAbp(); | |
82 } | |
83 | |
84 public AdblockWebView(Context context, AttributeSet attrs, int defStyle) | |
85 { | |
86 super(context, attrs, defStyle); | |
87 initAbp(); | |
88 } | |
89 | |
90 private volatile boolean addDomListener = true; | |
diegocarloslima
2016/11/08 16:30:46
I feel like having member variable declarations mi
| |
91 | |
92 /** | |
93 * Warning: do not rename (used in injected JS by method name) | |
94 * @param value set if one need to set DOM listener | |
95 */ | |
96 @JavascriptInterface | |
97 public void setAddDomListener(boolean value) | |
98 { | |
99 d("addDomListener=" + value); | |
100 this.addDomListener = value; | |
101 } | |
102 | |
103 @JavascriptInterface | |
104 public boolean getAddDomListener() | |
105 { | |
106 return addDomListener; | |
107 } | |
108 | |
109 private boolean adblockEnabled = true; | |
diegocarloslima
2016/11/08 16:30:47
I feel like having member variable declarations mi
| |
110 | |
111 public boolean isAdblockEnabled() | |
112 { | |
113 return adblockEnabled; | |
114 } | |
115 | |
116 private void applyAdblockEnabled() | |
117 { | |
118 super.setWebViewClient(adblockEnabled ? intWebViewClient : extWebViewClient) ; | |
119 super.setWebChromeClient(adblockEnabled ? intWebChromeClient : extWebChromeC lient); | |
120 } | |
121 | |
122 public void setAdblockEnabled(boolean adblockEnabled) | |
123 { | |
124 this.adblockEnabled = adblockEnabled; | |
125 applyAdblockEnabled(); | |
126 } | |
127 | |
128 private WebChromeClient extWebChromeClient; | |
diegocarloslima
2016/11/08 16:30:55
I feel like having member variable declarations mi
| |
129 | |
130 @Override | |
131 public void setWebChromeClient(WebChromeClient client) | |
132 { | |
133 extWebChromeClient = client; | |
134 applyAdblockEnabled(); | |
135 } | |
136 | |
137 private boolean debugMode; | |
diegocarloslima
2016/11/08 16:30:53
I feel like having member variable declarations mi
| |
138 | |
139 public boolean isDebugMode() | |
140 { | |
141 return debugMode; | |
142 } | |
143 | |
144 /** | |
145 * Set to true to see debug log output int AdblockWebView and JS console | |
146 * @param debugMode is debug mode | |
147 */ | |
148 public void setDebugMode(boolean debugMode) | |
149 { | |
150 this.debugMode = debugMode; | |
151 } | |
152 | |
153 private void d(String message) | |
154 { | |
155 if (debugMode) | |
156 { | |
157 Log.d(TAG, message); | |
158 } | |
159 } | |
160 | |
161 private void w(String message) | |
162 { | |
163 if (debugMode) | |
164 { | |
165 Log.w(TAG, message); | |
166 } | |
167 } | |
168 | |
169 private void e(String message, Throwable t) | |
170 { | |
171 Log.e(TAG, message, t); | |
172 } | |
173 | |
174 private void e(String message) | |
175 { | |
176 Log.e(TAG, message); | |
177 } | |
178 | |
diegocarloslima
2016/11/08 16:30:54
I would rather have these log methods in a utility
anton
2016/11/09 12:30:40
i'd prefer to use logging facade like slf4j but i
| |
179 private static final String BRIDGE_TOKEN = "{{BRIDGE}}"; | |
180 private static final String DEBUG_TOKEN = "{{DEBUG}}"; | |
181 private static final String HIDE_TOKEN = "{{HIDE}}"; | |
182 private static final String BRIDGE = "jsBridge"; | |
diegocarloslima
2016/11/08 16:30:45
I feel like having member variable declarations mi
| |
183 | |
184 private String readScriptFile(String filename) throws IOException | |
185 { | |
186 return Utils | |
187 .readAssetAsString(getContext(), filename) | |
188 .replace(BRIDGE_TOKEN, BRIDGE) | |
189 .replace(DEBUG_TOKEN, (debugMode ? "" : "//")); | |
190 } | |
191 | |
192 private void runScript(String script) | |
diegocarloslima
2016/11/08 16:30:45
We might should add @TargetApi annotation here, to
anton
2016/11/09 12:30:39
It will work for android pre-19 too. If it works s
| |
193 { | |
194 d("runScript started"); | |
195 if (Build.VERSION.SDK_INT >= 19) | |
196 { | |
197 evaluateJavascript(script, null); | |
198 } | |
199 else | |
200 { | |
201 loadUrl("javascript:" + script); | |
202 } | |
203 d("runScript finished"); | |
204 } | |
205 | |
206 private AdblockEngine adblockEngine; | |
diegocarloslima
2016/11/08 16:30:53
I feel like having member variable declarations mi
| |
207 | |
208 public AdblockEngine getAdblockEngine() | |
209 { | |
210 return adblockEngine; | |
211 } | |
212 | |
213 private boolean disposeEngine; | |
214 private Integer loadError; | |
diegocarloslima
2016/11/08 16:30:54
I feel like having member variable declarations mi
| |
215 | |
216 /** | |
217 * Set external adblockEngine. A new (internal) is created automatically if no t set | |
218 * Don't forget to invoke {@link #dispose(Runnable)} later and dispose externa l adblockEngine | |
219 * @param adblockEngine external adblockEngine | |
220 */ | |
221 public void setAdblockEngine(final AdblockEngine adblockEngine) | |
222 { | |
223 if (this.adblockEngine != null && adblockEngine != null && this.adblockEngin e == adblockEngine) | |
224 { | |
225 return; | |
226 } | |
227 | |
228 final Runnable setRunnable = new Runnable() | |
229 { | |
230 @Override | |
231 public void run() | |
232 { | |
233 AdblockWebView.this.adblockEngine = adblockEngine; | |
234 AdblockWebView.this.disposeEngine = false; | |
235 } | |
236 }; | |
237 | |
238 if (this.adblockEngine != null && disposeEngine) | |
239 { | |
240 // as adblockEngine can be busy with elemhide thread we need to use callba ck | |
241 this.dispose(setRunnable); | |
242 } | |
243 else | |
244 { | |
245 setRunnable.run(); | |
246 } | |
247 } | |
248 | |
249 private WebChromeClient intWebChromeClient = new WebChromeClient() | |
diegocarloslima
2016/11/08 16:30:47
This annonymous inner class is really big, reducin
anton
2016/11/09 12:30:40
I've been thinking about it.
Unfortunately client
| |
250 { | |
251 @Override | |
252 public void onReceivedTitle(WebView view, String title) | |
253 { | |
254 if (extWebChromeClient != null) | |
255 { | |
256 extWebChromeClient.onReceivedTitle(view, title); | |
257 } | |
258 } | |
259 | |
260 @Override | |
261 public void onReceivedIcon(WebView view, Bitmap icon) | |
262 { | |
263 if (extWebChromeClient != null) | |
264 { | |
265 extWebChromeClient.onReceivedIcon(view, icon); | |
266 } | |
267 } | |
268 | |
269 @Override | |
270 public void onReceivedTouchIconUrl(WebView view, String url, boolean precomp osed) | |
271 { | |
272 if (extWebChromeClient != null) | |
273 { | |
274 extWebChromeClient.onReceivedTouchIconUrl(view, url, precomposed); | |
275 } | |
276 } | |
277 | |
278 @Override | |
279 public void onShowCustomView(View view, CustomViewCallback callback) | |
280 { | |
281 if (extWebChromeClient != null) | |
282 { | |
283 extWebChromeClient.onShowCustomView(view, callback); | |
284 } | |
285 } | |
286 | |
287 @Override | |
288 public void onShowCustomView(View view, int requestedOrientation, CustomView Callback callback) | |
289 { | |
290 if (extWebChromeClient != null) | |
291 { | |
292 extWebChromeClient.onShowCustomView(view, requestedOrientation, callback ); | |
293 } | |
294 } | |
295 | |
296 @Override | |
297 public void onHideCustomView() | |
298 { | |
299 if (extWebChromeClient != null) | |
300 { | |
301 extWebChromeClient.onHideCustomView(); | |
302 } | |
303 } | |
304 | |
305 @Override | |
306 public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUser Gesture, | |
307 Message resultMsg) | |
308 { | |
309 if (extWebChromeClient != null) | |
310 { | |
311 return extWebChromeClient.onCreateWindow(view, isDialog, isUserGesture, resultMsg); | |
312 } | |
313 else | |
314 { | |
315 return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg); | |
316 } | |
317 } | |
318 | |
319 @Override | |
320 public void onRequestFocus(WebView view) | |
321 { | |
322 if (extWebChromeClient != null) | |
323 { | |
324 extWebChromeClient.onRequestFocus(view); | |
325 } | |
326 } | |
327 | |
328 @Override | |
329 public void onCloseWindow(WebView window) | |
330 { | |
331 if (extWebChromeClient != null) | |
332 { | |
333 extWebChromeClient.onCloseWindow(window); | |
334 } | |
335 } | |
336 | |
337 @Override | |
338 public boolean onJsAlert(WebView view, String url, String message, JsResult result) | |
339 { | |
340 if (extWebChromeClient != null) | |
341 { | |
342 return extWebChromeClient.onJsAlert(view, url, message, result); | |
343 } | |
344 else | |
345 { | |
346 return super.onJsAlert(view, url, message, result); | |
347 } | |
348 } | |
349 | |
350 @Override | |
351 public boolean onJsConfirm(WebView view, String url, String message, JsResul t result) | |
352 { | |
353 if (extWebChromeClient != null) | |
354 { | |
355 return extWebChromeClient.onJsConfirm(view, url, message, result); | |
356 } | |
357 else | |
358 { | |
359 return super.onJsConfirm(view, url, message, result); | |
360 } | |
361 } | |
362 | |
363 @Override | |
364 public boolean onJsPrompt(WebView view, String url, String message, String d efaultValue, | |
365 JsPromptResult result) | |
366 { | |
367 if (extWebChromeClient != null) | |
368 { | |
369 return extWebChromeClient.onJsPrompt(view, url, message, defaultValue, r esult); | |
370 } | |
371 else | |
372 { | |
373 return super.onJsPrompt(view, url, message, defaultValue, result); | |
374 } | |
375 } | |
376 | |
377 @Override | |
378 public boolean onJsBeforeUnload(WebView view, String url, String message, Js Result result) | |
379 { | |
380 if (extWebChromeClient != null) | |
381 { | |
382 return extWebChromeClient.onJsBeforeUnload(view, url, message, result); | |
383 } | |
384 else | |
385 { | |
386 return super.onJsBeforeUnload(view, url, message, result); | |
387 } | |
388 } | |
389 | |
390 @Override | |
391 public void onExceededDatabaseQuota(String url, String databaseIdentifier, l ong quota, | |
392 long estimatedDatabaseSize, long totalQu ota, | |
393 WebStorage.QuotaUpdater quotaUpdater) | |
394 { | |
395 if (extWebChromeClient != null) | |
396 { | |
397 extWebChromeClient.onExceededDatabaseQuota(url, databaseIdentifier, quot a, | |
398 estimatedDatabaseSize, totalQuota, quotaUpdater); | |
399 } | |
400 else | |
401 { | |
402 super.onExceededDatabaseQuota(url, databaseIdentifier, quota, | |
403 estimatedDatabaseSize, totalQuota, quotaUpdater); | |
404 } | |
405 } | |
406 | |
407 @Override | |
408 public void onReachedMaxAppCacheSize(long requiredStorage, long quota, | |
409 WebStorage.QuotaUpdater quotaUpdater) | |
410 { | |
411 if (extWebChromeClient != null) | |
412 { | |
413 extWebChromeClient.onReachedMaxAppCacheSize(requiredStorage, quota, quot aUpdater); | |
414 } | |
415 else | |
416 { | |
417 super.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater); | |
418 } | |
419 } | |
420 | |
421 @Override | |
422 public void onGeolocationPermissionsShowPrompt(String origin, | |
423 GeolocationPermissions.Callba ck callback) | |
424 { | |
425 if (extWebChromeClient != null) | |
426 { | |
427 extWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback); | |
428 } | |
429 else | |
430 { | |
431 super.onGeolocationPermissionsShowPrompt(origin, callback); | |
432 } | |
433 } | |
434 | |
435 @Override | |
436 public void onGeolocationPermissionsHidePrompt() | |
437 { | |
438 if (extWebChromeClient != null) | |
439 { | |
440 extWebChromeClient.onGeolocationPermissionsHidePrompt(); | |
441 } | |
442 else | |
443 { | |
444 super.onGeolocationPermissionsHidePrompt(); | |
445 } | |
446 } | |
447 | |
448 @Override | |
449 public boolean onJsTimeout() | |
450 { | |
451 if (extWebChromeClient != null) | |
452 { | |
453 return extWebChromeClient.onJsTimeout(); | |
454 } | |
455 else | |
456 { | |
457 return super.onJsTimeout(); | |
458 } | |
459 } | |
460 | |
461 @Override | |
462 public void onConsoleMessage(String message, int lineNumber, String sourceID ) | |
463 { | |
464 if (extWebChromeClient != null) | |
465 { | |
466 extWebChromeClient.onConsoleMessage(message, lineNumber, sourceID); | |
467 } | |
468 else | |
469 { | |
470 super.onConsoleMessage(message, lineNumber, sourceID); | |
471 } | |
472 } | |
473 | |
474 @Override | |
475 public boolean onConsoleMessage(ConsoleMessage consoleMessage) | |
476 { | |
477 d("JS: level=" + consoleMessage.messageLevel() | |
478 + ", message=\"" + consoleMessage.message() + "\"" | |
479 + ", sourceId=\"" + consoleMessage.sourceId() + "\"" | |
480 + ", line=" + consoleMessage.lineNumber()); | |
481 | |
482 if (extWebChromeClient != null) | |
483 { | |
484 return extWebChromeClient.onConsoleMessage(consoleMessage); | |
485 } | |
486 else | |
487 { | |
488 return super.onConsoleMessage(consoleMessage); | |
489 } | |
490 } | |
491 | |
492 @Override | |
493 public Bitmap getDefaultVideoPoster() | |
494 { | |
495 if (extWebChromeClient != null) | |
496 { | |
497 return extWebChromeClient.getDefaultVideoPoster(); | |
498 } | |
499 else | |
500 { | |
501 return super.getDefaultVideoPoster(); | |
502 } | |
503 } | |
504 | |
505 @Override | |
506 public View getVideoLoadingProgressView() | |
507 { | |
508 if (extWebChromeClient != null) | |
509 { | |
510 return extWebChromeClient.getVideoLoadingProgressView(); | |
511 } | |
512 else | |
513 { | |
514 return super.getVideoLoadingProgressView(); | |
515 } | |
516 } | |
517 | |
518 @Override | |
519 public void getVisitedHistory(ValueCallback<String[]> callback) | |
520 { | |
521 if (extWebChromeClient != null) | |
522 { | |
523 extWebChromeClient.getVisitedHistory(callback); | |
524 } | |
525 else | |
526 { | |
527 super.getVisitedHistory(callback); | |
528 } | |
529 } | |
530 | |
531 @Override | |
532 public void onProgressChanged(WebView view, int newProgress) | |
533 { | |
534 d("Loading progress=" + newProgress + "%"); | |
535 | |
536 // addDomListener is changed to 'false' in `setAddDomListener` invoked fro m injected JS | |
537 if (getAddDomListener() && loadError == null && injectJs != null) | |
538 { | |
539 d("Injecting script"); | |
540 runScript(injectJs); | |
541 | |
542 if (allowDraw && loading) | |
543 { | |
544 startPreventDrawing(); | |
545 } | |
546 } | |
547 | |
548 if (extWebChromeClient != null) | |
549 { | |
550 extWebChromeClient.onProgressChanged(view, newProgress); | |
551 } | |
552 } | |
553 }; | |
554 | |
555 /** | |
556 * Default (in some conditions) start redraw delay after DOM modified with inj ected JS (millis) | |
557 */ | |
558 public static final int ALLOW_DRAW_DELAY = 200; | |
diegocarloslima
2016/11/08 16:30:52
I feel like having member variable declarations mi
| |
559 /* | |
560 The value could be different for devices and completely unclear why we need it and | |
561 how to measure actual value | |
562 */ | |
563 | |
564 private int allowDrawDelay = ALLOW_DRAW_DELAY; | |
diegocarloslima
2016/11/08 16:30:46
I feel like having member variable declarations mi
| |
565 | |
566 public int getAllowDrawDelay() | |
567 { | |
568 return allowDrawDelay; | |
569 } | |
570 | |
571 /** | |
572 * Set start redraw delay after DOM modified with injected JS | |
573 * (used to prevent flickering after 'DOM ready') | |
574 * @param allowDrawDelay delay (in millis) | |
575 */ | |
576 public void setAllowDrawDelay(int allowDrawDelay) | |
577 { | |
578 if (allowDrawDelay < 0) | |
579 { | |
580 throw new IllegalArgumentException("Negative value is not allowed"); | |
581 } | |
582 | |
583 this.allowDrawDelay = allowDrawDelay; | |
584 } | |
585 | |
586 private WebViewClient extWebViewClient; | |
diegocarloslima
2016/11/08 16:30:53
I feel like having member variable declarations mi
| |
587 | |
588 @Override | |
589 public void setWebViewClient(WebViewClient client) | |
590 { | |
591 extWebViewClient = client; | |
592 applyAdblockEnabled(); | |
593 } | |
594 | |
595 private static final Pattern RE_JS = Pattern.compile("\\.js$", Pattern.CASE_IN SENSITIVE); | |
596 private static final Pattern RE_CSS = Pattern.compile("\\.css$", Pattern.CASE_ INSENSITIVE); | |
597 private static final Pattern RE_IMAGE = Pattern.compile("\\.(?:gif|png|jpe?g|b mp|ico)$", Pattern.CASE_INSENSITIVE); | |
598 private static final Pattern RE_FONT = Pattern.compile("\\.(?:ttf|woff)$", Pat tern.CASE_INSENSITIVE); | |
599 private static final Pattern RE_HTML = Pattern.compile("\\.html?$", Pattern.CA SE_INSENSITIVE); | |
diegocarloslima
2016/11/08 16:30:55
I feel like having member variable declarations mi
| |
600 | |
601 private WebViewClient intWebViewClient; | |
diegocarloslima
2016/11/08 16:30:47
I feel like having member variable declarations mi
| |
602 | |
603 /** | |
604 * WebViewClient for API pre 21 | |
605 * (does not have Referrers information) | |
606 */ | |
607 private class AdblockWebViewClient extends WebViewClient | |
diegocarloslima
2016/11/08 16:30:45
This annonymous inner class is really big, reducin
anton
2016/11/09 12:30:41
same reason as for another client. Also they will
| |
608 { | |
609 @Override | |
610 public boolean shouldOverrideUrlLoading(WebView view, String url) | |
611 { | |
612 if (extWebViewClient != null) | |
613 { | |
614 return extWebViewClient.shouldOverrideUrlLoading(view, url); | |
615 } | |
616 else | |
617 { | |
618 return super.shouldOverrideUrlLoading(view, url); | |
619 } | |
620 } | |
621 | |
622 @Override | |
623 public void onPageStarted(WebView view, String url, Bitmap favicon) | |
624 { | |
625 if (loading) | |
626 { | |
627 stopAbpLoading(); | |
628 } | |
629 | |
630 startAbpLoading(url); | |
631 | |
632 if (extWebViewClient != null) | |
633 { | |
634 extWebViewClient.onPageStarted(view, url, favicon); | |
635 } | |
636 else | |
637 { | |
638 super.onPageStarted(view, url, favicon); | |
639 } | |
640 } | |
641 | |
642 @Override | |
643 public void onPageFinished(WebView view, String url) | |
644 { | |
645 loading = false; | |
646 if (extWebViewClient != null) | |
647 { | |
648 extWebViewClient.onPageFinished(view, url); | |
649 } | |
650 else | |
651 { | |
652 super.onPageFinished(view, url); | |
653 } | |
654 } | |
655 | |
656 @Override | |
657 public void onLoadResource(WebView view, String url) | |
658 { | |
659 if (extWebViewClient != null) | |
660 { | |
661 extWebViewClient.onLoadResource(view, url); | |
662 } | |
663 else | |
664 { | |
665 super.onLoadResource(view, url); | |
666 } | |
667 } | |
668 | |
669 @Override | |
670 public void onTooManyRedirects(WebView view, Message cancelMsg, Message cont inueMsg) | |
671 { | |
672 if (extWebViewClient != null) | |
673 { | |
674 extWebViewClient.onTooManyRedirects(view, cancelMsg, continueMsg); | |
675 } | |
676 else | |
677 { | |
678 super.onTooManyRedirects(view, cancelMsg, continueMsg); | |
679 } | |
680 } | |
681 | |
682 @Override | |
683 public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) | |
684 { | |
685 e("Load error:" + | |
686 " code=" + errorCode + | |
687 " with description=" + description + | |
688 " for url=" + failingUrl); | |
689 loadError = errorCode; | |
690 | |
691 stopAbpLoading(); | |
692 | |
693 if (extWebViewClient != null) | |
694 { | |
695 extWebViewClient.onReceivedError(view, errorCode, description, failingUr l); | |
696 } | |
697 else | |
698 { | |
699 super.onReceivedError(view, errorCode, description, failingUrl); | |
700 } | |
701 } | |
702 | |
703 @Override | |
704 public void onFormResubmission(WebView view, Message dontResend, Message res end) | |
705 { | |
706 if (extWebViewClient != null) | |
707 { | |
708 extWebViewClient.onFormResubmission(view, dontResend, resend); | |
709 } | |
710 else | |
711 { | |
712 super.onFormResubmission(view, dontResend, resend); | |
713 } | |
714 } | |
715 | |
716 @Override | |
717 public void doUpdateVisitedHistory(WebView view, String url, boolean isReloa d) | |
718 { | |
719 if (extWebViewClient != null) | |
720 { | |
721 extWebViewClient.doUpdateVisitedHistory(view, url, isReload); | |
722 } | |
723 else | |
724 { | |
725 super.doUpdateVisitedHistory(view, url, isReload); | |
726 } | |
727 } | |
728 | |
729 @Override | |
730 public void onReceivedSslError(WebView view, SslErrorHandler handler, SslErr or error) | |
731 { | |
732 if (extWebViewClient != null) | |
733 { | |
734 extWebViewClient.onReceivedSslError(view, handler, error); | |
735 } | |
736 else | |
737 { | |
738 super.onReceivedSslError(view, handler, error); | |
739 } | |
740 } | |
741 | |
742 @Override | |
743 public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) | |
744 { | |
745 if (extWebViewClient != null) | |
746 { | |
747 extWebViewClient.onReceivedHttpAuthRequest(view, handler, host, realm); | |
748 } | |
749 else | |
750 { | |
751 super.onReceivedHttpAuthRequest(view, handler, host, realm); | |
752 } | |
753 } | |
754 | |
755 @Override | |
756 public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) | |
757 { | |
758 if (extWebViewClient != null) | |
759 { | |
760 return extWebViewClient.shouldOverrideKeyEvent(view, event); | |
761 } | |
762 else | |
763 { | |
764 return super.shouldOverrideKeyEvent(view, event); | |
765 } | |
766 } | |
767 | |
768 @Override | |
769 public void onUnhandledKeyEvent(WebView view, KeyEvent event) | |
770 { | |
771 if (extWebViewClient != null) | |
772 { | |
773 extWebViewClient.onUnhandledKeyEvent(view, event); | |
774 } | |
775 else | |
776 { | |
777 super.onUnhandledKeyEvent(view, event); | |
778 } | |
779 } | |
780 | |
781 @Override | |
782 public void onScaleChanged(WebView view, float oldScale, float newScale) | |
783 { | |
784 if (extWebViewClient != null) | |
785 { | |
786 extWebViewClient.onScaleChanged(view, oldScale, newScale); | |
787 } | |
788 else | |
789 { | |
790 super.onScaleChanged(view, oldScale, newScale); | |
791 } | |
792 } | |
793 | |
794 @Override | |
795 public void onReceivedLoginRequest(WebView view, String realm, String accoun t, String args) | |
796 { | |
797 if (extWebViewClient != null) | |
798 { | |
799 extWebViewClient.onReceivedLoginRequest(view, realm, account, args); | |
800 } | |
801 else | |
802 { | |
803 super.onReceivedLoginRequest(view, realm, account, args); | |
804 } | |
805 } | |
806 | |
807 protected WebResourceResponse shouldInterceptRequest( | |
808 WebView webview, String url, boolean isMainFrame, String[] referrerChainAr ray) | |
809 { | |
810 // if dispose() was invoke, but the page is still loading then just let it go | |
811 if (adblockEngine == null) | |
812 { | |
813 e("FilterEngine already disposed, allow loading"); | |
814 | |
815 // allow loading by returning null | |
816 return null; | |
817 } | |
818 | |
819 if (isMainFrame) | |
820 { | |
821 // never blocking main frame requests, just subrequests | |
822 w(url + " is main frame, allow loading"); | |
823 | |
824 // allow loading by returning null | |
825 return null; | |
826 } | |
827 | |
828 // whitelisted | |
829 if (adblockEngine.isDomainWhitelisted(url, referrerChainArray)) | |
830 { | |
831 w(url + " domain is whitelisted, allow loading"); | |
832 | |
833 // allow loading by returning null | |
834 return null; | |
835 } | |
836 | |
837 if (adblockEngine.isDocumentWhitelisted(url, referrerChainArray)) | |
838 { | |
839 w(url + " document is whitelisted, allow loading"); | |
840 | |
841 // allow loading by returning null | |
842 return null; | |
843 } | |
844 | |
845 // determine the content | |
846 FilterEngine.ContentType contentType; | |
847 if (RE_JS.matcher(url).find()) | |
848 { | |
849 contentType = FilterEngine.ContentType.SCRIPT; | |
850 } | |
851 else if (RE_CSS.matcher(url).find()) | |
852 { | |
853 contentType = FilterEngine.ContentType.STYLESHEET; | |
854 } | |
855 else if (RE_IMAGE.matcher(url).find()) | |
856 { | |
857 contentType = FilterEngine.ContentType.IMAGE; | |
858 } | |
859 else if (RE_FONT.matcher(url).find()) | |
860 { | |
861 contentType = FilterEngine.ContentType.FONT; | |
862 } | |
863 else if (RE_HTML.matcher(url).find()) | |
864 { | |
865 contentType = FilterEngine.ContentType.SUBDOCUMENT; | |
866 } | |
867 else | |
868 { | |
869 contentType = FilterEngine.ContentType.OTHER; | |
870 } | |
871 | |
872 // check if we should block | |
873 if (adblockEngine.matches(url, contentType, referrerChainArray)) | |
874 { | |
875 w("Blocked loading " + url); | |
876 | |
877 // if we should block, return empty response which results in 'errorLoad ing' callback | |
878 return new WebResourceResponse("text/plain", "UTF-8", null); | |
879 } | |
880 | |
881 d("Allowed loading " + url); | |
882 | |
883 // continue by returning null | |
884 return null; | |
885 } | |
886 | |
887 @Override | |
888 public WebResourceResponse shouldInterceptRequest(WebView view, String url) | |
889 { | |
890 /* | |
891 we use hack - injecting Proxy instance instead of IoThreadClient and int ercepting | |
892 `isForMainFrame` argument value, see `initIoThreadClient()`. | |
893 | |
894 it's expected to be not `null` as first invocation goes to | |
895 https://android.googlesource.com/platform/external/chromium_org/+/androi d-4.4_r1/android_webview/java/src/org/chromium/android_webview/AwContents.java#2 35 | |
896 and we intercept it by injecting our proxy instead of `IoThreadClientImp l` | |
897 and then it goes here | |
898 https://android.googlesource.com/platform/external/chromium_org/+/androi d-4.4_r1/android_webview/java/src/org/chromium/android_webview/AwContents.java#2 42 | |
899 */ | |
900 Boolean isMainFrame = IoThreadClientInvocationHandler.isMainFrame; | |
901 if (isMainFrame == null) | |
902 { | |
903 // the only reason for having null here is probably it failed to inject IoThreadClient proxy | |
904 isMainFrame = false; | |
905 } | |
906 | |
907 // sadly we don't have request headers | |
908 // (and referrer until android-21 and new callback with request (instead o f just url) argument) | |
909 String[] referrers = null; | |
910 | |
911 return shouldInterceptRequest(view, url, isMainFrame, referrers); | |
912 } | |
913 } | |
914 | |
915 private void clearReferrers() | |
916 { | |
917 d("Clearing referrers"); | |
918 url2Referrer.clear(); | |
919 } | |
920 | |
921 /** | |
922 * WebViewClient for API 21 and newer | |
923 * (has Referrer since it overrides `shouldInterceptRequest(..., request)` wit h referrer) | |
924 */ | |
925 private class AdblockWebViewClient21 extends AdblockWebViewClient | |
diegocarloslima
2016/11/08 16:30:55
This annonymous inner class is really big, reducin
| |
926 { | |
927 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
928 @Override | |
929 public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceR equest request) | |
930 { | |
931 // here we just trying to fill url -> referrer map | |
932 // blocking/allowing loading will happen in `shouldInterceptRequest(WebVie w,String)` | |
933 String url = request.getUrl().toString(); | |
934 String referrer = request.getRequestHeaders().get("Referer"); | |
935 String[] referrers; | |
936 | |
937 if (referrer != null) | |
938 { | |
939 d("Header referrer for " + url + " is " + referrer); | |
940 url2Referrer.put(url, referrer); | |
941 | |
942 referrers = new String[] | |
943 { | |
944 referrer | |
945 }; | |
946 } | |
947 else | |
948 { | |
949 w("No referrer header for " + url); | |
950 referrers = EMPTY_ARRAY; | |
951 } | |
952 | |
953 return shouldInterceptRequest(view, url, request.isForMainFrame(), referre rs); | |
954 } | |
955 } | |
956 | |
957 private Map<String, String> url2Referrer = Collections.synchronizedMap(new Has hMap<String, String>()); | |
diegocarloslima
2016/11/08 16:30:52
I feel like having member variable declarations mi
| |
958 | |
959 private void initAbp() | |
960 { | |
961 addJavascriptInterface(this, BRIDGE); | |
962 initClients(); | |
963 | |
964 if (Build.VERSION.SDK_INT < 21) | |
965 { | |
966 // we need to inject proxy to hijack `isMainFrame` argument value at pre-a ndroid-21 | |
967 initIoThreadClient(); | |
968 } | |
969 } | |
970 | |
971 private synchronized void initIoThreadClient() | |
972 { | |
973 final Object awContents = ReflectionUtils.extractProperty(this, new String[] | |
974 { | |
975 "mProvider", | |
976 "mAwContents" | |
977 }); | |
978 | |
979 final String ioThreadClientProperty = "mIoThreadClient"; | |
980 final Object originalClient = ReflectionUtils.extractProperty( | |
981 awContents, | |
982 new String[] | |
983 { | |
984 ioThreadClientProperty | |
985 }); | |
986 | |
987 // avoid injecting twice (already injected Proxy instance has another class name) | |
988 if (!originalClient.getClass().getSimpleName().startsWith("$Proxy")) | |
989 { | |
990 Object proxyClient = Proxy.newProxyInstance( | |
991 originalClient.getClass().getClassLoader(), | |
992 originalClient.getClass().getInterfaces(), | |
993 new IoThreadClientInvocationHandler(originalClient)); | |
994 | |
995 // inject proxy instead of original client | |
996 boolean injected = ReflectionUtils.injectProperty(awContents, ioThreadClie ntProperty, | |
997 proxyClient); | |
998 if (injected) | |
999 { | |
1000 // after we injected it as field we should pass it to native code as inv ocation comes from it | |
1001 Integer mNativeAwContents = (Integer) ReflectionUtils.extractProperty(aw Contents, | |
1002 "mNativeAwContents"); | |
1003 Object mWebContentsDelegate = ReflectionUtils.extractProperty(awContents , | |
1004 "mWebContentsDelegate"); | |
1005 Object mContentsClientBridge = ReflectionUtils.extractProperty(awContent s, | |
1006 "mContentsClientBridge"); | |
1007 Object mInterceptNavigationDelegate = ReflectionUtils.extractProperty(aw Contents, | |
1008 "mInterceptNavigationDelegate"); | |
1009 | |
1010 boolean invoked = ReflectionUtils.invokeMethod(awContents, "nativeSetJav aPeers", new Object[] | |
1011 { | |
1012 mNativeAwContents, awContents, mWebContentsDelegate, | |
1013 mContentsClientBridge, proxyClient, mInterceptNavigationDelegate | |
1014 }); | |
1015 if (!invoked) | |
1016 { | |
1017 e("Failed to inject IoThreadClient proxy"); | |
1018 } | |
1019 } | |
1020 } | |
1021 } | |
1022 | |
1023 private void initClients() | |
1024 { | |
1025 if (Build.VERSION.SDK_INT >= 21) | |
1026 { | |
1027 intWebViewClient = new AdblockWebViewClient21(); | |
1028 } | |
1029 else | |
1030 { | |
1031 intWebViewClient = new AdblockWebViewClient(); | |
1032 } | |
1033 applyAdblockEnabled(); | |
1034 } | |
1035 | |
1036 private void createAdblockEngine() | |
1037 { | |
1038 w("Creating AdblockEngine"); | |
1039 | |
1040 // assuming `this.debugMode` can be used as `developmentBuild` value | |
1041 adblockEngine = AdblockEngine.create( | |
1042 this.getContext(), | |
1043 AdblockEngine.generateAppInfo(this.getContext(), debugMode), | |
1044 this.getContext().getCacheDir().getAbsolutePath(), | |
1045 true); | |
1046 } | |
1047 | |
1048 private String url; | |
1049 private String domain; | |
1050 private String injectJs; | |
1051 private CountDownLatch elemHideLatch; | |
1052 private String elemHideSelectorsString; | |
1053 private Object elemHideThreadLockObject = new Object(); | |
1054 private ElemHideThread elemHideThread; | |
diegocarloslima
2016/11/08 16:30:55
I feel like having member variable declarations mi
| |
1055 | |
1056 private static final String[] EMPTY_ARRAY = new String[] | |
1057 { | |
1058 }; | |
diegocarloslima
2016/11/08 16:30:53
I would prefer to go for EMPTY_ARRAY = {};
anton
2016/11/09 12:30:42
Acknowledged.
| |
1059 | |
1060 private class ElemHideThread extends Thread | |
1061 { | |
1062 private String selectorsString; | |
1063 private CountDownLatch finishedLatch; | |
1064 private AtomicBoolean isCancelled; | |
1065 | |
1066 public ElemHideThread(CountDownLatch finishedLatch) | |
1067 { | |
1068 this.finishedLatch = finishedLatch; | |
1069 isCancelled = new AtomicBoolean(false); | |
1070 } | |
1071 | |
1072 @Override | |
1073 public void run() | |
1074 { | |
1075 try | |
1076 { | |
1077 if (adblockEngine == null) | |
1078 { | |
1079 w("FilterEngine already disposed"); | |
1080 selectorsString = EMPTY_ELEMHIDE_ARRAY_STRING; | |
1081 } | |
1082 else | |
1083 { | |
1084 String[] referrers = new String[] | |
1085 { | |
1086 url | |
1087 }; | |
1088 | |
1089 List<Subscription> subscriptions = adblockEngine.getFilterEngine().get ListedSubscriptions(); | |
1090 d("Listed subscriptions: " + subscriptions.size()); | |
1091 if (debugMode) | |
1092 { | |
1093 for (Subscription eachSubscription : subscriptions) | |
1094 { | |
1095 d("Subscribed to " + eachSubscription); | |
1096 } | |
1097 } | |
1098 | |
1099 d("Requesting elemhide selectors from AdblockEngine for " + url + " in " + this); | |
1100 List<String> selectors = adblockEngine.getElementHidingSelectors(url, domain, referrers); | |
1101 d("Finished requesting elemhide selectors, got " + selectors.size() + " in " + this); | |
1102 selectorsString = Utils.stringListToJsonArray(selectors); | |
1103 } | |
1104 } | |
1105 finally | |
1106 { | |
1107 if (!isCancelled.get()) | |
1108 { | |
1109 finish(selectorsString); | |
1110 } | |
1111 else | |
1112 { | |
1113 w("This thread is cancelled, exiting silently " + this); | |
1114 } | |
1115 } | |
1116 } | |
1117 | |
1118 private void onFinished() | |
1119 { | |
1120 finishedLatch.countDown(); | |
1121 synchronized (finishedRunnableLockObject) | |
1122 { | |
1123 if (finishedRunnable != null) | |
1124 { | |
1125 finishedRunnable.run(); | |
1126 } | |
1127 } | |
1128 } | |
1129 | |
1130 private void finish(String result) | |
1131 { | |
1132 d("Setting elemhide string " + result.length() + " bytes"); | |
1133 elemHideSelectorsString = result; | |
1134 onFinished(); | |
1135 } | |
1136 | |
1137 private Object finishedRunnableLockObject = new Object(); | |
diegocarloslima
2016/11/08 16:30:54
Lock objects for synchronized statements should be
anton
2016/11/09 12:30:41
Acknowledged.
| |
1138 private Runnable finishedRunnable; | |
1139 | |
1140 public void setFinishedRunnable(Runnable runnable) | |
1141 { | |
1142 synchronized (finishedRunnableLockObject) | |
1143 { | |
1144 this.finishedRunnable = runnable; | |
1145 } | |
1146 } | |
1147 | |
1148 public void cancel() | |
1149 { | |
1150 w("Cancelling elemhide thread " + this); | |
1151 isCancelled.set(true); | |
1152 | |
1153 finish(EMPTY_ELEMHIDE_ARRAY_STRING); | |
1154 } | |
1155 } | |
1156 | |
1157 private Runnable elemHideThreadFinishedRunnable = new Runnable() | |
1158 { | |
1159 @Override | |
1160 public void run() | |
1161 { | |
1162 synchronized (elemHideThreadLockObject) | |
1163 { | |
1164 w("elemHideThread set to null"); | |
1165 elemHideThread = null; | |
1166 } | |
1167 } | |
1168 }; | |
1169 | |
1170 private boolean loading; | |
diegocarloslima
2016/11/08 16:30:52
I feel like having member variable declarations mi
| |
1171 | |
1172 private void initAbpLoading() | |
1173 { | |
1174 getSettings().setJavaScriptEnabled(true); | |
1175 buildInjectJs(); | |
1176 | |
1177 if (adblockEngine == null) | |
1178 { | |
1179 createAdblockEngine(); | |
1180 disposeEngine = true; | |
1181 } | |
1182 } | |
1183 | |
1184 private void startAbpLoading(String newUrl) | |
1185 { | |
1186 d("Start loading " + newUrl); | |
1187 | |
1188 loading = true; | |
1189 addDomListener = true; | |
1190 elementsHidden = false; | |
1191 loadError = null; | |
1192 url = newUrl; | |
1193 | |
1194 if (url != null && adblockEngine != null) | |
1195 { | |
1196 try | |
1197 { | |
1198 domain = adblockEngine.getFilterEngine().getHostFromURL(url); | |
1199 if (domain == null) | |
1200 { | |
1201 throw new RuntimeException("Failed to extract domain from " + url); | |
1202 } | |
1203 | |
1204 d("Extracted domain " + domain + " from " + url); | |
1205 } | |
1206 catch (Throwable t) | |
1207 { | |
1208 e("Failed to extract domain from " + url, t); | |
1209 } | |
1210 | |
1211 elemHideLatch = new CountDownLatch(1); | |
1212 elemHideThread = new ElemHideThread(elemHideLatch); | |
1213 elemHideThread.setFinishedRunnable(elemHideThreadFinishedRunnable); | |
1214 elemHideThread.start(); | |
1215 } | |
1216 else | |
1217 { | |
1218 elemHideLatch = null; | |
1219 } | |
1220 } | |
1221 | |
1222 private void buildInjectJs() | |
1223 { | |
1224 try | |
1225 { | |
1226 if (injectJs == null) | |
1227 { | |
1228 injectJs = readScriptFile("inject.js").replace(HIDE_TOKEN, readScriptFil e("css.js")); | |
1229 } | |
1230 } | |
1231 catch (IOException e) | |
1232 { | |
1233 e("Failed to read script", e); | |
1234 } | |
1235 } | |
1236 | |
1237 @Override | |
1238 public void goBack() | |
1239 { | |
1240 if (loading) | |
1241 { | |
1242 stopAbpLoading(); | |
1243 } | |
1244 | |
1245 super.goBack(); | |
1246 } | |
1247 | |
1248 @Override | |
1249 public void goForward() | |
1250 { | |
1251 if (loading) | |
1252 { | |
1253 stopAbpLoading(); | |
1254 } | |
1255 | |
1256 super.goForward(); | |
1257 } | |
1258 | |
1259 @Override | |
1260 public void reload() | |
1261 { | |
1262 initAbpLoading(); | |
1263 | |
1264 if (loading) | |
1265 { | |
1266 stopAbpLoading(); | |
1267 } | |
1268 | |
1269 super.reload(); | |
1270 } | |
1271 | |
1272 @Override | |
1273 public void loadUrl(String url) | |
1274 { | |
1275 initAbpLoading(); | |
1276 | |
1277 if (loading) | |
1278 { | |
1279 stopAbpLoading(); | |
1280 } | |
1281 | |
1282 super.loadUrl(url); | |
1283 } | |
1284 | |
1285 @Override | |
1286 public void loadUrl(String url, Map<String, String> additionalHttpHeaders) | |
1287 { | |
1288 initAbpLoading(); | |
1289 | |
1290 if (loading) | |
1291 { | |
1292 stopAbpLoading(); | |
1293 } | |
1294 | |
1295 super.loadUrl(url, additionalHttpHeaders); | |
1296 } | |
1297 | |
1298 @Override | |
1299 public void loadData(String data, String mimeType, String encoding) | |
1300 { | |
1301 initAbpLoading(); | |
1302 | |
1303 if (loading) | |
1304 { | |
1305 stopAbpLoading(); | |
1306 } | |
1307 | |
1308 super.loadData(data, mimeType, encoding); | |
1309 } | |
1310 | |
1311 @Override | |
1312 public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, | |
1313 String historyUrl) | |
1314 { | |
1315 initAbpLoading(); | |
1316 | |
1317 if (loading) | |
1318 { | |
1319 stopAbpLoading(); | |
1320 } | |
1321 | |
1322 super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); | |
1323 } | |
1324 | |
1325 @Override | |
1326 public void stopLoading() | |
1327 { | |
1328 stopAbpLoading(); | |
1329 super.stopLoading(); | |
1330 } | |
1331 | |
1332 private void stopAbpLoading() | |
1333 { | |
1334 d("Stop abp loading"); | |
1335 | |
1336 loading = false; | |
1337 stopPreventDrawing(); | |
1338 clearReferrers(); | |
1339 | |
1340 synchronized (elemHideThreadLockObject) | |
1341 { | |
1342 if (elemHideThread != null) | |
1343 { | |
1344 elemHideThread.cancel(); | |
1345 } | |
1346 } | |
1347 } | |
1348 | |
1349 private volatile boolean elementsHidden = false; | |
diegocarloslima
2016/11/08 16:30:48
I feel like having member variable declarations mi
| |
1350 | |
1351 // warning: do not rename (used in injected JS by method name) | |
1352 @JavascriptInterface | |
1353 public void setElementsHidden(boolean value) | |
1354 { | |
1355 // invoked with 'true' by JS callback when DOM is loaded | |
1356 elementsHidden = value; | |
1357 | |
1358 // fired on worker thread, but needs to be invoked on main thread | |
1359 if (value) | |
1360 { | |
1361 // handler.post(allowDrawRunnable); | |
1362 // should work, but it's not working: | |
1363 // the user can see element visible even though it was hidden on dom event | |
1364 | |
1365 if (allowDrawDelay > 0) | |
1366 { | |
1367 d("Scheduled 'allow drawing' invocation in " + allowDrawDelay + " ms"); | |
1368 } | |
1369 handler.postDelayed(allowDrawRunnable, allowDrawDelay); | |
1370 } | |
1371 } | |
1372 | |
1373 // warning: do not rename (used in injected JS by method name) | |
1374 @JavascriptInterface | |
1375 public boolean isElementsHidden() | |
1376 { | |
1377 return elementsHidden; | |
1378 } | |
1379 | |
1380 @Override | |
1381 public void onPause() | |
1382 { | |
1383 handler.removeCallbacks(allowDrawRunnable); | |
1384 super.onPause(); | |
1385 } | |
1386 | |
1387 // used to prevent user see flickering for elements to hide | |
1388 // for some reason it's rendered even if element is hidden on 'dom ready' even t | |
1389 private volatile boolean allowDraw = true; | |
diegocarloslima
2016/11/08 16:30:54
I feel like having member variable declarations mi
| |
1390 | |
1391 @Override | |
1392 protected void onDraw(Canvas canvas) | |
1393 { | |
1394 if (allowDraw) | |
1395 { | |
1396 super.onDraw(canvas); | |
1397 } | |
1398 else | |
1399 { | |
1400 w("Prevent drawing"); | |
1401 drawEmptyPage(canvas); | |
1402 } | |
1403 } | |
1404 | |
1405 private void drawEmptyPage(Canvas canvas) | |
1406 { | |
1407 // assuming default color is WHITE | |
1408 canvas.drawColor(Color.WHITE); | |
1409 } | |
1410 | |
1411 private Handler handler = new Handler(); | |
diegocarloslima
2016/11/08 16:30:47
I feel like having member variable declarations mi
| |
1412 | |
1413 protected void startPreventDrawing() | |
1414 { | |
1415 w("Start prevent drawing"); | |
1416 | |
1417 allowDraw = false; | |
1418 } | |
1419 | |
1420 protected void stopPreventDrawing() | |
1421 { | |
1422 d("Stop prevent drawing, invalidating"); | |
1423 | |
1424 allowDraw = true; | |
1425 invalidate(); | |
1426 } | |
1427 | |
1428 private Runnable allowDrawRunnable = new Runnable() | |
1429 { | |
1430 @Override | |
1431 public void run() | |
1432 { | |
1433 stopPreventDrawing(); | |
1434 } | |
1435 }; | |
1436 | |
1437 private static final String EMPTY_ELEMHIDE_ARRAY_STRING = "[]"; | |
diegocarloslima
2016/11/08 16:30:48
I feel like having member variable declarations mi
| |
1438 | |
1439 // warning: do not rename (used in injected JS by method name) | |
1440 @JavascriptInterface | |
1441 public String getElemhideSelectors() | |
1442 { | |
1443 if (elemHideLatch == null) | |
1444 { | |
1445 return EMPTY_ELEMHIDE_ARRAY_STRING; | |
1446 } | |
1447 else | |
1448 { | |
1449 try | |
1450 { | |
1451 // elemhide selectors list getting is started in startAbpLoad() in backg round thread | |
1452 d("Waiting for elemhide selectors to be ready"); | |
1453 elemHideLatch.await(); | |
1454 d("Elemhide selectors ready, " + elemHideSelectorsString.length() + " by tes"); | |
1455 | |
1456 clearReferrers(); | |
1457 | |
1458 return elemHideSelectorsString; | |
1459 } | |
1460 catch (InterruptedException e) | |
1461 { | |
1462 w("Interrupted, returning empty selectors list"); | |
1463 return EMPTY_ELEMHIDE_ARRAY_STRING; | |
1464 } | |
1465 } | |
1466 } | |
1467 | |
1468 private void doDispose() | |
1469 { | |
1470 w("Disposing AdblockEngine"); | |
1471 adblockEngine.dispose(); | |
1472 | |
1473 disposeEngine = false; | |
1474 } | |
1475 | |
1476 private class DisposeRunnable implements Runnable | |
1477 { | |
1478 private Runnable disposeFinished; | |
1479 | |
1480 private DisposeRunnable(Runnable disposeFinished) | |
1481 { | |
1482 this.disposeFinished = disposeFinished; | |
1483 } | |
1484 | |
1485 @Override | |
1486 public void run() | |
1487 { | |
1488 if (disposeEngine) | |
1489 { | |
1490 doDispose(); | |
1491 } | |
1492 | |
1493 if (disposeFinished != null) | |
1494 { | |
1495 disposeFinished.run(); | |
1496 } | |
1497 } | |
1498 } | |
1499 | |
1500 /** | |
1501 * Dispose AdblockWebView and internal adblockEngine if it was created | |
1502 * If external AdblockEngine was passed using `setAdblockEngine()` it should b e disposed explicitly | |
1503 * Warning: runnable can be invoked from background thread | |
1504 * @param disposeFinished runnable to run when AdblockWebView is disposed | |
1505 */ | |
1506 public void dispose(final Runnable disposeFinished) | |
1507 { | |
1508 d("Dispose invoked"); | |
1509 | |
1510 stopLoading(); | |
1511 | |
1512 removeJavascriptInterface(BRIDGE); | |
1513 adblockEngine = null; | |
1514 | |
1515 DisposeRunnable disposeRunnable = new DisposeRunnable(disposeFinished); | |
1516 synchronized (elemHideThreadLockObject) | |
1517 { | |
1518 if (elemHideThread != null) | |
1519 { | |
1520 w("Busy with elemhide selectors, delayed disposing scheduled"); | |
1521 elemHideThread.setFinishedRunnable(disposeRunnable); | |
1522 } | |
1523 else | |
1524 { | |
1525 disposeRunnable.run(); | |
1526 } | |
1527 } | |
1528 } | |
1529 } | |
OLD | NEW |