Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: libadblockplus-android/src/org/adblockplus/android/AdblockWebView.java

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

Powered by Google App Engine
This is Rietveld