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

Unified Diff: lib/filterStorage.js

Issue 29335650: Issue 2595 - Use the core code from adblockpluscore (Closed)
Patch Set: Created Feb. 4, 2016, 6:35 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 | « lib/filterNotifier.js ('k') | lib/matcher.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/filterStorage.js
===================================================================
deleted file mode 100644
--- a/lib/filterStorage.js
+++ /dev/null
@@ -1,800 +0,0 @@
-/*
- * 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/>.
- */
-
-/**
- * @fileOverview FilterStorage class responsible for managing user's subscriptions and filters.
- */
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-let {IO} = require("io");
-let {Prefs} = require("prefs");
-let {Filter, ActiveFilter} = require("filterClasses");
-let {Subscription, SpecialSubscription, ExternalSubscription} = require("subscriptionClasses");
-let {FilterNotifier} = require("filterNotifier");
-let {Utils} = require("utils");
-
-/**
- * Version number of the filter storage file format.
- * @type Integer
- */
-let formatVersion = 4;
-
-/**
- * This class reads user's filters from disk, manages them in memory and writes them back.
- * @class
- */
-let FilterStorage = exports.FilterStorage =
-{
- /**
- * Version number of the patterns.ini format used.
- * @type Integer
- */
- get formatVersion()
- {
- return formatVersion;
- },
-
- /**
- * File that the filter list has been loaded from and should be saved to
- * @type nsIFile
- */
- get sourceFile()
- {
- let file = null;
- if (Prefs.patternsfile)
- {
- // Override in place, use it instead of placing the file in the regular data dir
- file = IO.resolveFilePath(Prefs.patternsfile);
- }
- if (!file)
- {
- // Place the file in the data dir
- file = IO.resolveFilePath(Prefs.data_directory);
- if (file)
- file.append("patterns.ini");
- }
- if (!file)
- {
- // Data directory pref misconfigured? Try the default value
- try
- {
- file = IO.resolveFilePath(Services.prefs.getDefaultBranch("extensions.adblockplus.").getCharPref("data_directory"));
- if (file)
- file.append("patterns.ini");
- } catch(e) {}
- }
-
- if (!file)
- Cu.reportError("Adblock Plus: Failed to resolve filter file location from extensions.adblockplus.patternsfile preference");
-
- // Property is configurable because of the test suite.
- Object.defineProperty(this, "sourceFile", {value: file, configurable: true});
- return file;
- },
-
- /**
- * Will be set to true if no patterns.ini file exists.
- * @type Boolean
- */
- firstRun: false,
-
- /**
- * Map of properties listed in the filter storage file before the sections
- * start. Right now this should be only the format version.
- */
- fileProperties: Object.create(null),
-
- /**
- * List of filter subscriptions containing all filters
- * @type Subscription[]
- */
- subscriptions: [],
-
- /**
- * Map of subscriptions already on the list, by their URL/identifier
- * @type Object
- */
- knownSubscriptions: Object.create(null),
-
- /**
- * Finds the filter group that a filter should be added to by default. Will
- * return null if this group doesn't exist yet.
- */
- getGroupForFilter: function(/**Filter*/ filter) /**SpecialSubscription*/
- {
- let generalSubscription = null;
- for (let subscription of FilterStorage.subscriptions)
- {
- if (subscription instanceof SpecialSubscription && !subscription.disabled)
- {
- // Always prefer specialized subscriptions
- if (subscription.isDefaultFor(filter))
- return subscription;
-
- // If this is a general subscription - store it as fallback
- if (!generalSubscription && (!subscription.defaults || !subscription.defaults.length))
- generalSubscription = subscription;
- }
- }
- return generalSubscription;
- },
-
- /**
- * Adds a filter subscription to the list
- * @param {Subscription} subscription filter subscription to be added
- * @param {Boolean} silent if true, no listeners will be triggered (to be used when filter list is reloaded)
- */
- addSubscription: function(subscription, silent)
- {
- if (subscription.url in FilterStorage.knownSubscriptions)
- return;
-
- FilterStorage.subscriptions.push(subscription);
- FilterStorage.knownSubscriptions[subscription.url] = subscription;
- addSubscriptionFilters(subscription);
-
- if (!silent)
- FilterNotifier.triggerListeners("subscription.added", subscription);
- },
-
- /**
- * Removes a filter subscription from the list
- * @param {Subscription} subscription filter subscription to be removed
- * @param {Boolean} silent if true, no listeners will be triggered (to be used when filter list is reloaded)
- */
- removeSubscription: function(subscription, silent)
- {
- for (let i = 0; i < FilterStorage.subscriptions.length; i++)
- {
- if (FilterStorage.subscriptions[i].url == subscription.url)
- {
- removeSubscriptionFilters(subscription);
-
- FilterStorage.subscriptions.splice(i--, 1);
- delete FilterStorage.knownSubscriptions[subscription.url];
- if (!silent)
- FilterNotifier.triggerListeners("subscription.removed", subscription);
- return;
- }
- }
- },
-
- /**
- * Moves a subscription in the list to a new position.
- * @param {Subscription} subscription filter subscription to be moved
- * @param {Subscription} [insertBefore] filter subscription to insert before
- * (if omitted the subscription will be put at the end of the list)
- */
- moveSubscription: function(subscription, insertBefore)
- {
- let currentPos = FilterStorage.subscriptions.indexOf(subscription);
- if (currentPos < 0)
- return;
-
- let newPos = insertBefore ? FilterStorage.subscriptions.indexOf(insertBefore) : -1;
- if (newPos < 0)
- newPos = FilterStorage.subscriptions.length;
-
- if (currentPos < newPos)
- newPos--;
- if (currentPos == newPos)
- return;
-
- FilterStorage.subscriptions.splice(currentPos, 1);
- FilterStorage.subscriptions.splice(newPos, 0, subscription);
- FilterNotifier.triggerListeners("subscription.moved", subscription);
- },
-
- /**
- * Replaces the list of filters in a subscription by a new list
- * @param {Subscription} subscription filter subscription to be updated
- * @param {Filter[]} filters new filter list
- */
- updateSubscriptionFilters: function(subscription, filters)
- {
- removeSubscriptionFilters(subscription);
- subscription.oldFilters = subscription.filters;
- subscription.filters = filters;
- addSubscriptionFilters(subscription);
- FilterNotifier.triggerListeners("subscription.updated", subscription);
- delete subscription.oldFilters;
- },
-
- /**
- * Adds a user-defined filter to the list
- * @param {Filter} filter
- * @param {SpecialSubscription} [subscription] particular group that the filter should be added to
- * @param {Integer} [position] position within the subscription at which the filter should be added
- * @param {Boolean} silent if true, no listeners will be triggered (to be used when filter list is reloaded)
- */
- addFilter: function(filter, subscription, position, silent)
- {
- if (!subscription)
- {
- if (filter.subscriptions.some(s => s instanceof SpecialSubscription && !s.disabled))
- return; // No need to add
- subscription = FilterStorage.getGroupForFilter(filter);
- }
- if (!subscription)
- {
- // No group for this filter exists, create one
- subscription = SpecialSubscription.createForFilter(filter);
- this.addSubscription(subscription);
- return;
- }
-
- if (typeof position == "undefined")
- position = subscription.filters.length;
-
- if (filter.subscriptions.indexOf(subscription) < 0)
- filter.subscriptions.push(subscription);
- subscription.filters.splice(position, 0, filter);
- if (!silent)
- FilterNotifier.triggerListeners("filter.added", filter, subscription, position);
- },
-
- /**
- * Removes a user-defined filter from the list
- * @param {Filter} filter
- * @param {SpecialSubscription} [subscription] a particular filter group that
- * the filter should be removed from (if ommited will be removed from all subscriptions)
- * @param {Integer} [position] position inside the filter group at which the
- * filter should be removed (if ommited all instances will be removed)
- */
- removeFilter: function(filter, subscription, position)
- {
- let subscriptions = (subscription ? [subscription] : filter.subscriptions.slice());
- for (let i = 0; i < subscriptions.length; i++)
- {
- let subscription = subscriptions[i];
- if (subscription instanceof SpecialSubscription)
- {
- let positions = [];
- if (typeof position == "undefined")
- {
- let index = -1;
- do
- {
- index = subscription.filters.indexOf(filter, index + 1);
- if (index >= 0)
- positions.push(index);
- } while (index >= 0);
- }
- else
- positions.push(position);
-
- for (let j = positions.length - 1; j >= 0; j--)
- {
- let position = positions[j];
- if (subscription.filters[position] == filter)
- {
- subscription.filters.splice(position, 1);
- if (subscription.filters.indexOf(filter) < 0)
- {
- let index = filter.subscriptions.indexOf(subscription);
- if (index >= 0)
- filter.subscriptions.splice(index, 1);
- }
- FilterNotifier.triggerListeners("filter.removed", filter, subscription, position);
- }
- }
- }
- }
- },
-
- /**
- * Moves a user-defined filter to a new position
- * @param {Filter} filter
- * @param {SpecialSubscription} subscription filter group where the filter is located
- * @param {Integer} oldPosition current position of the filter
- * @param {Integer} newPosition new position of the filter
- */
- moveFilter: function(filter, subscription, oldPosition, newPosition)
- {
- if (!(subscription instanceof SpecialSubscription) || subscription.filters[oldPosition] != filter)
- return;
-
- newPosition = Math.min(Math.max(newPosition, 0), subscription.filters.length - 1);
- if (oldPosition == newPosition)
- return;
-
- subscription.filters.splice(oldPosition, 1);
- subscription.filters.splice(newPosition, 0, filter);
- FilterNotifier.triggerListeners("filter.moved", filter, subscription, oldPosition, newPosition);
- },
-
- /**
- * Increases the hit count for a filter by one
- * @param {Filter} filter
- */
- increaseHitCount: function(filter)
- {
- if (!Prefs.savestats || !(filter instanceof ActiveFilter))
- return;
-
- filter.hitCount++;
- filter.lastHit = Date.now();
- },
-
- /**
- * Resets hit count for some filters
- * @param {Filter[]} filters filters to be reset, if null all filters will be reset
- */
- resetHitCounts: function(filters)
- {
- if (!filters)
- {
- filters = [];
- for (let text in Filter.knownFilters)
- filters.push(Filter.knownFilters[text]);
- }
- for (let filter of filters)
- {
- filter.hitCount = 0;
- filter.lastHit = 0;
- }
- },
-
- _loading: false,
-
- /**
- * Loads all subscriptions from the disk
- * @param {nsIFile} [sourceFile] File to read from
- */
- loadFromDisk: function(sourceFile)
- {
- if (this._loading)
- return;
-
- this._loading = true;
-
- let readFile = function(sourceFile, backupIndex)
- {
- let parser = new INIParser();
- IO.readFromFile(sourceFile, parser, function(e)
- {
- if (!e && parser.subscriptions.length == 0)
- {
- // No filter subscriptions in the file, this isn't right.
- e = new Error("No data in the file");
- }
-
- if (e)
- Cu.reportError(e);
-
- if (e && !explicitFile)
- {
- // Attempt to load a backup
- sourceFile = this.sourceFile;
- if (sourceFile)
- {
- let [, part1, part2] = /^(.*)(\.\w+)$/.exec(sourceFile.leafName) || [null, sourceFile.leafName, ""];
-
- sourceFile = sourceFile.clone();
- sourceFile.leafName = part1 + "-backup" + (++backupIndex) + part2;
-
- IO.statFile(sourceFile, function(e, statData)
- {
- if (!e && statData.exists)
- readFile(sourceFile, backupIndex);
- else
- doneReading(parser);
- });
- return;
- }
- }
- doneReading(parser);
- }.bind(this));
- }.bind(this);
-
- var doneReading = function(parser)
- {
- // Old special groups might have been converted, remove them if they are empty
- let specialMap = {"~il~": true, "~wl~": true, "~fl~": true, "~eh~": true};
- let knownSubscriptions = Object.create(null);
- for (let i = 0; i < parser.subscriptions.length; i++)
- {
- let subscription = parser.subscriptions[i];
- if (subscription instanceof SpecialSubscription && subscription.filters.length == 0 && subscription.url in specialMap)
- parser.subscriptions.splice(i--, 1);
- else
- knownSubscriptions[subscription.url] = subscription;
- }
-
- this.fileProperties = parser.fileProperties;
- this.subscriptions = parser.subscriptions;
- this.knownSubscriptions = knownSubscriptions;
- Filter.knownFilters = parser.knownFilters;
- Subscription.knownSubscriptions = parser.knownSubscriptions;
-
- if (parser.userFilters)
- {
- for (let i = 0; i < parser.userFilters.length; i++)
- {
- let filter = Filter.fromText(parser.userFilters[i]);
- this.addFilter(filter, null, undefined, true);
- }
- }
-
- this._loading = false;
- FilterNotifier.triggerListeners("load");
-
- if (sourceFile != this.sourceFile)
- this.saveToDisk();
-
- }.bind(this);
-
- let explicitFile;
- if (sourceFile)
- {
- explicitFile = true;
- readFile(sourceFile, 0);
- }
- else
- {
- explicitFile = false;
- sourceFile = FilterStorage.sourceFile;
-
- let callback = function(e, statData)
- {
- if (e || !statData.exists)
- {
- this.firstRun = true;
- this._loading = false;
- FilterNotifier.triggerListeners("load");
- }
- else
- readFile(sourceFile, 0);
- }.bind(this);
-
- if (sourceFile)
- IO.statFile(sourceFile, callback);
- else
- callback(true);
- }
- },
-
- _generateFilterData: function*(subscriptions)
- {
- yield "# Adblock Plus preferences";
- yield "version=" + formatVersion;
-
- let saved = Object.create(null);
- let buf = [];
-
- // Save filter data
- for (let i = 0; i < subscriptions.length; i++)
- {
- let subscription = subscriptions[i];
- for (let j = 0; j < subscription.filters.length; j++)
- {
- let filter = subscription.filters[j];
- if (!(filter.text in saved))
- {
- filter.serialize(buf);
- saved[filter.text] = filter;
- for (let k = 0; k < buf.length; k++)
- yield buf[k];
- buf.splice(0);
- }
- }
- }
-
- // Save subscriptions
- for (let i = 0; i < subscriptions.length; i++)
- {
- let subscription = subscriptions[i];
-
- yield "";
-
- subscription.serialize(buf);
- if (subscription.filters.length)
- {
- buf.push("", "[Subscription filters]")
- subscription.serializeFilters(buf);
- }
- for (let k = 0; k < buf.length; k++)
- yield buf[k];
- buf.splice(0);
- }
- },
-
- /**
- * Will be set to true if saveToDisk() is running (reentrance protection).
- * @type Boolean
- */
- _saving: false,
-
- /**
- * Will be set to true if a saveToDisk() call arrives while saveToDisk() is
- * already running (delayed execution).
- * @type Boolean
- */
- _needsSave: false,
-
- /**
- * Saves all subscriptions back to disk
- * @param {nsIFile} [targetFile] File to be written
- */
- saveToDisk: function(targetFile)
- {
- let explicitFile = true;
- if (!targetFile)
- {
- targetFile = FilterStorage.sourceFile;
- explicitFile = false;
- }
- if (!targetFile)
- return;
-
- if (!explicitFile && this._saving)
- {
- this._needsSave = true;
- return;
- }
-
- // Make sure the file's parent directory exists
- try {
- targetFile.parent.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
- } catch (e) {}
-
- let writeFilters = function()
- {
- IO.writeToFile(targetFile, this._generateFilterData(subscriptions), function(e)
- {
- if (!explicitFile)
- this._saving = false;
-
- if (e)
- Cu.reportError(e);
-
- if (!explicitFile && this._needsSave)
- {
- this._needsSave = false;
- this.saveToDisk();
- }
- else
- FilterNotifier.triggerListeners("save");
- }.bind(this));
- }.bind(this);
-
- let checkBackupRequired = function(callbackNotRequired, callbackRequired)
- {
- if (explicitFile || Prefs.patternsbackups <= 0)
- callbackNotRequired();
- else
- {
- IO.statFile(targetFile, function(e, statData)
- {
- if (e || !statData.exists)
- callbackNotRequired();
- else
- {
- let [, part1, part2] = /^(.*)(\.\w+)$/.exec(targetFile.leafName) || [null, targetFile.leafName, ""];
- let newestBackup = targetFile.clone();
- newestBackup.leafName = part1 + "-backup1" + part2;
- IO.statFile(newestBackup, function(e, statData)
- {
- if (!e && (!statData.exists || (Date.now() - statData.lastModified) / 3600000 >= Prefs.patternsbackupinterval))
- callbackRequired(part1, part2)
- else
- callbackNotRequired();
- });
- }
- });
- }
- }.bind(this);
-
- let removeLastBackup = function(part1, part2)
- {
- let file = targetFile.clone();
- file.leafName = part1 + "-backup" + Prefs.patternsbackups + part2;
- IO.removeFile(file, (e) => renameBackup(part1, part2, Prefs.patternsbackups - 1));
- }.bind(this);
-
- let renameBackup = function(part1, part2, index)
- {
- if (index > 0)
- {
- let fromFile = targetFile.clone();
- fromFile.leafName = part1 + "-backup" + index + part2;
-
- let toName = part1 + "-backup" + (index + 1) + part2;
-
- IO.renameFile(fromFile, toName, (e) => renameBackup(part1, part2, index - 1));
- }
- else
- {
- let toFile = targetFile.clone();
- toFile.leafName = part1 + "-backup" + (index + 1) + part2;
-
- IO.copyFile(targetFile, toFile, writeFilters);
- }
- }.bind(this);
-
- // Do not persist external subscriptions
- let subscriptions = this.subscriptions.filter((s) => !(s instanceof ExternalSubscription));
- if (!explicitFile)
- this._saving = true;
-
- checkBackupRequired(writeFilters, removeLastBackup);
- },
-
- /**
- * Returns the list of existing backup files.
- */
- getBackupFiles: function() /**nsIFile[]*/
- {
- // TODO: This method should be asynchronous
- let result = [];
-
- let [, part1, part2] = /^(.*)(\.\w+)$/.exec(FilterStorage.sourceFile.leafName) || [null, FilterStorage.sourceFile.leafName, ""];
- for (let i = 1; ; i++)
- {
- let file = FilterStorage.sourceFile.clone();
- file.leafName = part1 + "-backup" + i + part2;
- if (file.exists())
- result.push(file);
- else
- break;
- }
- return result;
- }
-};
-
-/**
- * Joins subscription's filters to the subscription without any notifications.
- * @param {Subscription} subscription filter subscription that should be connected to its filters
- */
-function addSubscriptionFilters(subscription)
-{
- if (!(subscription.url in FilterStorage.knownSubscriptions))
- return;
-
- for (let filter of subscription.filters)
- filter.subscriptions.push(subscription);
-}
-
-/**
- * Removes subscription's filters from the subscription without any notifications.
- * @param {Subscription} subscription filter subscription to be removed
- */
-function removeSubscriptionFilters(subscription)
-{
- if (!(subscription.url in FilterStorage.knownSubscriptions))
- return;
-
- for (let filter of subscription.filters)
- {
- let i = filter.subscriptions.indexOf(subscription);
- if (i >= 0)
- filter.subscriptions.splice(i, 1);
- }
-}
-
-/**
- * IO.readFromFile() listener to parse filter data.
- * @constructor
- */
-function INIParser()
-{
- this.fileProperties = this.curObj = {};
- this.subscriptions = [];
- this.knownFilters = Object.create(null);
- this.knownSubscriptions = Object.create(null);
-}
-INIParser.prototype =
-{
- linesProcessed: 0,
- subscriptions: null,
- knownFilters: null,
- knownSubscriptions : null,
- wantObj: true,
- fileProperties: null,
- curObj: null,
- curSection: null,
- userFilters: null,
-
- process: function(val)
- {
- let origKnownFilters = Filter.knownFilters;
- Filter.knownFilters = this.knownFilters;
- let origKnownSubscriptions = Subscription.knownSubscriptions;
- Subscription.knownSubscriptions = this.knownSubscriptions;
- let match;
- try
- {
- if (this.wantObj === true && (match = /^(\w+)=(.*)$/.exec(val)))
- this.curObj[match[1]] = match[2];
- else if (val === null || (match = /^\s*\[(.+)\]\s*$/.exec(val)))
- {
- if (this.curObj)
- {
- // Process current object before going to next section
- switch (this.curSection)
- {
- case "filter":
- case "pattern":
- if ("text" in this.curObj)
- Filter.fromObject(this.curObj);
- break;
- case "subscription":
- let subscription = Subscription.fromObject(this.curObj);
- if (subscription)
- this.subscriptions.push(subscription);
- break;
- case "subscription filters":
- case "subscription patterns":
- if (this.subscriptions.length)
- {
- let subscription = this.subscriptions[this.subscriptions.length - 1];
- for (let text of this.curObj)
- {
- let filter = Filter.fromText(text);
- subscription.filters.push(filter);
- filter.subscriptions.push(subscription);
- }
- }
- break;
- case "user patterns":
- this.userFilters = this.curObj;
- break;
- }
- }
-
- if (val === null)
- return;
-
- this.curSection = match[1].toLowerCase();
- switch (this.curSection)
- {
- case "filter":
- case "pattern":
- case "subscription":
- this.wantObj = true;
- this.curObj = {};
- break;
- case "subscription filters":
- case "subscription patterns":
- case "user patterns":
- this.wantObj = false;
- this.curObj = [];
- break;
- default:
- this.wantObj = undefined;
- this.curObj = null;
- }
- }
- else if (this.wantObj === false && val)
- this.curObj.push(val.replace(/\\\[/g, "["));
- }
- finally
- {
- Filter.knownFilters = origKnownFilters;
- Subscription.knownSubscriptions = origKnownSubscriptions;
- }
-
- // Allow events to be processed every now and then.
- // Note: IO.readFromFile() will deal with the potential reentrance here.
- this.linesProcessed++;
- if (this.linesProcessed % 1000 == 0)
- Utils.yield();
- }
-};
« no previous file with comments | « lib/filterNotifier.js ('k') | lib/matcher.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld