diff options
Diffstat (limited to 'data/extensions/spyblock@gnu.org/lib/notification.js')
-rw-r--r-- | data/extensions/spyblock@gnu.org/lib/notification.js | 294 |
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(); |