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

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

Issue 29361445: Issue 4399 - Add WebView inheritor with ad blocking (Closed)
Patch Set: renamings, updated README Created Dec. 19, 2016, 3:04 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: libadblockplus-android/src/org/adblockplus/libadblockplus/android/AdblockEngine.java
diff --git a/libadblockplus-android/src/org/adblockplus/libadblockplus/android/AdblockEngine.java b/libadblockplus-android/src/org/adblockplus/libadblockplus/android/AdblockEngine.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b6138245dddd39e780ee5c851f7b584956c9681
--- /dev/null
+++ b/libadblockplus-android/src/org/adblockplus/libadblockplus/android/AdblockEngine.java
@@ -0,0 +1,473 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2016 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.adblockplus.libadblockplus.android;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import org.adblockplus.libadblockplus.AppInfo;
+import org.adblockplus.libadblockplus.Filter;
+import org.adblockplus.libadblockplus.FilterChangeCallback;
+import org.adblockplus.libadblockplus.FilterEngine;
+import org.adblockplus.libadblockplus.FilterEngine.ContentType;
+import org.adblockplus.libadblockplus.JsEngine;
+import org.adblockplus.libadblockplus.LogSystem;
+import org.adblockplus.libadblockplus.ShowNotificationCallback;
+import org.adblockplus.libadblockplus.Subscription;
+import org.adblockplus.libadblockplus.UpdateAvailableCallback;
+import org.adblockplus.libadblockplus.UpdateCheckDoneCallback;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build.VERSION;
+import android.util.Log;
+
+public final class AdblockEngine
+{
+ private static final String TAG = Utils.getTag(AdblockEngine.class);
+
+ /*
+ * The fields below are volatile because:
+ *
+ * I encountered JNI related bugs/crashes caused by JNI backed Java objects. It seemed that under
+ * certain conditions the objects were optimized away which resulted in crashes when trying to
+ * release the object, sometimes even on access.
+ *
+ * The only solution that really worked was to declare the variables holding the references
+ * volatile, this seems to prevent the JNI from 'optimizing away' those objects (as a volatile
+ * variable might be changed at any time from any thread).
+ */
+ private volatile JsEngine jsEngine;
+ private volatile FilterEngine filterEngine;
+ private volatile LogSystem logSystem;
+ private volatile AndroidWebRequest webRequest;
+ private volatile UpdateAvailableCallback updateAvailableCallback;
+ private volatile UpdateCheckDoneCallback updateCheckDoneCallback;
+ private volatile FilterChangeCallback filterChangeCallback;
+ private volatile ShowNotificationCallback showNotificationCallback;
+ private final boolean elemhideEnabled;
+ private volatile boolean enabled = true;
+ private List<String> whitelistedDomains;
+
+ private AdblockEngine(final boolean enableElemhide)
+ {
+ this.elemhideEnabled = enableElemhide;
+ }
+
+ public static AppInfo generateAppInfo(final Context context, boolean developmentBuild)
+ {
+ String version = "0";
+ try
+ {
+ final PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
+ version = info.versionName;
+ if (developmentBuild)
+ version += "." + info.versionCode;
+ }
+ catch (final NameNotFoundException e)
+ {
+ Log.e(TAG, "Failed to get the application version number", e);
+ }
+ final String sdkVersion = String.valueOf(VERSION.SDK_INT);
+ final String locale = Locale.getDefault().toString().replace('_', '-');
+
+ return AppInfo.builder()
+ .setVersion(version)
+ .setApplicationVersion(sdkVersion)
+ .setLocale(locale)
+ .setDevelopmentBuild(developmentBuild)
+ .build();
+ }
+
+ public static AdblockEngine create(final AppInfo appInfo,
+ final String basePath, boolean enableElemhide,
+ UpdateAvailableCallback updateAvailableCallback,
+ UpdateCheckDoneCallback updateCheckDoneCallback,
+ ShowNotificationCallback showNotificationCallback,
+ FilterChangeCallback filterChangeCallback)
+ {
+ Log.w(TAG, "Create");
+
+ final AdblockEngine engine = new AdblockEngine(enableElemhide);
+
+ engine.jsEngine = new JsEngine(appInfo);
+ engine.jsEngine.setDefaultFileSystem(basePath);
+
+ engine.logSystem = new AndroidLogSystem();
+ engine.jsEngine.setLogSystem(engine.logSystem);
+
+ engine.webRequest = new AndroidWebRequest(enableElemhide);
+ engine.jsEngine.setWebRequest(engine.webRequest);
+
+ engine.filterEngine = new FilterEngine(engine.jsEngine);
+
+ engine.updateAvailableCallback = updateAvailableCallback;
+ if (engine.updateAvailableCallback != null)
+ {
+ engine.filterEngine.setUpdateAvailableCallback(updateAvailableCallback);
+ }
+
+ engine.updateCheckDoneCallback = updateCheckDoneCallback;
+
+ engine.showNotificationCallback = showNotificationCallback;
+ if (engine.showNotificationCallback != null)
+ {
+ engine.filterEngine.setShowNotificationCallback(showNotificationCallback);
+ }
+
+ engine.filterChangeCallback = filterChangeCallback;
+ if (engine.filterChangeCallback != null)
+ {
+ engine.filterEngine.setFilterChangeCallback(filterChangeCallback);
+ }
+
+ engine.webRequest.updateSubscriptionURLs(engine.filterEngine);
+
+ return engine;
+ }
+
+ public static AdblockEngine create(final AppInfo appInfo,
+ final String basePath, boolean elemhideEnabled)
+ {
+ return create(appInfo, basePath, elemhideEnabled, null, null, null, null);
+ }
+
+ public void dispose()
+ {
+ Log.w(TAG, "Dispose");
+
+ if (this.logSystem != null)
+ {
+ this.logSystem.dispose();
+ this.logSystem = null;
+ }
+
+ if (this.webRequest != null)
+ {
+ this.webRequest.dispose();
+ this.webRequest = null;
+ }
+
+ if (this.updateAvailableCallback != null)
+ {
+ if (this.filterEngine != null)
+ {
+ this.filterEngine.removeUpdateAvailableCallback();
+ }
+
+ this.updateAvailableCallback.dispose();
+ this.updateAvailableCallback = null;
+ }
+
+ if (this.updateCheckDoneCallback != null)
+ {
+ this.updateCheckDoneCallback.dispose();
+ this.updateCheckDoneCallback = null;
+ }
+
+ if (this.filterChangeCallback != null)
+ {
+ if (this.filterEngine != null)
+ {
+ this.filterEngine.removeFilterChangeCallback();
+ }
+
+ this.filterChangeCallback.dispose();
+ this.filterChangeCallback = null;
+ }
+
+ if (this.showNotificationCallback != null)
+ {
+ if (this.filterEngine != null)
+ {
+ this.filterEngine.removeShowNotificationCallback();
+ }
+
+ this.showNotificationCallback.dispose();
+ this.showNotificationCallback = null;
+ }
+
+ // Safe disposing (just in case)
+ if (this.filterEngine != null)
+ {
+ this.filterEngine.dispose();
+ this.filterEngine = null;
+ }
+
+ if (this.jsEngine != null)
+ {
+ this.jsEngine.dispose();
+ this.jsEngine = null;
+ }
+ }
+
+ public boolean isFirstRun()
+ {
+ return this.filterEngine.isFirstRun();
+ }
+
+ public boolean isElemhideEnabled()
+ {
+ return this.elemhideEnabled;
+ }
+
+ private static org.adblockplus.libadblockplus.android.Subscription convertJsSubscription(final Subscription jsSubscription)
+ {
+ final org.adblockplus.libadblockplus.android.Subscription subscription =
+ new org.adblockplus.libadblockplus.android.Subscription();
+
+ subscription.title = jsSubscription.getProperty("title").toString();
+ subscription.url = jsSubscription.getProperty("url").toString();
+
+ return subscription;
+ }
+
+ private static org.adblockplus.libadblockplus.android.Subscription[] convertJsSubscriptions(
+ final List<Subscription> jsSubscriptions)
+ {
+ final org.adblockplus.libadblockplus.android.Subscription[] subscriptions =
+ new org.adblockplus.libadblockplus.android.Subscription[jsSubscriptions.size()];
+
+ for (int i = 0; i < subscriptions.length; i++)
+ {
+ subscriptions[i] = convertJsSubscription(jsSubscriptions.get(i));
+ }
+
+ return subscriptions;
+ }
+
+ public org.adblockplus.libadblockplus.android.Subscription[] getRecommendedSubscriptions()
+ {
+ return convertJsSubscriptions(this.filterEngine.fetchAvailableSubscriptions());
+ }
+
+ public org.adblockplus.libadblockplus.android.Subscription[] getListedSubscriptions()
+ {
+ return convertJsSubscriptions(this.filterEngine.getListedSubscriptions());
+ }
+
+ public void clearSubscriptions()
+ {
+ for (final Subscription s : this.filterEngine.getListedSubscriptions())
+ {
+ s.removeFromList();
+ }
+ }
+
+ public void setSubscription(final String url)
+ {
+ clearSubscriptions();
+
+ final Subscription sub = this.filterEngine.getSubscription(url);
+ if (sub != null)
+ {
+ sub.addToList();
+ }
+ }
+
+ public void setSubscriptions(Collection<String> urls)
+ {
+ clearSubscriptions();
+
+ for (String eachUrl : urls)
+ {
+ final Subscription sub = this.filterEngine.getSubscription(eachUrl);
+ if (sub != null)
+ {
+ sub.addToList();
+ }
+ }
+ }
+
+ public void refreshSubscriptions()
+ {
+ for (final Subscription s : this.filterEngine.getListedSubscriptions())
+ {
+ s.updateFilters();
+ }
+ }
+
+ public boolean isAcceptableAdsEnabled()
+ {
+ final String url = getAcceptableAdsSubscriptionURL();
+ List<Subscription> subscriptions = this.filterEngine.getListedSubscriptions();
+ for (Subscription eachSubscription : subscriptions)
+ {
+ if (eachSubscription.getProperty("url").toString().equals(url))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setEnabled(final boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ public String getAcceptableAdsSubscriptionURL()
+ {
+ return this.filterEngine.getPref("subscriptions_exceptionsurl").toString();
+ }
+
+ public void setAcceptableAdsEnabled(final boolean enabled)
+ {
+ final String url = getAcceptableAdsSubscriptionURL();
+ final Subscription sub = this.filterEngine.getSubscription(url);
+ if (sub != null)
+ {
+ if (enabled)
+ {
+ sub.addToList();
+ }
+ else
+ {
+ sub.removeFromList();
+ }
+ }
+ }
+
+ public String getDocumentationLink()
+ {
+ return this.filterEngine.getPref("documentation_link").toString();
+ }
+
+ public boolean matches(final String fullUrl, final ContentType contentType, final String[] referrerChainArray)
+ {
+ if (!enabled)
+ {
+ return false;
+ }
+
+ final Filter filter = this.filterEngine.matches(fullUrl, contentType, referrerChainArray);
+
+ if (filter == null)
+ {
+ return false;
+ }
+
+ // hack: if there is no referrer, block only if filter is domain-specific
+ // (to re-enable in-app ads blocking, proposed on 12.11.2012 Monday meeting)
+ // (documentUrls contains the referrers on Android)
+ try
+ {
+ if (referrerChainArray.length == 0 && (filter.getProperty("text").toString()).contains("||"))
+ {
+ return false;
+ }
+ } catch (NullPointerException e) {
+ }
+
+ return filter.getType() != Filter.Type.EXCEPTION;
+ }
+
+ public boolean isDocumentWhitelisted(final String url, final String[] referrerChainArray)
+ {
+ return this.filterEngine.isDocumentWhitelisted(url, referrerChainArray);
+ }
+
+ public boolean isDomainWhitelisted(final String url, final String[] referrerChainArray)
+ {
+ if (whitelistedDomains == null)
+ {
+ return false;
+ }
+
+ // using Set to remove duplicates
+ Set<String> referrersAndResourceUrls = new HashSet<String>();
+ if (referrerChainArray != null)
+ {
+ referrersAndResourceUrls.addAll(Arrays.asList(referrerChainArray));
+ }
+ referrersAndResourceUrls.add(url);
+
+ for (String eachUrl : referrersAndResourceUrls)
+ {
+ if (whitelistedDomains.contains(filterEngine.getHostFromURL(eachUrl)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean isElemhideWhitelisted(final String url, final String[] referrerChainArray)
+ {
+ return this.filterEngine.isElemhideWhitelisted(url, referrerChainArray);
+ }
+
+ public List<String> getElementHidingSelectors(final String url, final String domain, final String[] referrerChainArray)
+ {
+ /*
+ * Issue 3364 (https://issues.adblockplus.org/ticket/3364) introduced the
+ * feature to re-enabled element hiding.
+ *
+ * Nothing changes for Adblock Plus for Android, as `this.elemhideEnabled`
+ * is `false`, which results in an empty list being returned and converted
+ * into a `(String[])null` in AdblockPlus.java, which is the only place
+ * this function here is called from Adblock Plus for Android.
+ *
+ * If element hiding is enabled, then this function now first checks for
+ * possible whitelisting of either the document or element hiding for
+ * the given URL and returns an empty list if so. This is needed to
+ * ensure correct functioning of e.g. acceptable ads.
+ */
+ if (!this.enabled
+ || !this.elemhideEnabled
+ || this.isDomainWhitelisted(url, referrerChainArray)
+ || this.isDocumentWhitelisted(url, referrerChainArray)
+ || this.isElemhideWhitelisted(url, referrerChainArray))
+ {
+ return new ArrayList<String>();
+ }
+ return this.filterEngine.getElementHidingSelectors(domain);
+ }
+
+ public void checkForUpdates()
+ {
+ this.filterEngine.forceUpdateCheck(this.updateCheckDoneCallback);
+ }
+
+ public FilterEngine getFilterEngine()
+ {
+ return this.filterEngine;
+ }
+
+ public void setWhitelistedDomains(List<String> domains)
+ {
+ this.whitelistedDomains = domains;
+ }
+
+ public List<String> getWhitelistedDomains()
+ {
+ return whitelistedDomains;
+ }
+}

Powered by Google App Engine
This is Rietveld