summaryrefslogtreecommitdiff
path: root/data/extensions/spyblock@gnu.org/lib/notification.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/spyblock@gnu.org/lib/notification.js')
-rw-r--r--data/extensions/spyblock@gnu.org/lib/notification.js294
1 files changed, 216 insertions, 78 deletions
diff --git a/data/extensions/spyblock@gnu.org/lib/notification.js b/data/extensions/spyblock@gnu.org/lib/notification.js
index 1fa8eed..311e4e8 100644
--- a/data/extensions/spyblock@gnu.org/lib/notification.js
+++ b/data/extensions/spyblock@gnu.org/lib/notification.js
@@ -1,6 +1,6 @@
/*
* This file is part of Adblock Plus <https://adblockplus.org/>,
- * Copyright (C) 2006-2015 Eyeo GmbH
+ * Copyright (C) 2006-2017 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
@@ -15,32 +15,39 @@
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
*/
+"use strict";
+
/**
* @fileOverview Handles notifications.
*/
-Cu.import("resource://gre/modules/Services.jsm");
+const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
-let {Prefs} = require("prefs");
-let {Downloader, Downloadable, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader");
-let {Utils} = require("utils");
-let {Matcher} = require("matcher");
-let {Filter} = require("filterClasses");
+const {Prefs} = require("prefs");
+const {Downloader, Downloadable,
+ MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader");
+const {Utils} = require("utils");
+const {Matcher, defaultMatcher} = require("matcher");
+const {Filter, RegExpFilter, WhitelistFilter} = require("filterClasses");
-let INITIAL_DELAY = 12 * MILLIS_IN_MINUTE;
-let CHECK_INTERVAL = 1 * MILLIS_IN_HOUR;
-let EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY;
-let TYPE = {
+const INITIAL_DELAY = 1 * MILLIS_IN_MINUTE;
+const CHECK_INTERVAL = 1 * MILLIS_IN_HOUR;
+const EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY;
+const TYPE = {
information: 0,
question: 1,
- critical: 2
+ relentless: 2,
+ critical: 3
};
-let listeners = {};
+let showListeners = [];
+let questionListeners = {};
function getNumericalSeverity(notification)
{
- return (notification.type in TYPE ? TYPE[notification.type] : TYPE.information);
+ if (notification.type in TYPE)
+ return TYPE[notification.type];
+ return TYPE.information;
}
function saveNotificationData()
@@ -64,7 +71,7 @@ function localize(translations, locale)
/**
* The object providing actual downloading functionality.
- * @type Downloader
+ * @type {Downloader}
*/
let downloader = null;
let localData = [];
@@ -78,31 +85,31 @@ let Notification = exports.Notification =
/**
* Called on module startup.
*/
- init: function()
+ init()
{
- downloader = new Downloader(this._getDownloadables.bind(this), INITIAL_DELAY, CHECK_INTERVAL);
- onShutdown.add(function()
- {
- downloader.cancel();
- });
-
+ downloader = new Downloader(this._getDownloadables.bind(this),
+ INITIAL_DELAY, CHECK_INTERVAL);
downloader.onExpirationChange = this._onExpirationChange.bind(this);
downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this);
downloader.onDownloadError = this._onDownloadError.bind(this);
+ onShutdown.add(() => downloader.cancel());
},
/**
* Yields a Downloadable instances for the notifications download.
*/
- _getDownloadables: function()
+ *_getDownloadables()
{
let downloadable = new Downloadable(Prefs.notificationurl);
if (typeof Prefs.notificationdata.lastError === "number")
downloadable.lastError = Prefs.notificationdata.lastError;
if (typeof Prefs.notificationdata.lastCheck === "number")
downloadable.lastCheck = Prefs.notificationdata.lastCheck;
- if (typeof Prefs.notificationdata.data === "object" && "version" in Prefs.notificationdata.data)
+ if (typeof Prefs.notificationdata.data === "object" &&
+ "version" in Prefs.notificationdata.data)
+ {
downloadable.lastVersion = Prefs.notificationdata.data.version;
+ }
if (typeof Prefs.notificationdata.softExpiration === "number")
downloadable.softExpiration = Prefs.notificationdata.softExpiration;
if (typeof Prefs.notificationdata.hardExpiration === "number")
@@ -112,7 +119,7 @@ let Notification = exports.Notification =
yield downloadable;
},
- _onExpirationChange: function(downloadable)
+ _onExpirationChange(downloadable)
{
Prefs.notificationdata.lastCheck = downloadable.lastCheck;
Prefs.notificationdata.softExpiration = downloadable.softExpiration;
@@ -120,7 +127,8 @@ let Notification = exports.Notification =
saveNotificationData();
},
- _onDownloadSuccess: function(downloadable, responseText, errorCallback, redirectCallback)
+ _onDownloadSuccess(downloadable, responseText, errorCallback,
+ redirectCallback)
{
try
{
@@ -145,12 +153,18 @@ let Notification = exports.Notification =
Prefs.notificationdata.lastError = 0;
Prefs.notificationdata.downloadStatus = "synchronize_ok";
- [Prefs.notificationdata.softExpiration, Prefs.notificationdata.hardExpiration] = downloader.processExpirationInterval(EXPIRATION_INTERVAL);
+ [
+ Prefs.notificationdata.softExpiration,
+ Prefs.notificationdata.hardExpiration
+ ] = downloader.processExpirationInterval(EXPIRATION_INTERVAL);
Prefs.notificationdata.downloadCount = downloadable.downloadCount;
saveNotificationData();
+
+ Notification.showNext();
},
- _onDownloadError: function(downloadable, downloadURL, error, channelStatus, responseStatus, redirectCallback)
+ _onDownloadError(downloadable, downloadURL, error, channelStatus,
+ responseStatus, redirectCallback)
{
Prefs.notificationdata.lastError = Date.now();
Prefs.notificationdata.downloadStatus = error;
@@ -158,52 +172,115 @@ let Notification = exports.Notification =
},
/**
+ * Adds a listener for notifications to be shown.
+ * @param {Function} listener Listener to be invoked when a notification is
+ * to be shown
+ */
+ addShowListener(listener)
+ {
+ if (showListeners.indexOf(listener) == -1)
+ showListeners.push(listener);
+ },
+
+ /**
+ * Removes the supplied listener.
+ * @param {Function} listener Listener that was added via addShowListener()
+ */
+ removeShowListener(listener)
+ {
+ let index = showListeners.indexOf(listener);
+ if (index != -1)
+ showListeners.splice(index, 1);
+ },
+
+ /**
* Determines which notification is to be shown next.
- * @param {String} url URL to match notifications to (optional)
+ * @param {string} url URL to match notifications to (optional)
* @return {Object} notification to be shown, or null if there is none
*/
- getNextToShow: function(url)
+ _getNextToShow(url)
{
function checkTarget(target, parameter, name, version)
{
let minVersionKey = parameter + "MinVersion";
let maxVersionKey = parameter + "MaxVersion";
return !((parameter in target && target[parameter] != name) ||
- (minVersionKey in target && Services.vc.compare(version, target[minVersionKey]) < 0) ||
- (maxVersionKey in target && Services.vc.compare(version, target[maxVersionKey]) > 0));
+ (minVersionKey in target &&
+ Services.vc.compare(version, target[minVersionKey]) < 0) ||
+ (maxVersionKey in target &&
+ Services.vc.compare(version, target[maxVersionKey]) > 0));
}
let remoteData = [];
- if (typeof Prefs.notificationdata.data == "object" && Prefs.notificationdata.data.notifications instanceof Array)
- remoteData = Prefs.notificationdata.data.notifications;
-
- if (!(Prefs.notificationdata.shown instanceof Array))
+ if (typeof Prefs.notificationdata.data == "object" &&
+ Prefs.notificationdata.data.notifications instanceof Array)
{
- Prefs.notificationdata.shown = [];
- saveNotificationData();
+ remoteData = Prefs.notificationdata.data.notifications;
}
let notifications = localData.concat(remoteData);
if (notifications.length === 0)
return null;
- let {addonName, addonVersion, application, applicationVersion, platform, platformVersion} = require("info");
+ const {addonName, addonVersion, application,
+ applicationVersion, platform, platformVersion} = require("info");
let notificationToShow = null;
for (let notification of notifications)
{
- if ((typeof notification.type === "undefined" || notification.type !== "critical")
- && Prefs.notificationdata.shown.indexOf(notification.id) !== -1)
- continue;
+ if (typeof notification.type === "undefined" ||
+ notification.type !== "critical")
+ {
+ let shown;
+ if (typeof Prefs.notificationdata.shown == "object")
+ shown = Prefs.notificationdata.shown[notification.id];
+
+ if (typeof shown != "undefined")
+ {
+ if (typeof notification.interval == "number")
+ {
+ if (shown + notification.interval > Date.now())
+ continue;
+ }
+ else if (shown)
+ continue;
+ }
+
+ if (notification.type !== "relentless" &&
+ Prefs.notifications_ignoredcategories.indexOf("*") != -1)
+ {
+ continue;
+ }
+ }
if (typeof url === "string" || notification.urlFilters instanceof Array)
{
- if (typeof url === "string" && notification.urlFilters instanceof Array)
+ if (Prefs.enabled && typeof url === "string" &&
+ notification.urlFilters instanceof Array)
{
+ let host;
+ try
+ {
+ host = new URL(url).hostname;
+ }
+ catch (e)
+ {
+ host = "";
+ }
+
+ let exception = defaultMatcher.matchesAny(
+ url, RegExpFilter.typeMap.DOCUMENT, host, false, null
+ );
+ if (exception instanceof WhitelistFilter)
+ continue;
+
let matcher = new Matcher();
for (let urlFilter of notification.urlFilters)
matcher.add(Filter.fromText(urlFilter));
- if (!matcher.matchesAny(url, "DOCUMENT", url))
+ if (!matcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, host,
+ false, null))
+ {
continue;
+ }
}
else
continue;
@@ -215,7 +292,8 @@ let Notification = exports.Notification =
for (let target of notification.targets)
{
if (checkTarget(target, "extension", addonName, addonVersion) &&
- checkTarget(target, "application", application, applicationVersion) &&
+ checkTarget(target, "application", application,
+ applicationVersion) &&
checkTarget(target, "platform", platform, platformVersion))
{
match = true;
@@ -226,37 +304,63 @@ let Notification = exports.Notification =
continue;
}
- if (!notificationToShow
- || getNumericalSeverity(notification) > getNumericalSeverity(notificationToShow))
+ if (!notificationToShow ||
+ getNumericalSeverity(notification) >
+ getNumericalSeverity(notificationToShow))
notificationToShow = notification;
}
- if (notificationToShow && "id" in notificationToShow)
+ return notificationToShow;
+ },
+
+ /**
+ * Invokes the listeners added via addShowListener() with the next
+ * notification to be shown.
+ * @param {string} url URL to match notifications to (optional)
+ */
+ showNext(url)
+ {
+ let notification = Notification._getNextToShow(url);
+ if (notification)
{
- if (notificationToShow.type !== "question")
- this.markAsShown(notificationToShow.id);
+ for (let showListener of showListeners)
+ showListener(notification);
}
-
- return notificationToShow;
},
- markAsShown: function(id)
+ /**
+ * Marks a notification as shown.
+ * @param {string} id ID of the notification to be marked as shown
+ */
+ markAsShown(id)
{
- if (Prefs.notificationdata.shown.indexOf(id) > -1)
- return;
+ let now = Date.now();
+ let data = Prefs.notificationdata;
+
+ if (data.shown instanceof Array)
+ {
+ let newShown = {};
+ for (let oldId of data.shown)
+ newShown[oldId] = now;
+ data.shown = newShown;
+ }
+
+ if (typeof data.shown != "object")
+ data.shown = {};
+
+ data.shown[id] = now;
- Prefs.notificationdata.shown.push(id);
saveNotificationData();
},
/**
* Localizes the texts of the supplied notification.
* @param {Object} notification notification to translate
- * @param {String} locale the target locale (optional, defaults to the
+ * @param {string} locale the target locale (optional, defaults to the
* application locale)
* @return {Object} the translated texts
*/
- getLocalizedTexts: function(notification, locale)
+ getLocalizedTexts(notification, locale)
{
locale = locale || Utils.appLocale;
let textKeys = ["title", "message"];
@@ -278,7 +382,7 @@ let Notification = exports.Notification =
* Adds a local notification.
* @param {Object} notification notification to add
*/
- addNotification: function(notification)
+ addNotification(notification)
{
if (localData.indexOf(notification) == -1)
localData.push(notification);
@@ -288,7 +392,7 @@ let Notification = exports.Notification =
* Removes an existing local notification.
* @param {Object} notification notification to remove
*/
- removeNotification: function(notification)
+ removeNotification(notification)
{
let index = localData.indexOf(notification);
if (index > -1)
@@ -296,42 +400,76 @@ let Notification = exports.Notification =
},
/**
+ * A callback function which listens to see if notifications were approved.
+ *
+ * @callback QuestionListener
+ * @param {boolean} approved
+ */
+
+ /**
* Adds a listener for question-type notifications
+ * @param {string} id
+ * @param {QuestionListener} listener
*/
- addQuestionListener: function(/**string*/ id, /**function(approved)*/ listener)
+ addQuestionListener(id, listener)
{
- if (!(id in listeners))
- listeners[id] = [];
- if (listeners[id].indexOf(listener) === -1)
- listeners[id].push(listener);
+ if (!(id in questionListeners))
+ questionListeners[id] = [];
+ if (questionListeners[id].indexOf(listener) === -1)
+ questionListeners[id].push(listener);
},
/**
* Removes a listener that was previously added via addQuestionListener
+ * @param {string} id
+ * @param {QuestionListener} listener
*/
- removeQuestionListener: function(/**string*/ id, /**function(approved)*/ listener)
+ removeQuestionListener(id, listener)
{
- if (!(id in listeners))
+ if (!(id in questionListeners))
return;
- let index = listeners[id].indexOf(listener);
+ let index = questionListeners[id].indexOf(listener);
if (index > -1)
- listeners[id].splice(index, 1);
- if (listeners[id].length === 0)
- delete listeners[id];
+ questionListeners[id].splice(index, 1);
+ if (questionListeners[id].length === 0)
+ delete questionListeners[id];
},
/**
- * Notifies listeners about interactions with a notification
- * @param {String} id notification ID
- * @param {Boolean} approved indicator whether notification has been approved or not
+ * Notifies question listeners about interactions with a notification
+ * @param {string} id notification ID
+ * @param {boolean} approved indicator whether notification has been approved
*/
- triggerQuestionListeners: function(id, approved)
+ triggerQuestionListeners(id, approved)
{
- if (!(id in listeners))
+ if (!(id in questionListeners))
return;
- let questionListeners = listeners[id];
- for (let listener of questionListeners)
+ let listeners = questionListeners[id];
+ for (let listener of listeners)
listener(approved);
+ },
+
+ /**
+ * Toggles whether notifications of a specific category should be ignored
+ * @param {string} category notification category identifier
+ * @param {boolean} [forceValue] force specified value
+ */
+ toggleIgnoreCategory(category, forceValue)
+ {
+ let categories = Prefs.notifications_ignoredcategories;
+ let index = categories.indexOf(category);
+ if (index == -1 && forceValue !== false)
+ {
+ categories.push(category);
+ Prefs.notifications_showui = true;
+ }
+ else if (index != -1 && forceValue !== true)
+ categories.splice(index, 1);
+
+ // HACK: JSON values aren't saved unless they are assigned a
+ // different object.
+ Prefs.notifications_ignoredcategories =
+ JSON.parse(JSON.stringify(categories));
}
};
Notification.init();