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

Unified Diff: lib/contentFilterModule.js

Issue 29784555: Issue 6665 - Abstract element hiding container logic into its own module Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Move base class into lib/contentFilterModule.js Created Aug. 15, 2018, 8:56 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
« no previous file with comments | « no previous file | lib/elemHide.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/contentFilterModule.js
===================================================================
new file mode 100644
--- /dev/null
+++ b/lib/contentFilterModule.js
@@ -0,0 +1,201 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-present 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/>.
+ */
+
+"use strict";
+
+/**
+ * @fileOverview Base implementation for content filter modules.
+ */
+
+const {ElemHideExceptions} = require("./elemHideExceptions");
+
+/**
+ * Map to be used instead when a filter has a blank domains property.
+ * @type {Map.<string,boolean>}
+ * @const
+ */
+let defaultDomains = new Map([["", true]]);
+
+/**
+ * Base class for content filter modules
+ * @class
+ */
+function ContentFilterModule()
+{
+ this._filtersByDomain = new Map();
+ this._filterBySelector = new Map();
+ this._unconditionalSelectors = null;
+ this._knownFilters = new Set();
+
+ ElemHideExceptions.on("added", this._onExceptionAdded.bind(this));
+}
+
+ContentFilterModule.prototype = {
+ /**
+ * Lookup table, active flag, by filter by domain.
+ * (Only contains filters that aren't unconditionally matched for all
+ * domains.)
+ * @type {Map.<string,Map.<Filter,boolean>>}
+ */
+ _filtersByDomain: null,
+
+ /**
+ * Lookup table, filter by selector. (Only used for selectors that are
+ * unconditionally matched for all domains.)
+ * @type {Map.<string,Filter>}
+ */
+ _filterBySelector: null,
+
+ /**
+ * This array caches the keys of filterBySelector table (selectors
+ * which unconditionally apply on all domains). It will be null if the
+ * cache needs to be rebuilt.
+ * @type {?string[]}
+ */
+ _unconditionalSelectors: null,
+
+ /**
+ * Set containing known content filters
+ * @type {Set.<ContentFilter>}
+ */
+ _knownFilters: null,
+
+ /**
+ * Adds a filter to the lookup table of filters by domain.
+ * @param {Filter} filter
+ */
+ _addToFiltersByDomain(filter)
+ {
+ let domains = filter.domains || defaultDomains;
+ for (let [domain, isIncluded] of domains)
+ {
+ // There's no need to note that a filter is generically disabled.
+ if (!isIncluded && domain == "")
+ continue;
+
+ let filters = this._filtersByDomain.get(domain);
+ if (!filters)
+ this._filtersByDomain.set(domain, filters = new Map());
+ filters.set(filter, isIncluded);
+ }
+ },
+
+ /**
+ * Returns a list of selectors that apply on each website unconditionally.
+ * @returns {string[]}
+ */
+ _getUnconditionalSelectors()
+ {
+ if (!this._unconditionalSelectors)
+ this._unconditionalSelectors = [...this._filterBySelector.keys()];
+
+ return this._unconditionalSelectors;
+ },
+
+ /**
+ * Handles the event when a new element hiding exception has been added
+ * @param {ElemHideException} exception
+ */
+ _onExceptionAdded(exception)
+ {
+ let {selector} = exception;
+
+ // If this is the first exception for a previously unconditionally applied
+ // element hiding selector we need to take care to update the lookups.
+ let unconditionalFilterForSelector = this._filterBySelector.get(selector);
+ if (unconditionalFilterForSelector)
+ {
+ this._addToFiltersByDomain(unconditionalFilterForSelector);
+ this._filterBySelector.delete(selector);
+ this._unconditionalSelectors = null;
+ }
+ },
+
+ /**
+ * Removes all known filters
+ */
+ clear()
+ {
+ for (let collection of [this._filtersByDomain, this._filterBySelector,
+ this._knownFilters])
hub 2018/08/23 16:05:38 I'd be for using forEach() here.
+ {
+ collection.clear();
+ }
+
+ this._unconditionalSelectors = null;
+ },
+
+ /**
+ * Add a new content filter
+ * @param {ContentFilter} filter
+ */
+ add(filter)
+ {
+ if (this._knownFilters.has(filter))
+ return;
+
+ let {selector} = filter;
+
+ if (!(filter.domains || ElemHideExceptions.hasExceptions(selector)))
+ {
+ // The new filter's selector is unconditionally applied to all domains
+ this._filterBySelector.set(selector, filter);
+ this._unconditionalSelectors = null;
+ }
+ else
+ {
+ // The new filter's selector only applies to some domains
+ this._addToFiltersByDomain(filter);
+ }
+
+ this._knownFilters.add(filter);
+ },
+
+ /**
+ * Removes a content filter
+ * @param {ContentFilter} filter
+ */
+ remove(filter)
+ {
+ if (!this._knownFilters.has(filter))
+ return;
+
+ let {selector} = filter;
+
+ // Unconditially applied content filters
+ if (this._filterBySelector.get(selector) == filter)
+ {
+ this._filterBySelector.delete(selector);
+ this._unconditionalSelectors = null;
+ }
+ // Conditionally applied content filters
+ else
+ {
+ let domains = filter.domains || defaultDomains;
+ for (let domain of domains.keys())
+ {
+ let filters = this._filtersByDomain.get(domain);
+ if (filters)
+ filters.delete(filter);
+ }
+ }
+
+ this._knownFilters.delete(filter);
+ }
+};
+
+exports.ContentFilterModule = ContentFilterModule;
« no previous file with comments | « no previous file | lib/elemHide.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld