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 29671734: Issue 6265 - Create shared AdblockEngine instance in AdblockWebView in background (Closed)
Patch Set: Created Jan. 17, 2018, 11:48 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
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH 3 * Copyright (C) 2006-present eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 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 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 30 matching lines...) Expand all
41 import android.webkit.WebChromeClient; 41 import android.webkit.WebChromeClient;
42 import android.webkit.WebResourceRequest; // makes android min version to be 21 42 import android.webkit.WebResourceRequest; // makes android min version to be 21
43 import android.webkit.WebResourceResponse; 43 import android.webkit.WebResourceResponse;
44 import android.webkit.WebStorage; 44 import android.webkit.WebStorage;
45 import android.webkit.WebView; 45 import android.webkit.WebView;
46 import android.webkit.WebViewClient; 46 import android.webkit.WebViewClient;
47 47
48 import org.adblockplus.libadblockplus.FilterEngine; 48 import org.adblockplus.libadblockplus.FilterEngine;
49 import org.adblockplus.libadblockplus.Subscription; 49 import org.adblockplus.libadblockplus.Subscription;
50 import org.adblockplus.libadblockplus.android.AdblockEngine; 50 import org.adblockplus.libadblockplus.android.AdblockEngine;
51 import org.adblockplus.libadblockplus.android.AdblockEngineProvider;
52 import org.adblockplus.libadblockplus.android.SingleInstanceEngineProvider;
51 import org.adblockplus.libadblockplus.android.Utils; 53 import org.adblockplus.libadblockplus.android.Utils;
52 54
53 import java.io.IOException; 55 import java.io.IOException;
54 import java.util.Collections; 56 import java.util.Collections;
55 import java.util.HashMap; 57 import java.util.HashMap;
56 import java.util.List; 58 import java.util.List;
57 import java.util.Map; 59 import java.util.Map;
58 import java.util.concurrent.CountDownLatch; 60 import java.util.concurrent.CountDownLatch;
59 import java.util.concurrent.atomic.AtomicBoolean; 61 import java.util.concurrent.atomic.AtomicBoolean;
60 import java.util.regex.Pattern; 62 import java.util.regex.Pattern;
(...skipping 27 matching lines...) Expand all
88 90
89 private static final Pattern RE_JS = Pattern.compile("\\.js$", Pattern.CASE_IN SENSITIVE); 91 private static final Pattern RE_JS = Pattern.compile("\\.js$", Pattern.CASE_IN SENSITIVE);
90 private static final Pattern RE_CSS = Pattern.compile("\\.css$", Pattern.CASE_ INSENSITIVE); 92 private static final Pattern RE_CSS = Pattern.compile("\\.css$", Pattern.CASE_ INSENSITIVE);
91 private static final Pattern RE_IMAGE = Pattern.compile("\\.(?:gif|png|jpe?g|b mp|ico)$", Pattern.CASE_INSENSITIVE); 93 private static final Pattern RE_IMAGE = Pattern.compile("\\.(?:gif|png|jpe?g|b mp|ico)$", Pattern.CASE_INSENSITIVE);
92 private static final Pattern RE_FONT = Pattern.compile("\\.(?:ttf|woff)$", Pat tern.CASE_INSENSITIVE); 94 private static final Pattern RE_FONT = Pattern.compile("\\.(?:ttf|woff)$", Pat tern.CASE_INSENSITIVE);
93 private static final Pattern RE_HTML = Pattern.compile("\\.html?$", Pattern.CA SE_INSENSITIVE); 95 private static final Pattern RE_HTML = Pattern.compile("\\.html?$", Pattern.CA SE_INSENSITIVE);
94 96
95 private volatile boolean addDomListener = true; 97 private volatile boolean addDomListener = true;
96 private boolean adblockEnabled = true; 98 private boolean adblockEnabled = true;
97 private boolean debugMode; 99 private boolean debugMode;
98 private AdblockEngine adblockEngine; 100 private AdblockEngineProvider provider;
99 private boolean disposeEngine;
100 private Integer loadError; 101 private Integer loadError;
101 private int allowDrawDelay = ALLOW_DRAW_DELAY; 102 private int allowDrawDelay = ALLOW_DRAW_DELAY;
102 private WebChromeClient extWebChromeClient; 103 private WebChromeClient extWebChromeClient;
103 private WebViewClient extWebViewClient; 104 private WebViewClient extWebViewClient;
104 private WebViewClient intWebViewClient; 105 private WebViewClient intWebViewClient;
105 private Map<String, String> url2Referrer = Collections.synchronizedMap(new Has hMap<String, String>()); 106 private Map<String, String> url2Referrer = Collections.synchronizedMap(new Has hMap<String, String>());
106 private String url; 107 private String url;
107 private String domain; 108 private String domain;
108 private String injectJs; 109 private String injectJs;
109 private CountDownLatch elemHideLatch; 110 private CountDownLatch elemHideLatch;
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 applyAdblockEnabled(); 178 applyAdblockEnabled();
178 } 179 }
179 180
180 public boolean isDebugMode() 181 public boolean isDebugMode()
181 { 182 {
182 return debugMode; 183 return debugMode;
183 } 184 }
184 185
185 /** 186 /**
186 * Set to true to see debug log output int AdblockWebView and JS console 187 * Set to true to see debug log output int AdblockWebView and JS console
188 * Should be set before first URL loading if using internal AdblockEngineProvi der
187 * @param debugMode is debug mode 189 * @param debugMode is debug mode
188 */ 190 */
189 public void setDebugMode(boolean debugMode) 191 public void setDebugMode(boolean debugMode)
190 { 192 {
191 this.debugMode = debugMode; 193 this.debugMode = debugMode;
192 } 194 }
193 195
194 private void d(String message) 196 private void d(String message)
195 { 197 {
196 if (debugMode) 198 if (debugMode)
(...skipping 28 matching lines...) Expand all
225 .replace(DEBUG_TOKEN, (debugMode ? "" : "//")); 227 .replace(DEBUG_TOKEN, (debugMode ? "" : "//"));
226 } 228 }
227 229
228 private void runScript(String script) 230 private void runScript(String script)
229 { 231 {
230 d("runScript started"); 232 d("runScript started");
231 evaluateJavascript(script, null); 233 evaluateJavascript(script, null);
232 d("runScript finished"); 234 d("runScript finished");
233 } 235 }
234 236
235 public AdblockEngine getAdblockEngine() 237 public void setProvider(final AdblockEngineProvider provider)
236 { 238 {
237 return adblockEngine; 239 if (this.provider != null && provider != null && this.provider == provider)
sergei 2018/01/19 13:56:41 So, if both this.provider and provider are null th
diegocarloslima 2018/01/19 15:56:11 We can add the @NonNull annotation for AdblockEngi
anton 2018/01/22 06:17:36 I'd prefer to fix it by adding `null` check.
238 }
239
240 /**
241 * Set external adblockEngine. A new (internal) is created automatically if no t set
242 * Don't forget to invoke {@link #dispose(Runnable)} later and dispose externa l adblockEngine
243 * @param adblockEngine external adblockEngine
244 */
245 public void setAdblockEngine(final AdblockEngine adblockEngine)
246 {
247 if (this.adblockEngine != null && adblockEngine != null && this.adblockEngin e == adblockEngine)
248 { 240 {
249 return; 241 return;
250 } 242 }
251 243
252 final Runnable setRunnable = new Runnable() 244 final Runnable setRunnable = new Runnable()
253 { 245 {
254 @Override 246 @Override
255 public void run() 247 public void run()
256 { 248 {
257 AdblockWebView.this.adblockEngine = adblockEngine; 249 AdblockWebView.this.provider = provider;
258 AdblockWebView.this.disposeEngine = false; 250 AdblockWebView.this.provider.retain(true); // asynchronously
259 } 251 }
260 }; 252 };
261 253
262 if (this.adblockEngine != null && disposeEngine) 254 if (this.provider != null)
263 { 255 {
264 // as adblockEngine can be busy with elemhide thread we need to use callba ck 256 // as adblockEngine can be busy with elemhide thread we need to use callba ck
265 this.dispose(setRunnable); 257 this.dispose(setRunnable);
266 } 258 }
267 else 259 else
268 { 260 {
269 setRunnable.run(); 261 setRunnable.run();
270 } 262 }
271 } 263 }
272 264
(...skipping 545 matching lines...) Expand 10 before | Expand all | Expand 10 after
818 { 810 {
819 super.onReceivedLoginRequest(view, realm, account, args); 811 super.onReceivedLoginRequest(view, realm, account, args);
820 } 812 }
821 } 813 }
822 814
823 protected WebResourceResponse shouldInterceptRequest( 815 protected WebResourceResponse shouldInterceptRequest(
824 WebView webview, String url, boolean isMainFrame, 816 WebView webview, String url, boolean isMainFrame,
825 boolean isXmlHttpRequest, String[] referrerChainArray) 817 boolean isXmlHttpRequest, String[] referrerChainArray)
826 { 818 {
827 // if dispose() was invoke, but the page is still loading then just let it go 819 // if dispose() was invoke, but the page is still loading then just let it go
828 if (adblockEngine == null) 820 if (provider.getCounter() == 0)
829 { 821 {
830 e("FilterEngine already disposed, allow loading"); 822 e("FilterEngine already disposed, allow loading");
831 823
832 // allow loading by returning null 824 // allow loading by returning null
833 return null; 825 return null;
834 } 826 }
835 827
836 if (isMainFrame) 828 if (isMainFrame)
837 { 829 {
838 // never blocking main frame requests, just subrequests 830 // never blocking main frame requests, just subrequests
839 w(url + " is main frame, allow loading"); 831 w(url + " is main frame, allow loading");
840 832
841 // allow loading by returning null 833 // allow loading by returning null
842 return null; 834 return null;
843 } 835 }
844 836
845 // whitelisted 837 // whitelisted
846 if (adblockEngine.isDomainWhitelisted(url, referrerChainArray)) 838 if (provider.getEngine().isDomainWhitelisted(url, referrerChainArray))
847 { 839 {
848 w(url + " domain is whitelisted, allow loading"); 840 w(url + " domain is whitelisted, allow loading");
849 841
850 // allow loading by returning null 842 // allow loading by returning null
851 return null; 843 return null;
852 } 844 }
853 845
854 if (adblockEngine.isDocumentWhitelisted(url, referrerChainArray)) 846 if (provider.getEngine().isDocumentWhitelisted(url, referrerChainArray))
855 { 847 {
856 w(url + " document is whitelisted, allow loading"); 848 w(url + " document is whitelisted, allow loading");
857 849
858 // allow loading by returning null 850 // allow loading by returning null
859 return null; 851 return null;
860 } 852 }
861 853
862 // determine the content 854 // determine the content
863 FilterEngine.ContentType contentType; 855 FilterEngine.ContentType contentType;
864 if (isXmlHttpRequest) 856 if (isXmlHttpRequest)
(...skipping 22 matching lines...) Expand all
887 { 879 {
888 contentType = FilterEngine.ContentType.SUBDOCUMENT; 880 contentType = FilterEngine.ContentType.SUBDOCUMENT;
889 } 881 }
890 else 882 else
891 { 883 {
892 contentType = FilterEngine.ContentType.OTHER; 884 contentType = FilterEngine.ContentType.OTHER;
893 } 885 }
894 } 886 }
895 887
896 // check if we should block 888 // check if we should block
897 if (adblockEngine.matches(url, contentType, referrerChainArray)) 889 if (provider.getEngine().matches(url, contentType, referrerChainArray))
898 { 890 {
899 w("Blocked loading " + url); 891 w("Blocked loading " + url);
900 892
901 // if we should block, return empty response which results in 'errorLoad ing' callback 893 // if we should block, return empty response which results in 'errorLoad ing' callback
902 return new WebResourceResponse("text/plain", "UTF-8", null); 894 return new WebResourceResponse("text/plain", "UTF-8", null);
903 } 895 }
904 896
905 d("Allowed loading " + url); 897 d("Allowed loading " + url);
906 898
907 // continue by returning null 899 // continue by returning null
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
949 addJavascriptInterface(this, BRIDGE); 941 addJavascriptInterface(this, BRIDGE);
950 initClients(); 942 initClients();
951 } 943 }
952 944
953 private void initClients() 945 private void initClients()
954 { 946 {
955 intWebViewClient = new AdblockWebViewClient(); 947 intWebViewClient = new AdblockWebViewClient();
956 applyAdblockEnabled(); 948 applyAdblockEnabled();
957 } 949 }
958 950
959 private void createAdblockEngine()
960 {
961 w("Creating AdblockEngine");
962
963 // assuming `this.debugMode` can be used as `developmentBuild` value
964 adblockEngine = AdblockEngine
965 .builder(
966 AdblockEngine.generateAppInfo(this.getContext(), debugMode),
967 this.getContext().getDir(AdblockEngine.BASE_PATH_DIRECTORY, Context.MODE _PRIVATE).getAbsolutePath())
968 .enableElementHiding(true)
969 .build();
970 }
971
972 private class ElemHideThread extends Thread 951 private class ElemHideThread extends Thread
973 { 952 {
974 private String selectorsString; 953 private String selectorsString;
975 private CountDownLatch finishedLatch; 954 private CountDownLatch finishedLatch;
976 private AtomicBoolean isCancelled; 955 private AtomicBoolean isCancelled;
977 956
978 public ElemHideThread(CountDownLatch finishedLatch) 957 public ElemHideThread(CountDownLatch finishedLatch)
979 { 958 {
980 this.finishedLatch = finishedLatch; 959 this.finishedLatch = finishedLatch;
981 isCancelled = new AtomicBoolean(false); 960 isCancelled = new AtomicBoolean(false);
982 } 961 }
983 962
984 @Override 963 @Override
985 public void run() 964 public void run()
986 { 965 {
987 try 966 try
988 { 967 {
989 if (adblockEngine == null) 968 if (provider.getCounter() == 0)
sergei 2018/01/19 13:56:41 Can it actually happen and if so, why it cannot ha
anton 2018/01/22 06:17:36 I've just reviewed the code and it seems that it c
990 { 969 {
991 w("FilterEngine already disposed"); 970 w("FilterEngine already disposed");
992 selectorsString = EMPTY_ELEMHIDE_ARRAY_STRING; 971 selectorsString = EMPTY_ELEMHIDE_ARRAY_STRING;
993 } 972 }
994 else 973 else
995 { 974 {
996 String[] referrers = new String[] 975 String[] referrers = new String[]
997 { 976 {
998 url 977 url
999 }; 978 };
1000 979
1001 List<Subscription> subscriptions = adblockEngine.getFilterEngine().get ListedSubscriptions(); 980 List<Subscription> subscriptions = provider
981 .getEngine()
982 .getFilterEngine()
983 .getListedSubscriptions();
984
1002 try 985 try
1003 { 986 {
1004 d("Listed subscriptions: " + subscriptions.size()); 987 d("Listed subscriptions: " + subscriptions.size());
1005 if (debugMode) 988 if (debugMode)
1006 { 989 {
1007 for (Subscription eachSubscription : subscriptions) 990 for (Subscription eachSubscription : subscriptions)
1008 { 991 {
1009 d("Subscribed to " 992 d("Subscribed to "
1010 + (eachSubscription.isDisabled() ? "disabled" : "enabled") 993 + (eachSubscription.isDisabled() ? "disabled" : "enabled")
1011 + " " + eachSubscription); 994 + " " + eachSubscription);
1012 } 995 }
1013 } 996 }
1014 } 997 }
1015 finally 998 finally
1016 { 999 {
1017 for (Subscription eachSubscription : subscriptions) 1000 for (Subscription eachSubscription : subscriptions)
1018 { 1001 {
1019 eachSubscription.dispose(); 1002 eachSubscription.dispose();
1020 } 1003 }
1021 } 1004 }
1022 1005
1023 d("Requesting elemhide selectors from AdblockEngine for " + url + " in " + this); 1006 d("Requesting elemhide selectors from AdblockEngine for " + url + " in " + this);
1024 List<String> selectors = adblockEngine.getElementHidingSelectors(url, domain, referrers); 1007 List<String> selectors = provider
1008 .getEngine()
1009 .getElementHidingSelectors(url, domain, referrers);
1010
1025 d("Finished requesting elemhide selectors, got " + selectors.size() + " in " + this); 1011 d("Finished requesting elemhide selectors, got " + selectors.size() + " in " + this);
1026 selectorsString = Utils.stringListToJsonArray(selectors); 1012 selectorsString = Utils.stringListToJsonArray(selectors);
1027 } 1013 }
1028 } 1014 }
1029 finally 1015 finally
1030 { 1016 {
1031 if (!isCancelled.get()) 1017 if (!isCancelled.get())
1032 { 1018 {
1033 finish(selectorsString); 1019 finish(selectorsString);
1034 } 1020 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
1088 w("elemHideThread set to null"); 1074 w("elemHideThread set to null");
1089 elemHideThread = null; 1075 elemHideThread = null;
1090 } 1076 }
1091 } 1077 }
1092 }; 1078 };
1093 1079
1094 private void initAbpLoading() 1080 private void initAbpLoading()
1095 { 1081 {
1096 getSettings().setJavaScriptEnabled(true); 1082 getSettings().setJavaScriptEnabled(true);
1097 buildInjectJs(); 1083 buildInjectJs();
1084 initProvider();
1085 }
1098 1086
1099 if (adblockEngine == null) 1087 private void initProvider()
sergei 2018/01/19 13:56:41 Would it be better to call it ensureAbpProvider()?
anton 2018/01/22 06:17:36 Acknowledged.
1088 {
1089 // if AdblockWebView works as drop-in replacement for WebView 'provider' is not set.
1090 // Thus AdblockWebView is using SingleInstanceEngineProvider instance
1091 if (provider == null)
1100 { 1092 {
1101 createAdblockEngine(); 1093 setProvider(new SingleInstanceEngineProvider(
1102 disposeEngine = true; 1094 getContext(), AdblockEngine.BASE_PATH_DIRECTORY, debugMode));
1103 } 1095 }
1104 } 1096 }
1105 1097
1106 private void startAbpLoading(String newUrl) 1098 private void startAbpLoading(String newUrl)
1107 { 1099 {
1108 d("Start loading " + newUrl); 1100 d("Start loading " + newUrl);
1109 1101
1110 loading = true; 1102 loading = true;
1111 addDomListener = true; 1103 addDomListener = true;
1112 elementsHidden = false; 1104 elementsHidden = false;
1113 loadError = null; 1105 loadError = null;
1114 url = newUrl; 1106 url = newUrl;
1115 1107
1116 if (url != null && adblockEngine != null) 1108 if (url != null)
1117 { 1109 {
1118 try 1110 try
1119 { 1111 {
1120 domain = adblockEngine.getFilterEngine().getHostFromURL(url); 1112 d("Waiting for adblock engine");
1113 provider.waitForReady();
1114
1115 domain = provider.getEngine().getFilterEngine().getHostFromURL(url);
1121 if (domain == null) 1116 if (domain == null)
1122 { 1117 {
1123 throw new RuntimeException("Failed to extract domain from " + url); 1118 throw new RuntimeException("Failed to extract domain from " + url);
1124 } 1119 }
1125 1120
1126 d("Extracted domain " + domain + " from " + url); 1121 d("Extracted domain " + domain + " from " + url);
1127 } 1122 }
1128 catch (Throwable t) 1123 catch (Throwable t)
1129 { 1124 {
1130 e("Failed to extract domain from " + url, t); 1125 e("Failed to extract domain from " + url, t);
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after
1373 { 1368 {
1374 w("Interrupted, returning empty selectors list"); 1369 w("Interrupted, returning empty selectors list");
1375 return EMPTY_ELEMHIDE_ARRAY_STRING; 1370 return EMPTY_ELEMHIDE_ARRAY_STRING;
1376 } 1371 }
1377 } 1372 }
1378 } 1373 }
1379 1374
1380 private void doDispose() 1375 private void doDispose()
1381 { 1376 {
1382 w("Disposing AdblockEngine"); 1377 w("Disposing AdblockEngine");
1383 adblockEngine.dispose(); 1378 provider.release();
1384 adblockEngine = null;
1385
1386 disposeEngine = false;
1387 } 1379 }
1388 1380
1389 private class DisposeRunnable implements Runnable 1381 private class DisposeRunnable implements Runnable
1390 { 1382 {
1391 private Runnable disposeFinished; 1383 private Runnable disposeFinished;
1392 1384
1393 private DisposeRunnable(Runnable disposeFinished) 1385 private DisposeRunnable(Runnable disposeFinished)
1394 { 1386 {
1395 this.disposeFinished = disposeFinished; 1387 this.disposeFinished = disposeFinished;
1396 } 1388 }
1397 1389
1398 @Override 1390 @Override
1399 public void run() 1391 public void run()
1400 { 1392 {
1401 if (disposeEngine) 1393 doDispose();
1402 {
1403 doDispose();
1404 }
1405 1394
1406 if (disposeFinished != null) 1395 if (disposeFinished != null)
1407 { 1396 {
1408 disposeFinished.run(); 1397 disposeFinished.run();
1409 } 1398 }
1410 } 1399 }
1411 } 1400 }
1412 1401
1413 /** 1402 /**
1414 * Dispose AdblockWebView and internal adblockEngine if it was created 1403 * Dispose AdblockWebView and internal adblockEngine if it was created
1415 * If external AdblockEngine was passed using `setAdblockEngine()` it should b e disposed explicitly 1404 * If external AdblockEngine was passed using `setAdblockEngine()` it should b e disposed explicitly
1416 * Warning: runnable can be invoked from background thread 1405 * Warning: runnable can be invoked from background thread
1417 * @param disposeFinished runnable to run when AdblockWebView is disposed 1406 * @param disposeFinished runnable to run when AdblockWebView is disposed
1418 */ 1407 */
1419 public void dispose(final Runnable disposeFinished) 1408 public void dispose(final Runnable disposeFinished)
1420 { 1409 {
1421 d("Dispose invoked"); 1410 d("Dispose invoked");
1422 1411
1412 if (provider == null)
1413 {
1414 d("No internal AdblockEngineProvider created");
1415 return;
1416 }
1417
1423 stopLoading(); 1418 stopLoading();
1424 1419
1425 removeJavascriptInterface(BRIDGE);
1426 if (!disposeEngine)
1427 {
1428 adblockEngine = null;
1429 }
1430
1431 DisposeRunnable disposeRunnable = new DisposeRunnable(disposeFinished); 1420 DisposeRunnable disposeRunnable = new DisposeRunnable(disposeFinished);
1432 synchronized (elemHideThreadLockObject) 1421 synchronized (elemHideThreadLockObject)
1433 { 1422 {
1434 if (elemHideThread != null) 1423 if (elemHideThread != null)
1435 { 1424 {
1436 w("Busy with elemhide selectors, delayed disposing scheduled"); 1425 w("Busy with elemhide selectors, delayed disposing scheduled");
1437 elemHideThread.setFinishedRunnable(disposeRunnable); 1426 elemHideThread.setFinishedRunnable(disposeRunnable);
1438 } 1427 }
1439 else 1428 else
1440 { 1429 {
1441 disposeRunnable.run(); 1430 disposeRunnable.run();
1442 } 1431 }
1443 } 1432 }
1444 } 1433 }
1445 } 1434 }
OLDNEW

Powered by Google App Engine
This is Rietveld