diff options
author | Ruben Rodriguez <ruben@gnu.org> | 2014-10-20 02:24:51 +0200 |
---|---|---|
committer | Ruben Rodriguez <ruben@gnu.org> | 2014-10-20 02:24:51 +0200 |
commit | 6e7918b6ccb69876d339a320091fdee811445395 (patch) | |
tree | 31cb88ee438d652fddefca1193f70289a8b3dcc8 /data/extensions/spyblock@gnu.org/lib/sync.js | |
parent | 60e5b13c35d4d3ba21bb03b026750a0a414f6c77 (diff) |
Generalize data directory
Diffstat (limited to 'data/extensions/spyblock@gnu.org/lib/sync.js')
-rw-r--r-- | data/extensions/spyblock@gnu.org/lib/sync.js | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/data/extensions/spyblock@gnu.org/lib/sync.js b/data/extensions/spyblock@gnu.org/lib/sync.js new file mode 100644 index 0000000..05eeced --- /dev/null +++ b/data/extensions/spyblock@gnu.org/lib/sync.js @@ -0,0 +1,459 @@ +/* + * This file is part of Adblock Plus <http://adblockplus.org/>, + * Copyright (C) 2006-2014 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 Firefox Sync integration + */ + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +let {FilterStorage} = require("filterStorage"); +let {FilterNotifier} = require("filterNotifier"); +let {Synchronizer} = require("synchronizer"); +let {Subscription, SpecialSubscription, DownloadableSubscription, ExternalSubscription} = require("subscriptionClasses"); +let {Filter, ActiveFilter} = require("filterClasses"); + +// Firefox Sync classes are set later in initEngine() +let Service, Engines, SyncEngine, Store, Tracker; + +/** + * ID of the only record stored + * @type String + */ +let filtersRecordID = "6fad6286-8207-46b6-aa39-8e0ce0bd7c49"; + +let Sync = exports.Sync = +{ + /** + * Will be set to true if/when Weave starts up. + * @type Boolean + */ + initialized: false, + + /** + * Whether Weave requested us to track changes. + * @type Boolean + */ + trackingEnabled: false, + + /** + * Returns Adblock Plus sync engine. + * @result Engine + */ + getEngine: function() + { + if (this.initialized) + return Engines.get("adblockplus"); + else + return null; + } +}; + +/** + * Listens to notifications from Sync service. + */ +let SyncServiceObserver = +{ + init: function() + { + try + { + let {Status, STATUS_DISABLED, CLIENT_NOT_CONFIGURED} = Cu.import("resource://services-sync/status.js", null); + Sync.initialized = Status.ready; + Sync.trackingEnabled = (Status.service != STATUS_DISABLED && Status.service != CLIENT_NOT_CONFIGURED); + } + catch (e) + { + return; + } + + if (Sync.initialized) + this.initEngine(); + else + Services.obs.addObserver(this, "weave:service:ready", true); + Services.obs.addObserver(this, "weave:engine:start-tracking", true); + Services.obs.addObserver(this, "weave:engine:stop-tracking", true); + + onShutdown.add(function() + { + try + { + Services.obs.removeObserver(this, "weave:service:ready"); + } catch (e) {} + Services.obs.removeObserver(this, "weave:engine:start-tracking"); + Services.obs.removeObserver(this, "weave:engine:stop-tracking"); + }.bind(this)); + }, + + initEngine: function() + { + ({Engines, SyncEngine, Store, Tracker} = Cu.import("resource://services-sync/engines.js")); + if (typeof Engines == "undefined") + { + ({Service} = Cu.import("resource://services-sync/service.js")); + Engines = Service.engineManager; + } + + ABPEngine.prototype.__proto__ = SyncEngine.prototype; + ABPStore.prototype.__proto__ = Store.prototype; + ABPTracker.prototype.__proto__ = Tracker.prototype; + + Engines.register(ABPEngine); + onShutdown.add(function() + { + Engines.unregister("adblockplus"); + }); + }, + + observe: function(subject, topic, data) + { + switch (topic) + { + case "weave:service:ready": + if (Sync.initialized) + return; + + this.initEngine(); + Sync.initialized = true; + break; + case "weave:engine:start-tracking": + Sync.trackingEnabled = true; + if (trackerInstance) + trackerInstance.startTracking(); + break; + case "weave:engine:stop-tracking": + Sync.trackingEnabled = false; + if (trackerInstance) + trackerInstance.stopTracking(); + break; + } + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), +}; + +function ABPEngine() +{ + SyncEngine.call(this, "AdblockPlus", Service); +} +ABPEngine.prototype = +{ + _storeObj: ABPStore, + _trackerObj: ABPTracker, + version: 1, + + _reconcile: function(item) + { + // Always process server data, we will do the merging ourselves + return true; + } +}; + +function ABPStore(name, engine) +{ + Store.call(this, name, engine); +} +ABPStore.prototype = +{ + getAllIDs: function() + { + let result = {} + result[filtersRecordID] = true; + return result; + }, + + changeItemID: function(oldId, newId) + { + // This should not be called, our engine doesn't implement _findDupe + throw Cr.NS_ERROR_UNEXPECTED; + }, + + itemExists: function(id) + { + // Only one id exists so far + return (id == filtersRecordID); + }, + + createRecord: function(id, collection) + { + let record = new ABPEngine.prototype._recordObj(collection, id); + if (id == filtersRecordID) + { + record.cleartext = { + id: id, + subscriptions: [], + }; + for (let subscription of FilterStorage.subscriptions) + { + if (subscription instanceof ExternalSubscription) + continue; + + let subscriptionEntry = + { + url: subscription.url, + disabled: subscription.disabled + }; + if (subscription instanceof SpecialSubscription) + { + subscriptionEntry.filters = []; + for (let filter of subscription.filters) + { + let filterEntry = {text: filter.text}; + if (filter instanceof ActiveFilter) + filterEntry.disabled = filter.disabled; + subscriptionEntry.filters.push(filterEntry); + } + } + else + subscriptionEntry.title = subscription.title; + record.cleartext.subscriptions.push(subscriptionEntry); + } + + // Data sent, forget about local changes now + trackerInstance.clearPrivateChanges() + } + else + record.deleted = true; + + return record; + }, + + create: function(record) + { + // This should not be called because our record list doesn't change but + // call update just in case. + this.update(record); + }, + + update: function(record) + { + if (record.id != filtersRecordID) + return; + + this._log.trace("Merging in remote data"); + + let data = record.cleartext.subscriptions; + + // First make sure we have the same subscriptions on both sides + let seenSubscription = {__proto__: null}; + for (let remoteSubscription of data) + { + seenSubscription[remoteSubscription.url] = true; + if (remoteSubscription.url in FilterStorage.knownSubscriptions) + { + let subscription = FilterStorage.knownSubscriptions[remoteSubscription.url]; + if (!trackerInstance.didSubscriptionChange(remoteSubscription)) + { + // Only change local subscription if there were no changes, otherwise dismiss remote changes + subscription.disabled = remoteSubscription.disabled; + if (subscription instanceof DownloadableSubscription) + subscription.title = remoteSubscription.title; + } + } + else if (!trackerInstance.didSubscriptionChange(remoteSubscription)) + { + // Subscription was added remotely, add it locally as well + let subscription = Subscription.fromURL(remoteSubscription.url); + if (!subscription) + continue; + + subscription.disabled = remoteSubscription.disabled; + if (subscription instanceof DownloadableSubscription) + { + subscription.title = remoteSubscription.title; + FilterStorage.addSubscription(subscription); + Synchronizer.execute(subscription); + } + } + } + + for (let subscription of FilterStorage.subscriptions.slice()) + { + if (!(subscription.url in seenSubscription) && subscription instanceof DownloadableSubscription && !trackerInstance.didSubscriptionChange(subscription)) + { + // Subscription was removed remotely, remove it locally as well + FilterStorage.removeSubscription(subscription); + } + } + + // Now sync the custom filters + let seenFilter = {__proto__: null}; + for (let remoteSubscription of data) + { + if (!("filters" in remoteSubscription)) + continue; + + for (let remoteFilter of remoteSubscription.filters) + { + seenFilter[remoteFilter.text] = true; + + let filter = Filter.fromText(remoteFilter.text); + if (trackerInstance.didFilterChange(filter)) + continue; + + if (filter.subscriptions.some((subscription) => subscription instanceof SpecialSubscription)) + { + // Filter might have been changed remotely + if (filter instanceof ActiveFilter) + filter.disabled = remoteFilter.disabled; + } + else + { + // Filter was added remotely, add it locally as well + FilterStorage.addFilter(filter); + } + } + } + + for (let subscription of FilterStorage.subscriptions) + { + if (!(subscription instanceof SpecialSubscription)) + continue; + + for (let filter of subscription.filters.slice()) + { + if (!(filter.text in seenFilter) && !trackerInstance.didFilterChange(filter)) + { + // Filter was removed remotely, remove it locally as well + FilterStorage.removeFilter(filter); + } + } + } + + // Merge done, forget about local changes now + trackerInstance.clearPrivateChanges() + }, + + remove: function(record) + { + // Shouldn't be called but if it is - ignore + }, + + wipe: function() + { + this._log.trace("Got wipe command, removing all data"); + + for (let subscription of FilterStorage.subscriptions.slice()) + { + if (subscription instanceof DownloadableSubscription) + FilterStorage.removeSubscription(subscription); + else if (subscription instanceof SpecialSubscription) + { + for (let filter of subscription.filters.slice()) + FilterStorage.removeFilter(filter); + } + } + + // Data wiped, forget about local changes now + trackerInstance.clearPrivateChanges() + } +}; + +/** + * Hack to allow store to use the tracker - store tracker pointer globally. + */ +let trackerInstance = null; + +function ABPTracker(name, engine) +{ + Tracker.call(this, name, engine); + + this.privateTracker = new Tracker(name + ".private", engine); + trackerInstance = this; + + this.onChange = this.onChange.bind(this); + + if (Sync.trackingEnabled) + this.startTracking(); +} +ABPTracker.prototype = +{ + privateTracker: null, + + startTracking: function() + { + FilterNotifier.addListener(this.onChange); + }, + + stopTracking: function() + { + FilterNotifier.removeListener(this.onChange); + }, + + clearPrivateChanges: function() + { + this.privateTracker.clearChangedIDs(); + }, + + addPrivateChange: function(id) + { + // Ignore changes during syncing + if (this.ignoreAll) + return; + + this.addChangedID(filtersRecordID); + this.privateTracker.addChangedID(id); + this.score += 10; + }, + + didSubscriptionChange: function(subscription) + { + return ("subscription " + subscription.url) in this.privateTracker.changedIDs; + }, + + didFilterChange: function(filter) + { + return ("filter " + filter.text) in this.privateTracker.changedIDs; + }, + + onChange: function(action, item) + { + switch (action) + { + case "subscription.updated": + if ("oldSubscription" in item) + { + // Subscription moved to a new address + this.addPrivateChange("subscription " + item.url); + this.addPrivateChange("subscription " + item.oldSubscription.url); + } + else if (item instanceof SpecialSubscription) + { + // User's filters changed via Preferences window + for (let filter of item.filters) + this.addPrivateChange("filter " + filter.text); + for (let filter of item.oldFilters) + this.addPrivateChange("filter " + filter.text); + } + break; + case "subscription.added": + case "subscription.removed": + case "subscription.disabled": + case "subscription.title": + this.addPrivateChange("subscription " + item.url); + break; + case "filter.added": + case "filter.removed": + case "filter.disabled": + this.addPrivateChange("filter " + item.text); + break; + } + } +}; + +SyncServiceObserver.init(); |