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

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

Issue 29361445: Issue 4399 - Add WebView inheritor with ad blocking (Closed)
Patch Set: Created Nov. 1, 2016, 12:14 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.libadblockplus.android;
19
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Locale;
26 import java.util.Set;
27
28 import org.adblockplus.libadblockplus.AppInfo;
29 import org.adblockplus.libadblockplus.Filter;
30 import org.adblockplus.libadblockplus.FilterChangeCallback;
31 import org.adblockplus.libadblockplus.FilterEngine;
32 import org.adblockplus.libadblockplus.FilterEngine.ContentType;
33 import org.adblockplus.libadblockplus.JsEngine;
34 import org.adblockplus.libadblockplus.JsValue;
35 import org.adblockplus.libadblockplus.LogSystem;
36 import org.adblockplus.libadblockplus.Notification;
37 import org.adblockplus.libadblockplus.ShowNotificationCallback;
38 import org.adblockplus.libadblockplus.Subscription;
39 import org.adblockplus.libadblockplus.UpdateAvailableCallback;
40 import org.adblockplus.libadblockplus.UpdateCheckDoneCallback;
41
42 import android.content.Context;
43 import android.content.pm.PackageInfo;
44 import android.content.pm.PackageManager.NameNotFoundException;
45 import android.os.Build.VERSION;
46 import android.util.Log;
47
48 public final class AdblockEngine
49 {
50 private static final String TAG = Utils.getTag(AdblockEngine.class);
51
52 private final Context context;
53
54 /*
55 * The fields below are volatile because:
56 *
57 * I encountered JNI related bugs/crashes caused by JNI backed Java objects. I t seemed that under
58 * certain conditions the objects were optimized away which resulted in crashe s when trying to
59 * release the object, sometimes even on access.
60 *
61 * The only solution that really worked was to declare the variables holding t he references
62 * volatile, this seems to prevent the JNI from 'optimizing away' those object s (as a volatile
63 * variable might be changed at any time from any thread).
64 */
65 private volatile JsEngine jsEngine;
66 private volatile FilterEngine filterEngine;
67 private volatile LogSystem logSystem;
68 private volatile AndroidWebRequest webRequest;
69 private volatile UpdateAvailableCallback updateAvailableCallback;
70 private volatile UpdateCheckDoneCallback updateCheckDoneCallback;
71 private volatile FilterChangeCallback filterChangeCallback;
72 private volatile ShowNotificationCallback showNotificationCallback;
73 private final boolean elemhideEnabled;
74
75 private AdblockEngine(final Context context, final boolean enableElemhide)
76 {
77 this.context = context;
78 this.elemhideEnabled = enableElemhide;
79 }
80
81 public static AppInfo generateAppInfo(final Context context, boolean developme ntBuild)
82 {
83 String version = "0";
84 try
85 {
86 final PackageInfo info = context.getPackageManager().getPackageInfo(contex t.getPackageName(), 0);
87 version = info.versionName;
88 if (developmentBuild)
89 version += "." + info.versionCode;
90 }
91 catch (final NameNotFoundException e)
92 {
93 Log.e(TAG, "Failed to get the application version number", e);
94 }
95 final String sdkVersion = String.valueOf(VERSION.SDK_INT);
96 final String locale = Locale.getDefault().toString().replace('_', '-');
97
98 return AppInfo.builder()
99 .setVersion(version)
100 .setApplicationVersion(sdkVersion)
101 .setLocale(locale)
102 .setDevelopmentBuild(developmentBuild)
103 .build();
104 }
105
106 public static final UpdateAvailableCallback UPDATE_AVAILABLE_CALLBACK =
107 new UpdateAvailableCallback()
108 {
109 @Override
110 public void updateAvailableCallback(String url)
111 {
112 Log.d(TAG, "Update available for " + url);
113 }
114 };
115
116 public static final UpdateCheckDoneCallback UPDATE_CHECK_DONE_CALLBACK =
117 new UpdateCheckDoneCallback()
118 {
119 @Override
120 public void updateCheckDoneCallback(String error)
121 {
122 Log.d(TAG, "Update check done, error: " + error);
123 }
124 };
125
126 public static final ShowNotificationCallback SHOW_NOTIFICATION_CALLBACK =
127 new ShowNotificationCallback()
128 {
129 @Override
130 public void showNotificationCallback(Notification jsValue)
131 {
132 Log.d(TAG, "Notification: " + jsValue);
133 }
134 };
135
136 public static final FilterChangeCallback FILTER_CHANGE_CALLBACK =
137 new FilterChangeCallback()
138 {
139 @Override
140 public void filterChangeCallback(String action, JsValue jsValue)
141 {
142 Log.d(TAG, "Filter changed: " + action + (!jsValue.isUndefined() ? ", " + jsValue : ""));
143 }
144 };
145
146 public static AdblockEngine create(final Context context, final AppInfo appInf o,
147 final String basePath, boolean enableElemhi de,
148 UpdateAvailableCallback updateAvailableCall back,
149 UpdateCheckDoneCallback updateCheckDoneCall back,
150 ShowNotificationCallback showNotificationCa llback,
151 FilterChangeCallback filterChangeCallback)
152 {
153 Log.w(TAG, "Create");
154
155 final AdblockEngine engine = new AdblockEngine(context, enableElemhide);
156
157 engine.jsEngine = new JsEngine(appInfo);
158 engine.jsEngine.setDefaultFileSystem(basePath);
159
160 engine.logSystem = new AndroidLogSystem();
161 engine.jsEngine.setLogSystem(engine.logSystem);
162
163 engine.webRequest = new AndroidWebRequest(enableElemhide);
164 engine.jsEngine.setWebRequest(engine.webRequest);
165
166 engine.filterEngine = new FilterEngine(engine.jsEngine);
167
168 engine.updateAvailableCallback = updateAvailableCallback;
169 if (engine.updateAvailableCallback != null)
170 {
171 engine.filterEngine.setUpdateAvailableCallback(updateAvailableCallback);
172 }
173
174 engine.updateCheckDoneCallback = updateCheckDoneCallback;
175
176 engine.showNotificationCallback = showNotificationCallback;
177 if (engine.showNotificationCallback != null)
178 {
179 engine.filterEngine.setShowNotificationCallback(showNotificationCallback);
180 }
181
182 engine.filterChangeCallback = filterChangeCallback;
183 if (engine.filterChangeCallback != null)
184 {
185 engine.filterEngine.setFilterChangeCallback(filterChangeCallback);
186 }
187
188 engine.webRequest.updateSubscriptionURLs(engine.filterEngine);
189
190 return engine;
191 }
192
193 public static AdblockEngine create(final Context context, final AppInfo appInf o,
194 final String basePath, boolean elemhideEnab led)
195 {
196 return create(context, appInfo, basePath, elemhideEnabled,
197 UPDATE_AVAILABLE_CALLBACK, UPDATE_CHECK_DONE_CALLBACK,
198 SHOW_NOTIFICATION_CALLBACK, FILTER_CHANGE_CALLBACK);
199 }
200
201 public void dispose()
202 {
203 Log.w(TAG, "Dispose");
204
205 // Safe disposing (just in case)
206 if (this.filterEngine != null)
207 {
208 this.filterEngine.dispose();
209 this.filterEngine = null;
210 }
211
212 if (this.jsEngine != null)
213 {
214 this.jsEngine.dispose();
215 this.jsEngine = null;
216 }
217
218 if (this.logSystem != null)
219 {
220 this.logSystem.dispose();
221 this.logSystem = null;
222 }
223
224 if (this.webRequest != null)
225 {
226 this.webRequest.dispose();
227 this.webRequest = null;
228 }
229
230 if (this.updateAvailableCallback != null)
231 {
232 this.updateAvailableCallback.dispose();
233 this.updateAvailableCallback = null;
234 }
235
236 if (this.updateCheckDoneCallback != null)
237 {
238 this.updateCheckDoneCallback.dispose();
239 this.updateCheckDoneCallback = null;
240 }
241
242 if (this.filterChangeCallback != null)
243 {
244 this.filterChangeCallback.dispose();
245 this.filterChangeCallback = null;
246 }
247
248 if (this.showNotificationCallback != null)
249 {
250 this.showNotificationCallback.dispose();
251 this.showNotificationCallback = null;
252 }
253 }
254
255 public boolean isFirstRun()
256 {
257 return this.filterEngine.isFirstRun();
258 }
259
260 public boolean isElemhideEnabled()
261 {
262 return this.elemhideEnabled;
263 }
264
265 private static org.adblockplus.libadblockplus.android.Subscription convertJsSu bscription(final Subscription jsSubscription)
266 {
267 final org.adblockplus.libadblockplus.android.Subscription subscription =
268 new org.adblockplus.libadblockplus.android.Subscription();
269
270 subscription.title = jsSubscription.getProperty("title").toString();
271 subscription.url = jsSubscription.getProperty("url").toString();
272
273 return subscription;
274 }
275
276 private static org.adblockplus.libadblockplus.android.Subscription[] convertJs Subscriptions(
277 final List<Subscription> jsSubscriptions)
278 {
279 final org.adblockplus.libadblockplus.android.Subscription[] subscriptions =
280 new org.adblockplus.libadblockplus.android.Subscription[jsSubscriptions.si ze()];
281
282 for (int i = 0; i < subscriptions.length; i++)
283 {
284 subscriptions[i] = convertJsSubscription(jsSubscriptions.get(i));
285 }
286
287 return subscriptions;
288 }
289
290 public org.adblockplus.libadblockplus.android.Subscription[] getRecommendedSub scriptions()
291 {
292 return convertJsSubscriptions(this.filterEngine.fetchAvailableSubscriptions( ));
293 }
294
295 public org.adblockplus.libadblockplus.android.Subscription[] getListedSubscrip tions()
296 {
297 return convertJsSubscriptions(this.filterEngine.getListedSubscriptions());
298 }
299
300 public void clearSubscriptions()
301 {
302 for (final Subscription s : this.filterEngine.getListedSubscriptions())
303 {
304 s.removeFromList();
305 }
306 }
307
308 public void setSubscription(final String url)
309 {
310 clearSubscriptions();
311
312 final Subscription sub = this.filterEngine.getSubscription(url);
313 if (sub != null)
314 {
315 sub.addToList();
316 }
317 }
318
319 public void setSubscriptions(Collection<String> urls)
320 {
321 clearSubscriptions();
322
323 for (String eachUrl : urls)
324 {
325 final Subscription sub = this.filterEngine.getSubscription(eachUrl);
326 if (sub != null)
327 {
328 sub.addToList();
329 }
330 }
331 }
332
333 public void refreshSubscriptions()
334 {
335 for (final Subscription s : this.filterEngine.getListedSubscriptions())
336 {
337 s.updateFilters();
338 }
339 }
340
341 public boolean isAcceptableAdsEnabled()
342 {
343 final String url = getAcceptableAdsSubscriptionURL();
344 List<Subscription> subscriptions = this.filterEngine.getListedSubscriptions( );
345 for (Subscription eachSubscription : subscriptions)
346 {
347 if (eachSubscription.getProperty("url").toString().equals(url))
348 {
349 return true;
350 }
351 }
352 return false;
353 }
354
355 private volatile boolean enabled = true;
356
357 public void setEnabled(final boolean enabled)
358 {
359 this.enabled = enabled;
360 }
361
362 public boolean isEnabled()
363 {
364 return enabled;
365 }
366
367 public String getAcceptableAdsSubscriptionURL()
368 {
369 return this.filterEngine.getPref("subscriptions_exceptionsurl").toString();
370 }
371
372 public void setAcceptableAdsEnabled(final boolean enabled)
373 {
374 final String url = getAcceptableAdsSubscriptionURL();
375 final Subscription sub = this.filterEngine.getSubscription(url);
376 if (sub != null)
377 {
378 if (enabled)
379 {
380 sub.addToList();
381 }
382 else
383 {
384 sub.removeFromList();
385 }
386 }
387 }
388
389 public String getDocumentationLink()
390 {
391 return this.filterEngine.getPref("documentation_link").toString();
392 }
393
394 public boolean matches(final String fullUrl, final ContentType contentType, fi nal String[] referrerChainArray)
395 {
396 if (!enabled)
397 {
398 return false;
399 }
400
401 final Filter filter = this.filterEngine.matches(fullUrl, contentType, referr erChainArray);
402
403 if (filter == null)
404 {
405 return false;
406 }
407
408 // hack: if there is no referrer, block only if filter is domain-specific
409 // (to re-enable in-app ads blocking, proposed on 12.11.2012 Monday meeting)
410 // (documentUrls contains the referrers on Android)
411 try
412 {
413 if (referrerChainArray.length == 0 && (filter.getProperty("text").toString ()).contains("||"))
414 {
415 return false;
416 }
417 } catch (NullPointerException e) {
418 }
419
420 return filter.getType() != Filter.Type.EXCEPTION;
421 }
422
423 public boolean isDocumentWhitelisted(final String url, final String[] referrer ChainArray)
424 {
425 return this.filterEngine.isDocumentWhitelisted(url, referrerChainArray);
426 }
427
428 public boolean isDomainWhitelisted(final String url, final String[] referrerCh ainArray)
429 {
430 if (whitelistedDomains == null)
431 {
432 return false;
433 }
434
435 // using Set to remove duplicates
436 Set<String> referrersAndResourceUrls = new HashSet<String>();
437 if (referrerChainArray != null)
438 {
439 referrersAndResourceUrls.addAll(Arrays.asList(referrerChainArray));
440 }
441 referrersAndResourceUrls.add(url);
442
443 for (String eachUrl : referrersAndResourceUrls)
444 {
445 if (whitelistedDomains.contains(filterEngine.getHostFromURL(eachUrl)))
446 {
447 return true;
448 }
449 }
450
451 return false;
452 }
453
454 public boolean isElemhideWhitelisted(final String url, final String[] referrer ChainArray)
455 {
456 return this.filterEngine.isElemhideWhitelisted(url, referrerChainArray);
457 }
458
459 public List<String> getElementHidingSelectors(final String url, final String d omain, final String[] referrerChainArray)
460 {
461 /*
462 * Issue 3364 (https://issues.adblockplus.org/ticket/3364) introduced the
463 * feature to re-enabled element hiding.
464 *
465 * Nothing changes for Adblock Plus for Android, as `this.elemhideEnabled`
466 * is `false`, which results in an empty list being returned and converted
467 * into a `(String[])null` in AdblockPlus.java, which is the only place
468 * this function here is called from Adblock Plus for Android.
469 *
470 * If element hiding is enabled, then this function now first checks for
471 * possible whitelisting of either the document or element hiding for
472 * the given URL and returns an empty list if so. This is needed to
473 * ensure correct functioning of e.g. acceptable ads.
474 */
475 if (!this.enabled
476 || !this.elemhideEnabled
477 || this.isDomainWhitelisted(url, referrerChainArray)
478 || this.isDocumentWhitelisted(url, referrerChainArray)
479 || this.isElemhideWhitelisted(url, referrerChainArray))
480 {
481 return new ArrayList<String>();
482 }
483 return this.filterEngine.getElementHidingSelectors(domain);
484 }
485
486 public void checkForUpdates()
487 {
488 this.filterEngine.forceUpdateCheck(this.updateCheckDoneCallback);
489 }
490
491 public FilterEngine getFilterEngine()
492 {
493 return this.filterEngine;
494 }
495
496 private List<String> whitelistedDomains;
497
498 public void setWhitelistedDomains(List<String> domains)
499 {
500 this.whitelistedDomains = domains;
501 }
502
503 public List<String> getWhitelistedDomains()
504 {
505 return whitelistedDomains;
506 }
507 }
OLDNEW

Powered by Google App Engine
This is Rietveld