| Index: lib/notification.js | 
| =================================================================== | 
| deleted file mode 100644 | 
| --- a/lib/notification.js | 
| +++ /dev/null | 
| @@ -1,412 +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 Handles notifications. | 
| - */ | 
| - | 
| -Cu.import("resource://gre/modules/Services.jsm"); | 
| - | 
| -var {Prefs} = require("prefs"); | 
| -var {Downloader, Downloadable, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader"); | 
| -var {Utils} = require("utils"); | 
| -var {Matcher, defaultMatcher} = require("matcher"); | 
| -var {Filter, RegExpFilter, WhitelistFilter} = require("filterClasses"); | 
| - | 
| -var INITIAL_DELAY = 1 * MILLIS_IN_MINUTE; | 
| -var CHECK_INTERVAL = 1 * MILLIS_IN_HOUR; | 
| -var EXPIRATION_INTERVAL = 1 * MILLIS_IN_DAY; | 
| -var TYPE = { | 
| -  information: 0, | 
| -  question: 1, | 
| -  critical: 2 | 
| -}; | 
| - | 
| -var showListeners = []; | 
| -var questionListeners = {}; | 
| - | 
| -function getNumericalSeverity(notification) | 
| -{ | 
| -  return (notification.type in TYPE ? TYPE[notification.type] : TYPE.information); | 
| -} | 
| - | 
| -function saveNotificationData() | 
| -{ | 
| -  // HACK: JSON values aren't saved unless they are assigned a different object. | 
| -  Prefs.notificationdata = JSON.parse(JSON.stringify(Prefs.notificationdata)); | 
| -} | 
| - | 
| -function localize(translations, locale) | 
| -{ | 
| -  if (locale in translations) | 
| -    return translations[locale]; | 
| - | 
| -  let languagePart = locale.substring(0, locale.indexOf("-")); | 
| -  if (languagePart && languagePart in translations) | 
| -    return translations[languagePart]; | 
| - | 
| -  let defaultLocale = "en-US"; | 
| -  return translations[defaultLocale]; | 
| -} | 
| - | 
| -/** | 
| - * The object providing actual downloading functionality. | 
| - * @type Downloader | 
| - */ | 
| -var downloader = null; | 
| -var localData = []; | 
| - | 
| -/** | 
| - * Regularly fetches notifications and decides which to show. | 
| - * @class | 
| - */ | 
| -var Notification = exports.Notification = | 
| -{ | 
| -  /** | 
| -   * Called on module startup. | 
| -   */ | 
| -  init: function() | 
| -  { | 
| -    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*() | 
| -  { | 
| -    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) | 
| -      downloadable.lastVersion = Prefs.notificationdata.data.version; | 
| -    if (typeof Prefs.notificationdata.softExpiration === "number") | 
| -      downloadable.softExpiration = Prefs.notificationdata.softExpiration; | 
| -    if (typeof Prefs.notificationdata.hardExpiration === "number") | 
| -      downloadable.hardExpiration = Prefs.notificationdata.hardExpiration; | 
| -    if (typeof Prefs.notificationdata.downloadCount === "number") | 
| -      downloadable.downloadCount = Prefs.notificationdata.downloadCount; | 
| -    yield downloadable; | 
| -  }, | 
| - | 
| -  _onExpirationChange: function(downloadable) | 
| -  { | 
| -    Prefs.notificationdata.lastCheck = downloadable.lastCheck; | 
| -    Prefs.notificationdata.softExpiration = downloadable.softExpiration; | 
| -    Prefs.notificationdata.hardExpiration = downloadable.hardExpiration; | 
| -    saveNotificationData(); | 
| -  }, | 
| - | 
| -  _onDownloadSuccess: function(downloadable, responseText, errorCallback, redirectCallback) | 
| -  { | 
| -    try | 
| -    { | 
| -      let data = JSON.parse(responseText); | 
| -      for (let notification of data.notifications) | 
| -      { | 
| -        if ("severity" in notification) | 
| -        { | 
| -          if (!("type" in notification)) | 
| -            notification.type = notification.severity; | 
| -          delete notification.severity; | 
| -        } | 
| -      } | 
| -      Prefs.notificationdata.data = data; | 
| -    } | 
| -    catch (e) | 
| -    { | 
| -      Cu.reportError(e); | 
| -      errorCallback("synchronize_invalid_data"); | 
| -      return; | 
| -    } | 
| - | 
| -    Prefs.notificationdata.lastError = 0; | 
| -    Prefs.notificationdata.downloadStatus = "synchronize_ok"; | 
| -    [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) | 
| -  { | 
| -    Prefs.notificationdata.lastError = Date.now(); | 
| -    Prefs.notificationdata.downloadStatus = error; | 
| -    saveNotificationData(); | 
| -  }, | 
| - | 
| -  /** | 
| -   * Adds a listener for notifications to be shown. | 
| -   * @param {Function} listener Listener to be invoked when a notification is | 
| -   *                   to be shown | 
| -   */ | 
| -  addShowListener: function(listener) | 
| -  { | 
| -    if (showListeners.indexOf(listener) == -1) | 
| -      showListeners.push(listener); | 
| -  }, | 
| - | 
| -  /** | 
| -   * Removes the supplied listener. | 
| -   * @param {Function} listener Listener that was added via addShowListener() | 
| -   */ | 
| -  removeShowListener: function(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) | 
| -   * @return {Object} notification to be shown, or null if there is none | 
| -   */ | 
| -  _getNextToShow: function(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)); | 
| -    } | 
| - | 
| -    let remoteData = []; | 
| -    if (typeof Prefs.notificationdata.data == "object" && Prefs.notificationdata.data.notifications instanceof Array) | 
| -      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"); | 
| -    let notificationToShow = null; | 
| -    for (let notification of notifications) | 
| -    { | 
| -      if (typeof notification.type === "undefined" || notification.type !== "critical") | 
| -      { | 
| -        let shown = Prefs.notificationdata.shown; | 
| -        if (shown instanceof Array && shown.indexOf(notification.id) != -1) | 
| -          continue; | 
| -        if (Prefs.notifications_ignoredcategories.indexOf("*") != -1) | 
| -          continue; | 
| -      } | 
| - | 
| -      if (typeof url === "string" || notification.urlFilters instanceof Array) | 
| -      { | 
| -        if (Prefs.enabled && typeof url === "string" && notification.urlFilters instanceof Array) | 
| -        { | 
| -          let host; | 
| -          if (typeof URL == "function") | 
| -            host = new URL(url).hostname; | 
| -          else | 
| -          { | 
| -            try | 
| -            { | 
| -              host = Services.io.newURI(url, null, null).host; | 
| -            } | 
| -            catch (e) | 
| -            { | 
| -              // Ignore, an exception is expected for about: and similar schemes | 
| -              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, RegExpFilter.typeMap.DOCUMENT, host, false, null)) | 
| -            continue; | 
| -        } | 
| -        else | 
| -          continue; | 
| -      } | 
| - | 
| -      if (notification.targets instanceof Array) | 
| -      { | 
| -        let match = false; | 
| -        for (let target of notification.targets) | 
| -        { | 
| -          if (checkTarget(target, "extension", addonName, addonVersion) && | 
| -              checkTarget(target, "application", application, applicationVersion) && | 
| -              checkTarget(target, "platform", platform, platformVersion)) | 
| -          { | 
| -            match = true; | 
| -            break; | 
| -          } | 
| -        } | 
| -        if (!match) | 
| -          continue; | 
| -      } | 
| - | 
| -      if (!notificationToShow | 
| -          || getNumericalSeverity(notification) > getNumericalSeverity(notificationToShow)) | 
| -        notificationToShow = notification; | 
| -    } | 
| - | 
| -    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: function(url) | 
| -  { | 
| -    let notification = Notification._getNextToShow(url); | 
| -    if (notification) | 
| -      for (let showListener of showListeners) | 
| -        showListener(notification); | 
| -  }, | 
| - | 
| -  /** | 
| -   * Marks a notification as shown. | 
| -   * @param {String} id ID of the notification to be marked as shown | 
| -   */ | 
| -  markAsShown: function(id) | 
| -  { | 
| -    var data = Prefs.notificationdata; | 
| - | 
| -    if (!(data.shown instanceof Array)) | 
| -      data.shown = []; | 
| -    if (data.shown.indexOf(id) != -1) | 
| -      return; | 
| - | 
| -    data.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 | 
| -   *                        application locale) | 
| -   * @return {Object} the translated texts | 
| -   */ | 
| -  getLocalizedTexts: function(notification, locale) | 
| -  { | 
| -    locale = locale || Utils.appLocale; | 
| -    let textKeys = ["title", "message"]; | 
| -    let localizedTexts = []; | 
| -    for (let key of textKeys) | 
| -    { | 
| -      if (key in notification) | 
| -      { | 
| -        if (typeof notification[key] == "string") | 
| -          localizedTexts[key] = notification[key]; | 
| -        else | 
| -          localizedTexts[key] = localize(notification[key], locale); | 
| -      } | 
| -    } | 
| -    return localizedTexts; | 
| -  }, | 
| - | 
| -  /** | 
| -   * Adds a local notification. | 
| -   * @param {Object} notification notification to add | 
| -   */ | 
| -  addNotification: function(notification) | 
| -  { | 
| -    if (localData.indexOf(notification) == -1) | 
| -      localData.push(notification); | 
| -  }, | 
| - | 
| -  /** | 
| -   * Removes an existing local notification. | 
| -   * @param {Object} notification notification to remove | 
| -   */ | 
| -  removeNotification: function(notification) | 
| -  { | 
| -    let index = localData.indexOf(notification); | 
| -    if (index > -1) | 
| -      localData.splice(index, 1); | 
| -  }, | 
| - | 
| -  /** | 
| -   * Adds a listener for question-type notifications | 
| -   */ | 
| -  addQuestionListener: function(/**string*/ id, /**function(approved)*/ 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 | 
| -   */ | 
| -  removeQuestionListener: function(/**string*/ id, /**function(approved)*/ listener) | 
| -  { | 
| -    if (!(id in questionListeners)) | 
| -      return; | 
| -    let index = questionListeners[id].indexOf(listener); | 
| -    if (index > -1) | 
| -      questionListeners[id].splice(index, 1); | 
| -    if (questionListeners[id].length === 0) | 
| -      delete questionListeners[id]; | 
| -  }, | 
| - | 
| -  /** | 
| -   * Notifies question listeners about interactions with a notification | 
| -   * @param {String} id notification ID | 
| -   * @param {Boolean} approved indicator whether notification has been approved or not | 
| -   */ | 
| -  triggerQuestionListeners: function(id, approved) | 
| -  { | 
| -    if (!(id in questionListeners)) | 
| -      return; | 
| -    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: function(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(); | 
|  |