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

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

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

Powered by Google App Engine
This is Rietveld