diff options
Diffstat (limited to 'data/extensions/spyblock@gnu.org/lib')
48 files changed, 0 insertions, 15494 deletions
diff --git a/data/extensions/spyblock@gnu.org/lib/Public.jsm b/data/extensions/spyblock@gnu.org/lib/Public.jsm deleted file mode 100644 index ecd7e95..0000000 --- a/data/extensions/spyblock@gnu.org/lib/Public.jsm +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Public Adblock Plus API. - */ - -var EXPORTED_SYMBOLS = ["AdblockPlus"]; - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/Services.jsm"); - -function require(module) -{ - let result = {}; - result.wrappedJSObject = result; - Services.obs.notifyObservers(result, "adblockplus-require", module); - return result.exports; -} - -let {FilterStorage} = require("filterStorage"); -let {Filter} = require("filterClasses"); -let {Subscription, SpecialSubscription, RegularSubscription, DownloadableSubscription, ExternalSubscription} = require("subscriptionClasses"); - -const externalPrefix = "~external~"; - -/** - * Class implementing public Adblock Plus API - * @class - */ -var AdblockPlus = -{ - /** - * Returns current subscription count - * @type Integer - */ - get subscriptionCount() - { - return FilterStorage.subscriptions.length; - }, - - /** - * Gets a subscription by its URL - */ - getSubscription: function(/**String*/ id) /**IAdblockPlusSubscription*/ - { - if (id in FilterStorage.knownSubscriptions) - return createSubscriptionWrapper(FilterStorage.knownSubscriptions[id]); - - return null; - }, - - /** - * Gets a subscription by its position in the list - */ - getSubscriptionAt: function(/**Integer*/ index) /**IAdblockPlusSubscription*/ - { - if (index < 0 || index >= FilterStorage.subscriptions.length) - return null; - - return createSubscriptionWrapper(FilterStorage.subscriptions[index]); - }, - - /** - * Updates an external subscription and creates it if necessary - */ - updateExternalSubscription: function(/**String*/ id, /**String*/ title, /**Array of Filter*/ filters) /**String*/ - { - if (id.substr(0, externalPrefix.length) != externalPrefix) - id = externalPrefix + id; - let subscription = Subscription.knownSubscriptions[id]; - if (typeof subscription == "undefined") - subscription = new ExternalSubscription(id, title); - - subscription.lastDownload = parseInt(new Date().getTime() / 1000); - - let newFilters = []; - for (let filter of filters) - { - filter = Filter.fromText(Filter.normalize(filter)); - if (filter) - newFilters.push(filter); - } - - if (id in FilterStorage.knownSubscriptions) - FilterStorage.updateSubscriptionFilters(subscription, newFilters); - else - { - subscription.filters = newFilters; - FilterStorage.addSubscription(subscription); - } - - return id; - }, - - /** - * Removes an external subscription by its identifier - */ - removeExternalSubscription: function(/**String*/ id) /**Boolean*/ - { - if (id.substr(0, externalPrefix.length) != externalPrefix) - id = externalPrefix + id; - if (!(id in FilterStorage.knownSubscriptions)) - return false; - - FilterStorage.removeSubscription(FilterStorage.knownSubscriptions[id]); - return true; - }, - - /** - * Adds user-defined filters to the list - */ - addPatterns: function(/**Array of String*/ filters) - { - for (let filter of filters) - { - filter = Filter.fromText(Filter.normalize(filter)); - if (filter) - { - filter.disabled = false; - FilterStorage.addFilter(filter); - } - } - }, - - /** - * Removes user-defined filters from the list - */ - removePatterns: function(/**Array of String*/ filters) - { - for (let filter of filters) - { - filter = Filter.fromText(Filter.normalize(filter)); - if (filter) - FilterStorage.removeFilter(filter); - } - }, - - /** - * Returns installed Adblock Plus version - */ - getInstalledVersion: function() /**String*/ - { - return require("info").addonVersion; - }, - - /** - * Returns source code revision this Adblock Plus build was created from (if available) - */ - getInstalledBuild: function() /**String*/ - { - return ""; - }, -}; - -/** - * Wraps a subscription into IAdblockPlusSubscription structure. - */ -function createSubscriptionWrapper(/**Subscription*/ subscription) /**IAdblockPlusSubscription*/ -{ - if (!subscription) - return null; - - return { - url: subscription.url, - special: subscription instanceof SpecialSubscription, - title: subscription.title, - autoDownload: true, - disabled: subscription.disabled, - external: subscription instanceof ExternalSubscription, - lastDownload: subscription instanceof RegularSubscription ? subscription.lastDownload : 0, - downloadStatus: subscription instanceof DownloadableSubscription ? subscription.downloadStatus : "synchronize_ok", - lastModified: subscription instanceof DownloadableSubscription ? subscription.lastModified : null, - expires: subscription instanceof DownloadableSubscription ? subscription.expires : 0, - getPatterns: function() - { - let result = subscription.filters.map(function(filter) - { - return filter.text; - }); - return result; - } - }; -} diff --git a/data/extensions/spyblock@gnu.org/lib/antiadblockInit.js b/data/extensions/spyblock@gnu.org/lib/antiadblockInit.js deleted file mode 100644 index c5b845f..0000000 --- a/data/extensions/spyblock@gnu.org/lib/antiadblockInit.js +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -const {Prefs} = require("prefs"); -const {ActiveFilter} = require("filterClasses"); -const {FilterStorage} = require("filterStorage"); -const {FilterNotifier} = require("filterNotifier"); -const {Subscription} = require("subscriptionClasses"); -const {Notification} = require("notification"); - -let ext; -if (typeof window != "undefined" && window.ext) - ({ext} = window); -else - ext = require("ext_background"); - -exports.initAntiAdblockNotification = function initAntiAdblockNotification() -{ - let notification = { - id: "antiadblock", - type: "question", - title: ext.i18n.getMessage("notification_antiadblock_title"), - message: ext.i18n.getMessage("notification_antiadblock_message"), - urlFilters: [] - }; - - function notificationListener(approved) - { - let subscription = Subscription.fromURL(Prefs.subscriptions_antiadblockurl); - if (subscription.url in FilterStorage.knownSubscriptions) - subscription.disabled = !approved; - } - - function addAntiAdblockNotification(subscription) - { - let urlFilters = []; - for (let filter of subscription.filters) - { - if (filter instanceof ActiveFilter) - { - for (let domain in filter.domains) - { - let urlFilter = "||" + domain + "^$document"; - if (domain && filter.domains[domain] && - urlFilters.indexOf(urlFilter) == -1) - urlFilters.push(urlFilter); - } - } - } - notification.urlFilters = urlFilters; - Notification.addNotification(notification); - Notification.addQuestionListener(notification.id, notificationListener); - } - - function removeAntiAdblockNotification() - { - Notification.removeNotification(notification); - Notification.removeQuestionListener(notification.id, notificationListener); - } - - let antiAdblockSubscription = Subscription.fromURL( - Prefs.subscriptions_antiadblockurl - ); - if (antiAdblockSubscription.lastDownload && antiAdblockSubscription.disabled) - addAntiAdblockNotification(antiAdblockSubscription); - - function onSubscriptionChange(subscription) - { - let url = Prefs.subscriptions_antiadblockurl; - if (url != subscription.url) - return; - - if (url in FilterStorage.knownSubscriptions && subscription.disabled) - addAntiAdblockNotification(subscription); - else - removeAntiAdblockNotification(); - } - - FilterNotifier.on("subscription.updated", onSubscriptionChange); - FilterNotifier.on("subscription.removed", onSubscriptionChange); - FilterNotifier.on("subscription.disabled", onSubscriptionChange); -}; diff --git a/data/extensions/spyblock@gnu.org/lib/appSupport.js b/data/extensions/spyblock@gnu.org/lib/appSupport.js deleted file mode 100644 index ba8fdd1..0000000 --- a/data/extensions/spyblock@gnu.org/lib/appSupport.js +++ /dev/null @@ -1,851 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Various application-specific functions. - */ - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/AddonManager.jsm"); - -/** - * Checks whether an application window is known and should get Adblock Plus - * user interface elements. - * @result Boolean - */ -exports.isKnownWindow = (/**Window*/ window) => false; - -/** - * HACK: In some applications the window finishes initialization during load - * event processing which makes an additional delay necessary. This flag - * indicates that. - * @type Boolean - */ -exports.delayInitialization = false; - -/** - * Retrieves the browser element for an application window. - * @type function(window) - */ -exports.getBrowser = null; - -/** - * Adds a new browser tab in the given application window. - * @type function(window, url, event) - */ -exports.addTab = null; - -/** - * Retrieves the current browser location for an application window. - */ -exports.getCurrentLocation = function getCurrentLocation(/**Window*/ window) /**nsIURI|String*/ -{ - let browser = (exports.getBrowser ? exports.getBrowser(window) : null); - return (browser ? browser.currentURI : null); -} - - -/** - * The ID (or a list of possible IDs) of the content area context menu. - * @type String|String[] - */ -exports.contentContextMenu = null; - -/** - * Determines the default placement of the toolbar icon via object properties - * parent, before and after. - * @type Object - */ -exports.defaultToolbarPosition = null; - -/** - * The properties parent, before, after determine the placement of the status - * bar icon. - * @type Object - */ -exports.statusbarPosition = null; - -/** - * The properties parent, before, after determine the placement of the Tools - * submenu. - * @type Object - */ -exports.toolsMenu = null; - -/** - * Maps windows to their bottom bar info. - */ -let bottomBars = new WeakMap(); - -/** - * Adds a bottom bar to the application window. - * @type function(window, element) - */ -exports.addBottomBar = null; - -function _addBottomBar(window, parent, element) -{ - if (bottomBars.has(window) || !parent) - return null; - - let bar = {elements: []}; - for (let child = element.firstElementChild; child; child = child.nextElementSibling) - { - let clone = child.cloneNode(true); - parent.appendChild(clone); - bar.elements.push(clone); - } - - bottomBars.set(window, bar); - return bar; -}; - -/** - * Removes the bottom bar from the application window. - * @type function(window) - */ -exports.removeBottomBar = null; - -function _removeBottomBar(window) -{ - if (!bottomBars.has(window)) - return null; - - let bar = bottomBars.get(window); - for (let i = 0; i < bar.elements.length; i++) - if (bar.elements[i].parentNode) - bar.elements[i].parentNode.removeChild(bar.elements[i]); - - bottomBars.delete(window); - return bar; -}; - -/** - * Maps windows to a list of progress listeners. - */ -let progressListeners = new WeakMap(); - -/** - * Makes sure that a function is called whenever the displayed browser location changes. - */ -exports.addBrowserLocationListener = function addBrowserLocationListener(/**Window*/ window, /**Function*/ callback, /**Boolean*/ ignoreSameDoc) -{ - let browser = (exports.getBrowser ? exports.getBrowser(window) : null); - if (browser) - { - let dummy = function() {}; - let progressListener = - { - callback: callback, - onLocationChange: function(progress, request, uri, flags) - { - if (!ignoreSameDoc || !flags || !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) - this.callback(); - }, - onProgressChange: dummy, - onSecurityChange: dummy, - onStateChange: dummy, - onStatusChange: dummy, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]) - }; - browser.addProgressListener(progressListener); - - if (progressListeners.has(window)) - progressListeners.get(window).push(progressListener); - else - progressListeners.set(window, [progressListener]); - } -}; - -/** - * Removes a location listener registered for a window. - */ -exports.removeBrowserLocationListener = function removeBrowserLocationListener(/**Window*/ window, /**Function*/ callback) -{ - if (!progressListeners.has(window)) - return; - - let browser = (exports.getBrowser ? exports.getBrowser(window) : null); - let listeners = progressListeners.get(window); - for (let i = 0; i < listeners.length; i++) - { - if (listeners[i].callback == callback) - { - if (browser) - browser.removeProgressListener(listeners[i]); - listeners.splice(i--, 1); - } - } -}; - -/** - * Removes all location listeners registered for a window, to be called on - * cleanup. - */ -exports.removeBrowserLocationListeners = function removeBrowserLocationListeners(/**Window*/ window) -{ - if (!progressListeners.has(window)) - return; - - let browser = (exports.getBrowser ? exports.getBrowser(window) : null); - if (browser) - { - let listeners = progressListeners.get(window); - for (let i = 0; i < listeners.length; i++) - browser.removeProgressListener(listeners[i]); - } - progressListeners.delete(window); -}; - -let {application} = require("info"); -switch (application) -{ - case "firefox": - { - exports.isKnownWindow = function ff_isKnownWindow(window) - { - return (window.document.documentElement.getAttribute("windowtype") == "navigator:browser"); - }; - - exports.getBrowser = (window) => window.gBrowser; - - exports.addTab = function ff_addTab(window, url, event) - { - if (event) - window.openNewTabWith(url, null, null, event, false); - else - window.gBrowser.loadOneTab(url, {inBackground: false}); - }; - - exports.contentContextMenu = "contentAreaContextMenu"; - - exports.defaultToolbarPosition = { - parent: "nav-bar" - }; - - exports.toolsMenu = { - parent: "menu_ToolsPopup" - }; - - exports.addBottomBar = function fx_addBottomBar(window, element) - { - let bar = _addBottomBar(window, window.document.getElementById("appcontent"), element); - if (bar) - { - let display = window.document.getElementById("statusbar-display"); - bar.changedFixed = display && !display.hasAttribute("fixed"); - if (bar.changedFixed) - display.setAttribute("fixed", "true"); - } - }; - - exports.removeBottomBar = function fx_removeBottomBar(window) - { - let bar = _removeBottomBar(window); - if (bar && bar.changedFixed) - window.document.getElementById("statusbar-display").removeAttribute("fixed"); - }; - - break; - } - - case "seamonkey": - { - exports.isKnownWindow = function sm_isKnownWindow(window) - { - let type = window.document.documentElement.getAttribute("windowtype"); - return (type == "navigator:browser" || type == "mail:3pane" || type == "mail:messageWindow"); - }; - - exports.addTab = function sm_addTab(window, url, event) - { - if (event || !("gBrowser" in window)) - window.openNewTabWith(url, null, null, event, false); - else - window.gBrowser.loadOneTab(url, {inBackground: false}); - }; - - exports.getBrowser = function sm_getBrowser(window) - { - if ("gBrowser" in window) - return window.gBrowser; - else if ("getMessageBrowser" in window) - return window.getMessageBrowser(); - else - return null; - }; - - exports.getCurrentLocation = function sm_getCurrentLocation(window) - { - if ("currentHeaderData" in window && "content-base" in window.currentHeaderData) - { - // This is a blog entry - return window.currentHeaderData["content-base"].headerValue; - } - else if ("currentHeaderData" in window && "from" in window.currentHeaderData) - { - // This is a mail/newsgroup entry - try - { - let headerParser = Cc["@mozilla.org/messenger/headerparser;1"].getService(Ci.nsIMsgHeaderParser); - let emailAddress = headerParser.extractHeaderAddressMailboxes(window.currentHeaderData.from.headerValue); - return "mailto:" + emailAddress.replace(/^[\s"]+/, "").replace(/[\s"]+$/, "").replace(/\s/g, "%20"); - } - catch(e) - { - return null; - } - } - else - { - let browser = exports.getBrowser(window); - return (browser ? browser.currentURI : null); - } - }; - - // for Seamonkey we have to ignore same document flag because of - // bug #1035171 (https://bugzilla.mozilla.org/show_bug.cgi?id=1035171) - let origAddBrowserLocationListener = exports.addBrowserLocationListener; - exports.addBrowserLocationListener = function sm_addBrowserLocationListener(window, callback, ignoreSameDoc) - { - origAddBrowserLocationListener(window, callback, false); - }; - - exports.contentContextMenu = ["contentAreaContextMenu", "mailContext"]; - - exports.defaultToolbarPosition = { - parent: ["PersonalToolbar", "msgToolbar"], - before: ["bookmarks-button", "button-junk"] - }; - - exports.statusbarPosition = { - parent: "status-bar" - }; - - exports.toolsMenu = { - parent: "taskPopup", - after: "downloadmgr" - }; - - exports.addBottomBar = function sm_addBottomBar(window, element) - { - _addBottomBar(window, window.document.getElementById("appcontent") || window.document.getElementById("messagepanebox"), element); - }; - - exports.removeBottomBar = _removeBottomBar; - - break; - } - - case "thunderbird": - { - exports.isKnownWindow = function tb_isKnownWindow(window) - { - let type = window.document.documentElement.getAttribute("windowtype"); - return (type == "mail:3pane" || type == "mail:messageWindow"); - }; - - exports.delayInitialization = true; - - exports.getBrowser = (window) => window.getBrowser(); - - exports.addTab = function tb_addTab(window, url, event) - { - let tabmail = window.document.getElementById("tabmail"); - if (!tabmail) - { - let wnd = Services.wm.getMostRecentWindow("mail:3pane"); - if (window) - tabmail = wnd.document.getElementById("tabmail"); - } - - if (tabmail) - tabmail.openTab("contentTab", {contentPage: url}); - else - { - window.openDialog("chrome://messenger/content/", "_blank", - "chrome,dialog=no,all", null, - { - tabType: "contentTab", - tabParams: {contentPage: url} - }); - } - }; - - exports.contentContextMenu = ["mailContext", "pageContextMenu"]; - - exports.defaultToolbarPosition = { - parent: "header-view-toolbar", - before: "hdrReplyButton", - addClass: "msgHeaderView-button" - }; - - exports.statusbarPosition = { - parent: "status-bar" - }; - - exports.toolsMenu = { - parent: "taskPopup", - after: "javaScriptConsole" - }; - - exports.getCurrentLocation = function getCurrentLocation(window) - { - let browser = exports.getBrowser(window); - if (!browser) - return null; - - if (browser.id == "messagepane" && "currentHeaderData" in window && "content-base" in window.currentHeaderData) - { - // This is a blog entry - return window.currentHeaderData["content-base"].headerValue; - } - else if (browser.id == "messagepane" && "currentHeaderData" in window && "from" in window.currentHeaderData) - { - // This is a mail/newsgroup entry - try - { - let headerParser = Cc["@mozilla.org/messenger/headerparser;1"].getService(Ci.nsIMsgHeaderParser); - let emailAddress = headerParser.extractHeaderAddressMailboxes(window.currentHeaderData.from.headerValue); - return "mailto:" + emailAddress.replace(/^[\s"]+/, "").replace(/[\s"]+$/, "").replace(/\s/g, "%20"); - } - catch(e) - { - return null; - } - } - else - return browser.currentURI; - } - - exports.addBottomBar = function tb_addBottomBar(window, element) - { - let browser = exports.getBrowser(window); - if (!browser) - return; - - let parent = window.document.getElementById("messagepanebox"); - if (!parent || !(parent.compareDocumentPosition(browser) & Ci.nsIDOMNode.DOCUMENT_POSITION_CONTAINED_BY)) - parent = browser.parentNode; - - _addBottomBar(window, parent, element); - }; - - exports.removeBottomBar = _removeBottomBar; - - let BrowserChangeListener = function(window, callback) - { - this.window = window; - this.callback = callback; - this.onSelect = this.onSelect.bind(this); - this.attach(); - }; - BrowserChangeListener.prototype = { - window: null, - callback: null, - currentBrowser: null, - - setBrowser: function(browser) - { - if (browser != this.currentBrowser) - { - let oldBrowser = this.currentBrowser; - this.currentBrowser = browser; - this.callback(oldBrowser, browser); - } - }, - - onSelect: function() - { - this.setBrowser(exports.getBrowser(this.window)); - }, - - attach: function() - { - this.onSelect(); - - let tabmail = this.window.document.getElementById("tabmail"); - if (tabmail) - tabmail.tabContainer.addEventListener("select", this.onSelect, false); - }, - detach: function() - { - let tabmail = this.window.document.getElementById("tabmail"); - if (tabmail) - tabmail.tabContainer.removeEventListener("select", this.onSelect, false); - - this.setBrowser(null); - } - }; - - exports.addBrowserLocationListener = function(/**Window*/ window, /**Function*/ callback, /**Boolean*/ ignoreSameDoc) - { - if (progressListeners.has(window)) - { - progressListeners.get(window).locationCallbacks.push(callback); - return; - } - - let callbacks = [callback]; - let dummy = function() {}; - let progressListener = - { - onLocationChange: function(progress, request, uri, flags) - { - if (!ignoreSameDoc || !flags || !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) - for (let i = 0; i < callbacks.length; i++) - callbacks[i](); - }, - onProgressChange: dummy, - onSecurityChange: dummy, - onStateChange: dummy, - onStatusChange: dummy, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]) - }; - let messageListener = - { - onStartHeaders: dummy, - onEndHeaders: function() - { - let browser = exports.getBrowser(window); - if (browser.id == "messagepane") - for (let i = 0; i < callbacks.length; i++) - callbacks[i](); - }, - onEndAttachments: dummy, - onBeforeShowHeaderPane: dummy - }; - - let listener = new BrowserChangeListener(window, function(oldBrowser, newBrowser) - { - if (oldBrowser) - oldBrowser.removeProgressListener(progressListener); - if (newBrowser) - newBrowser.addProgressListener(progressListener); - progressListener.onLocationChange(); - }); - listener.locationCallbacks = callbacks; - - if ("gMessageListeners" in window) - window.gMessageListeners.push(messageListener); - listener.messageListener = messageListener; - - progressListeners.set(window, listener); - }; - - exports.removeBrowserLocationListener = function(/**Window*/ window, /**Function*/ callback) - { - if (!progressListeners.has(window)) - return; - - let callbacks = progressListeners.get(window).locationCallbacks; - for (let i = 0; i < callbacks.length; i++) - if (callbacks[i] == callback) - callbacks.splice(i--, 1); - }; - - exports.removeBrowserLocationListeners = function(/**Window*/ window) - { - if (!progressListeners.has(window)) - return; - - let listener = progressListeners.get(window); - - let messageListener = listener.messageListener; - let index = ("gMessageListeners" in window ? window.gMessageListeners.indexOf(messageListener) : -1); - if (index >= 0) - window.gMessageListeners.splice(index, 1); - - listener.detach(); - progressListeners.delete(window); - }; - - // Make sure to close/reopen list of blockable items when the user changes tabs - let {WindowObserver} = require("windowObserver"); - new WindowObserver({ - listeners: new WeakMap(), - applyToWindow: function(window) - { - if (!exports.isKnownWindow(window) || this.listeners.has(window)) - return; - - let {Utils} = require("utils"); - Utils.runAsync(function() - { - let listener = new BrowserChangeListener(window, function(oldBrowser, newBrowser) - { - if (bottomBars.has(window)) - { - let {UI} = require("ui") - UI.toggleBottombar(window); - UI.toggleBottombar(window); - } - }); - this.listeners.set(window, listener); - }.bind(this)); - }, - removeFromWindow: function(window) - { - if (!this.listeners.has(window)) - return; - - let listener = this.listeners.get(window); - listener.detach(); - this.listeners.delete(window); - } - }); - - break; - } - - case "fennec2": - case "adblockbrowser": - { - exports.isKnownWindow = (window) => window.document.documentElement.id == "main-window"; - - exports.getBrowser = (window) => window.BrowserApp.selectedBrowser; - - exports.addTab = (window, url, event) => window.BrowserApp.addTab(url, {selected: true}); - - let BrowserChangeListener = function(window, callback) - { - this.window = window; - this.callback = callback; - this.onSelect = this.onSelect.bind(this); - this.attach = this.attach.bind(this); - if (window.BrowserApp.deck) - this.attach(); - else - window.addEventListener("UIReady", this.attach, false); - }; - BrowserChangeListener.prototype = { - window: null, - callback: null, - currentBrowser: null, - - setBrowser: function(browser) - { - if (browser != this.currentBrowser) - { - let oldBrowser = this.currentBrowser; - this.currentBrowser = browser; - this.callback(oldBrowser, browser); - } - }, - - onSelect: function() - { - let {Utils} = require("utils"); - Utils.runAsync(function() - { - this.setBrowser(exports.getBrowser(this.window)); - }.bind(this)); - }, - - attach: function() - { - this.window.removeEventListener("UIReady", this.attach, false); - this.onSelect(); - this.window.BrowserApp.deck.addEventListener("TabSelect", this.onSelect, false); - }, - detach: function() - { - this.window.BrowserApp.deck.removeEventListener("TabSelect", this.onSelect, false); - - this.setBrowser(null); - } - }; - - exports.addBrowserLocationListener = function ffn_addBrowserLocationListener(/**Window*/ window, /**Function*/ callback, /**Boolean*/ ignoreSameDoc) - { - if (progressListeners.has(window)) - { - progressListeners.get(window).locationCallbacks.push(callback); - return; - } - - let callbacks = [callback]; - let dummy = function() {}; - let progressListener = - { - onLocationChange: function(progress, request, uri, flags) - { - if (!ignoreSameDoc || !flags || !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) - for (let i = 0; i < callbacks.length; i++) - callbacks[i](); - }, - onProgressChange: dummy, - onSecurityChange: dummy, - onStateChange: dummy, - onStatusChange: dummy, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]) - }; - - let listener = new BrowserChangeListener(window, function(oldBrowser, newBrowser) - { - if (oldBrowser && typeof oldBrowser.removeProgressListener == "function") - oldBrowser.removeProgressListener(progressListener); - if (newBrowser && typeof newBrowser.removeProgressListener == "function") - newBrowser.addProgressListener(progressListener); - progressListener.onLocationChange(); - }); - listener.locationCallbacks = callbacks; - - progressListeners.set(window, listener); - }; - - exports.removeBrowserLocationListener = function ffn_removeBrowserLocationListener(/**Window*/ window, /**Function*/ callback) - { - if (!progressListeners.has(window)) - return; - - let callbacks = progressListeners.get(window).locationCallbacks; - for (let i = 0; i < callbacks.length; i++) - if (callbacks[i] == callback) - callbacks.splice(i--, 1); - }; - - exports.removeBrowserLocationListeners = function ffn_removeBrowserLocationListeners(/**Window*/ window) - { - if (!progressListeners.has(window)) - return; - - let listener = progressListeners.get(window); - listener.detach(); - progressListeners.delete(window); - }; - - let {Filter} = require("filterClasses"); - let {Prefs} = require("prefs"); - let {Policy} = require("contentPolicy"); - let {UI} = require("ui"); - let {Utils} = require("utils"); - - let toggleWhitelist = function(window) - { - if (!Prefs.enabled) - { - Prefs.enabled = true; - return; - } - - let location = exports.getCurrentLocation(window); - let host = null; - if (location instanceof Ci.nsIURL && Policy.isBlockableScheme(location)) - { - try - { - host = location.host.replace(/^www\./, ""); - } catch (e) {} - } - - if (!host) - return; - - if (Policy.isWhitelisted(location.spec)) - UI.removeWhitelist(window); - else - UI.toggleFilter(Filter.fromText("@@||" + host + "^$document")); - }; - - let menuItem = null; - onShutdown.add(function() - { - let window = null; - for (window of UI.applicationWindows) - break; - - if (window && menuItem) - window.NativeWindow.menu.remove(menuItem); - }); - - UI.updateIconState = function fmn_updateIconState(window, icon) - { - if (menuItem !== null) - { - window.NativeWindow.menu.remove(menuItem); - menuItem = null; - } - - let action; - let host = null; - if (Prefs.enabled) - { - let location = exports.getCurrentLocation(window); - if (location instanceof Ci.nsIURL && Policy.isBlockableScheme(location)) - { - try - { - host = location.host.replace(/^www\./, ""); - } catch (e) {} - } - if (!host) - return; - - if (host && Policy.isWhitelisted(location.spec)) - action = "enable_site"; - else if (host) - action = "disable_site"; - } - else - action = "enable"; - - let actionText = Utils.getString("mobile_menu_" + action); - if (host) - actionText = actionText.replace(/\?1\?/g, host); - - let iconUrl = require("info").addonRoot + "icon64.png"; - menuItem = window.NativeWindow.menu.add(actionText, iconUrl, toggleWhitelist.bind(null, window)); - }; - - UI.openSubscriptionDialog = function(window, url, title, mainURL, mainTitle) - { - let dialogTitle = this.overlay.attributes.subscriptionDialogTitle; - let dialogMessage = this.overlay.attributes.subscriptionDialogMessage.replace(/\?1\?/, title).replace(/\?2\?/, url); - if (Utils.confirm(window, dialogMessage, dialogTitle)) - this.setSubscription(url, title); - }; - - UI.openFiltersDialog = function() - { - let window = UI.currentWindow; - if (!window) - return - - let browser = exports.addTab(window, "about:addons").browser; - browser.addEventListener("load", function openAddonPrefs(event) - { - browser.removeEventListener("load", openAddonPrefs, true); - Utils.runAsync(function() - { - // The page won't be ready until the add-on manager data is loaded so we call this method - // to know when the data will be ready. - AddonManager.getAddonsByTypes(["extension", "theme", "locale"], function() - { - let event = new Event("Event"); - event.initEvent("popstate", true, false); - event.state = {id: require("info").addonID}; - browser._contentWindow.dispatchEvent(event); - }); - }); - }, true); - }; - - break; - } -} diff --git a/data/extensions/spyblock@gnu.org/lib/child/bootstrap.js b/data/extensions/spyblock@gnu.org/lib/child/bootstrap.js deleted file mode 100644 index 477ca44..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/bootstrap.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -(function() -{ - const Cc = Components.classes; - const Ci = Components.interfaces; - const Cr = Components.results; - const Cu = Components.utils; - - let {Loader, main, unload} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}); - let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - - Cu.importGlobalProperties(["atob", "btoa", "File", "URL", "URLSearchParams", - "TextDecoder", "TextEncoder"]); - - let shutdownHandlers = []; - let onShutdown = - { - done: false, - add: function(handler) - { - if (shutdownHandlers.indexOf(handler) < 0) - shutdownHandlers.push(handler); - }, - remove: function(handler) - { - let index = shutdownHandlers.indexOf(handler); - if (index >= 0) - shutdownHandlers.splice(index, 1); - } - }; - - function init() - { - let url = new URL(Components.stack.filename); - let params = new URLSearchParams(url.search.substr(1)); - let info = JSON.parse(params.get("info")); - - let loader = Loader({ - paths: { - "": info.addonRoot + "lib/" - }, - globals: { - Components, Cc, Ci, Cu, Cr, atob, btoa, File, URL, URLSearchParams, - TextDecoder, TextEncoder, onShutdown - }, - modules: {"info": info, "messageManager": this}, - id: info.addonID - }); - onShutdown.add(() => unload(loader, "disable")) - - main(loader, "child/main"); - } - - function shutdown(message) - { - if (message.data == Components.stack.filename) - { - onShutdown.done = true; - for (let i = shutdownHandlers.length - 1; i >= 0; i --) - { - try - { - shutdownHandlers[i](); - } - catch (e) - { - Cu.reportError(e); - } - } - shutdownHandlers = null; - } - } - - addMessageListener("AdblockPlus:Shutdown", shutdown); - onShutdown.add(() => - { - removeMessageListener("AdblockPlus:Shutdown", shutdown); - }); - - init(); -})(); diff --git a/data/extensions/spyblock@gnu.org/lib/child/contentPolicy.js b/data/extensions/spyblock@gnu.org/lib/child/contentPolicy.js deleted file mode 100644 index 97ea7b1..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/contentPolicy.js +++ /dev/null @@ -1,518 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Content policy implementation, responsible for blocking things. - */ - -"use strict"; - -try -{ - // Hack: SDK loader masks our Components object with a getter. - let proto = Object.getPrototypeOf(this); - let property = Object.getOwnPropertyDescriptor(proto, "Components"); - if (property && property.get) - delete proto.Components; -} -catch (e) -{ - Cu.reportError(e); -} - -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - -let {port} = require("messaging"); -let {Utils} = require("utils"); -let {getFrames, isPrivate, getRequestWindow} = require("child/utils"); -let {objectMouseEventHander} = require("child/objectTabs"); -let {RequestNotifier} = require("child/requestNotifier"); - -/** - * Randomly generated class name, to be applied to collapsed nodes. - * @type Promise.<string> - */ -let collapsedClass = port.emitWithResponse("getCollapsedClass"); - -/** - * Maps numerical content type IDs to strings. - * @type Map.<number,string> - */ -let types = new Map(); - -/** - * Contains nodes stored by storeNodes() mapped by their IDs. - * @type Map.<string,DOMNode[]> - */ -let storedNodes = new Map(); - -/** - * Process-dependent prefix to be used for unique nodes identifiers returned - * by storeNodes(). - * @type string - */ -let nodesIDPrefix = Services.appinfo.processID + " "; - -/** - * Counter used to generate unique nodes identifiers in storeNodes(). - * @type number - */ -let maxNodesID = 0; - -port.on("deleteNodes", onDeleteNodes); -port.on("refilterNodes", onRefilterNodes); - -/** - * Processes parent's response to the ShouldAllow message. - * @param {nsIDOMWindow} window window that the request is associated with - * @param {nsIDOMElement} node DOM element that the request is associated with - * @param {Object|undefined} response object received as response - * @return {Boolean} false if the request should be blocked - */ -function processPolicyResponse(window, node, response) -{ - if (typeof response == "undefined") - return true; - - let {allow, collapse, hits} = response; - let isObject = false; - for (let hit of hits) - { - if (hit.contentType == "OBJECT") - isObject = true; - - let context = node; - if (typeof hit.frameIndex == "number") - { - context = window; - for (let i = 0; i < hit.frameIndex; i++) - context = context.parent; - context = context.document; - } - RequestNotifier.addNodeData(context, window.top, hit); - } - - if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) - { - // Track mouse events for objects - if (allow && isObject) - { - node.addEventListener("mouseover", objectMouseEventHander, true); - node.addEventListener("mouseout", objectMouseEventHander, true); - } - - if (collapse) - schedulePostProcess(node); - } - return allow; -} - -/** - * Checks whether a request should be allowed, hides it if necessary - * @param {nsIDOMWindow} window - * @param {nsIDOMElement} node - * @param {String} contentType - * @param {String} location location of the request, filter key if contentType is ELEMHIDE - * @return {Boolean} false if the request should be blocked - */ -let shouldAllow = exports.shouldAllow = function(window, node, contentType, location) -{ - return processPolicyResponse(window, node, port.emitSync("shouldAllow", { - contentType, - location, - frames: getFrames(window), - isPrivate: isPrivate(window) - })); -}; - -/** - * Asynchronously checks whether a request should be allowed. - * @param {nsIDOMWindow} window - * @param {nsIDOMElement} node - * @param {String} contentType - * @param {String} location location of the request, filter key if contentType is ELEMHIDE - * @param {Function} callback callback to be called with a boolean value, if - * false the request should be blocked - */ -let shouldAllowAsync = exports.shouldAllowAsync = function(window, node, contentType, location, callback) -{ - port.emitWithResponse("shouldAllow", { - contentType, - location, - frames: getFrames(window), - isPrivate: isPrivate(window) - }).then(response => - { - callback(processPolicyResponse(window, node, response)); - }); -}; - -/** - * Stores nodes and generates a unique ID for them that can be used for - * Policy.refilterNodes() later. It's important that Policy.deleteNodes() is - * called later, otherwise the nodes will be leaked. - * @param {DOMNode[]} nodes list of nodes to be stored - * @return {string} unique ID for the nodes - */ -let storeNodes = exports.storeNodes = function(nodes) -{ - let id = nodesIDPrefix + (++maxNodesID); - storedNodes.set(id, nodes); - return id; -}; - -/** - * Called via message whenever Policy.deleteNodes() is called in the parent. - */ -function onDeleteNodes(id, sender) -{ - storedNodes.delete(id); -} - -/** - * Called via message whenever Policy.refilterNodes() is called in the parent. - */ -function onRefilterNodes({nodesID, entry}, sender) -{ - let nodes = storedNodes.get(nodesID); - if (nodes) - for (let node of nodes) - if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) - Utils.runAsync(refilterNode.bind(this, node, entry)); -} - -/** - * Re-checks filters on an element. - */ -function refilterNode(/**Node*/ node, /**Object*/ entry) -{ - let wnd = Utils.getWindow(node); - if (!wnd || wnd.closed) - return; - - if (entry.type == "OBJECT") - { - node.removeEventListener("mouseover", objectMouseEventHander, true); - node.removeEventListener("mouseout", objectMouseEventHander, true); - } - - shouldAllow(wnd, node, entry.type, entry.location, (allow) => { - // Force node to be collapsed - if (!allow) - schedulePostProcess(node) - }); -} - -/** - * Actual nsIContentPolicy and nsIChannelEventSink implementation - * @class - */ -var PolicyImplementation = -{ - classDescription: "Adblock Plus content policy", - classID: Components.ID("cfeaabe6-1dd1-11b2-a0c6-cb5c268894c9"), - contractID: "@adblockplus.org/abp/policy;1", - xpcom_categories: ["content-policy", "net-channel-event-sinks"], - - /** - * Registers the content policy on startup. - */ - init: function() - { - // Populate types map - let iface = Ci.nsIContentPolicy; - for (let name in iface) - if (name.indexOf("TYPE_") == 0 && name != "TYPE_DATAREQUEST") - types.set(iface[name], name.substr(5)); - - let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - registrar.registerFactory(this.classID, this.classDescription, this.contractID, this); - - let catMan = Utils.categoryManager; - for (let category of this.xpcom_categories) - catMan.addCategoryEntry(category, this.contractID, this.contractID, false, true); - - Services.obs.addObserver(this, "document-element-inserted", true); - - onShutdown.add(() => - { - Services.obs.removeObserver(this, "document-element-inserted"); - - for (let category of this.xpcom_categories) - catMan.deleteCategoryEntry(category, this.contractID, false); - - registrar.unregisterFactory(this.classID, this); - }); - }, - - // - // nsISupports interface implementation - // - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy, Ci.nsIObserver, - Ci.nsIChannelEventSink, Ci.nsIFactory, Ci.nsISupportsWeakReference]), - - // - // nsIContentPolicy interface implementation - // - - shouldLoad: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra) - { - // Ignore requests without context and top-level documents - if (!node || contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT) - return Ci.nsIContentPolicy.ACCEPT; - - // Bail out early for chrome: an resource: URLs, this is a work-around for - // https://bugzil.la/1127744 and https://bugzil.la/1247640 - let location = Utils.unwrapURL(contentLocation); - if (location.schemeIs("chrome") || location.schemeIs("resource")) - return Ci.nsIContentPolicy.ACCEPT; - - // Ignore standalone objects - if (contentType == Ci.nsIContentPolicy.TYPE_OBJECT && node.ownerDocument && !/^text\/|[+\/]xml$/.test(node.ownerDocument.contentType)) - return Ci.nsIContentPolicy.ACCEPT; - - let wnd = Utils.getWindow(node); - if (!wnd) - return Ci.nsIContentPolicy.ACCEPT; - - // Data loaded by plugins should be associated with the document - if (contentType == Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST && node instanceof Ci.nsIDOMElement) - node = node.ownerDocument; - - // Fix type for objects misrepresented as frames or images - if (contentType != Ci.nsIContentPolicy.TYPE_OBJECT && (node instanceof Ci.nsIDOMHTMLObjectElement || node instanceof Ci.nsIDOMHTMLEmbedElement)) - contentType = Ci.nsIContentPolicy.TYPE_OBJECT; - - let result = shouldAllow(wnd, node, types.get(contentType), location.spec); - return (result ? Ci.nsIContentPolicy.ACCEPT : Ci.nsIContentPolicy.REJECT_REQUEST); - }, - - shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode, mimeType, extra) - { - return Ci.nsIContentPolicy.ACCEPT; - }, - - // - // nsIObserver interface implementation - // - _openers: new WeakMap(), - _alreadyLoaded: Symbol(), - - observe: function(subject, topic, data, uri) - { - switch (topic) - { - case "document-element-inserted": - { - let window = subject.defaultView; - if (!window) - return; - - let type = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .itemType; - if (type != Ci.nsIDocShellTreeItem.typeContent) - return; - - let opener = this._openers.get(window); - if (opener == this._alreadyLoaded) - { - // This window has loaded already, ignore it regardless of whether - // window.opener is still set. - return; - } - - if (opener && Cu.isDeadWrapper(opener)) - opener = null; - - if (!opener) - { - // We don't know the opener for this window yet, try to find it - opener = window.opener; - if (!opener) - return; - - // The opener might be an intermediate window, get the real one - while (opener.location == "about:blank" && opener.opener) - opener = opener.opener; - - this._openers.set(window, opener); - - let forgetPopup = event => - { - subject.removeEventListener("DOMContentLoaded", forgetPopup); - this._openers.set(window, this._alreadyLoaded); - }; - subject.addEventListener("DOMContentLoaded", forgetPopup); - } - - if (!uri) - uri = window.location.href; - if (!shouldAllow(opener, opener.document, "POPUP", uri)) - { - window.stop(); - Utils.runAsync(() => window.close()); - } - else if (uri == "about:blank") - { - // An about:blank pop-up most likely means that a load will be - // initiated asynchronously. Wait for that. - Utils.runAsync(() => - { - let channel = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsIDocumentLoader) - .documentChannel; - if (channel) - this.observe(subject, topic, data, channel.URI.spec); - }); - } - break; - } - } - }, - - // - // nsIChannelEventSink interface implementation - // - - asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) - { - let async = false; - try - { - // nsILoadInfo.contentPolicyType was introduced in Gecko 35, then - // renamed to nsILoadInfo.externalContentPolicyType in Gecko 44. - let loadInfo = oldChannel.loadInfo; - let contentType = ("externalContentPolicyType" in loadInfo ? - loadInfo.externalContentPolicyType : loadInfo.contentPolicyType); - if (!contentType) - return; - - let wnd = getRequestWindow(newChannel); - if (!wnd) - return; - - if (contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT) - { - if (wnd.history.length <= 1 && wnd.opener) - { - // Special treatment for pop-up windows - this will close the window - // rather than preventing the redirect. Note that we might not have - // seen the original channel yet because the redirect happened before - // the async code in observe() had a chance to run. - this.observe(wnd.document, "document-element-inserted", null, oldChannel.URI.spec); - this.observe(wnd.document, "document-element-inserted", null, newChannel.URI.spec); - } - return; - } - - shouldAllowAsync(wnd, wnd.document, types.get(contentType), newChannel.URI.spec, function(allow) - { - callback.onRedirectVerifyCallback(allow ? Cr.NS_OK : Cr.NS_BINDING_ABORTED); - }); - async = true; - } - catch (e) - { - // We shouldn't throw exceptions here - this will prevent the redirect. - Cu.reportError(e); - } - finally - { - if (!async) - callback.onRedirectVerifyCallback(Cr.NS_OK); - } - }, - - // - // nsIFactory interface implementation - // - - createInstance: function(outer, iid) - { - if (outer) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.QueryInterface(iid); - } -}; -PolicyImplementation.init(); - -/** - * Nodes scheduled for post-processing (might be null). - * @type Node[] - */ -let scheduledNodes = null; - -/** - * Schedules a node for post-processing. - */ -function schedulePostProcess(/**Element*/ node) -{ - if (scheduledNodes) - scheduledNodes.push(node); - else - { - scheduledNodes = [node]; - Utils.runAsync(postProcessNodes); - } -} - -/** - * Processes nodes scheduled for post-processing (typically hides them). - */ -function postProcessNodes() -{ - collapsedClass.then(cls => - { - let nodes = scheduledNodes; - scheduledNodes = null; - - // Resolving class is async initially so the nodes might have already been - // processed in the meantime. - if (!nodes) - return; - - for (let node of nodes) - { - // adjust frameset's cols/rows for frames - let parentNode = node.parentNode; - if (parentNode && parentNode instanceof Ci.nsIDOMHTMLFrameSetElement) - { - let hasCols = (parentNode.cols && parentNode.cols.indexOf(",") > 0); - let hasRows = (parentNode.rows && parentNode.rows.indexOf(",") > 0); - if ((hasCols || hasRows) && !(hasCols && hasRows)) - { - let index = -1; - for (let frame = node; frame; frame = frame.previousSibling) - if (frame instanceof Ci.nsIDOMHTMLFrameElement || frame instanceof Ci.nsIDOMHTMLFrameSetElement) - index++; - - let property = (hasCols ? "cols" : "rows"); - let weights = parentNode[property].split(","); - weights[index] = "0"; - parentNode[property] = weights.join(","); - } - } - else - node.classList.add(cls); - } - }); -} diff --git a/data/extensions/spyblock@gnu.org/lib/child/contextMenu.js b/data/extensions/spyblock@gnu.org/lib/child/contextMenu.js deleted file mode 100644 index 297ef3e..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/contextMenu.js +++ /dev/null @@ -1,137 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); - -let {Utils} = require("utils"); -let {RequestNotifier} = require("child/requestNotifier"); -let {storeNodes} = require("child/contentPolicy"); - -/** - * Determines the context menu entries to be shown for a contextmenu event. - * @param {Event} event - * @return {Array} - */ -function getContextInfo(event) -{ - let items = []; - let target = event.target; - if (target.localName == "menupopup" && target.triggerNode) - { - // SeaMonkey gives us the context menu's popupshowing event - target = target.triggerNode; - } - if (target instanceof Ci.nsIDOMHTMLMapElement || target instanceof Ci.nsIDOMHTMLAreaElement) - { - // HTML image maps will usually receive events when the mouse pointer is - // over a different element, get the real event target. - let rect = target.getClientRects()[0]; - target = target.ownerDocument.elementFromPoint(Math.max(rect.left, 0), Math.max(rect.top, 0)); - } - - if (!target) - return items; - - let addMenuItem = function([node, nodeData]) - { - let nodeID = null; - if (node && node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) - nodeID = storeNodes([node]); - items.push([nodeID, nodeData]); - }.bind(this); - - // Look up data that we have for the node - let data = RequestNotifier.getDataForNode(target); - let hadImage = false; - if (data && !data[1].filter) - { - addMenuItem(data); - hadImage = (data[1].type == "IMAGE"); - } - - // Look for frame data - let wnd = Utils.getWindow(target); - if (wnd.frameElement) - { - let data = RequestNotifier.getDataForNode(wnd.frameElement, true); - if (data && !data[1].filter) - addMenuItem(data); - } - - // Look for a background image - if (!hadImage) - { - let extractImageURL = function(computedStyle, property) - { - let value = computedStyle.getPropertyCSSValue(property); - // CSSValueList - if ("length" in value && value.length >= 1) - value = value[0]; - // CSSValuePrimitiveType - if ("primitiveType" in value && value.primitiveType == value.CSS_URI) - return Utils.unwrapURL(value.getStringValue()).spec; - - return null; - }; - - let node = target; - while (node) - { - if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) - { - let style = wnd.getComputedStyle(node, ""); - let bgImage = extractImageURL(style, "background-image") || extractImageURL(style, "list-style-image"); - if (bgImage) - { - let data = RequestNotifier.getDataForNode(wnd.document, true, "IMAGE", bgImage); - if (data && !data[1].filter) - { - addMenuItem(data); - break; - } - } - } - - node = node.parentNode; - } - } - - return items; -}; - -let ContextMenuObserver = -{ - observe: function(subject, topic, data) - { - if (subject.wrappedJSObject) - subject = subject.wrappedJSObject; - - if (subject.addonInfo) - subject.addonInfo.adblockplus = getContextInfo(subject.event); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIObserver]) -}; - -Services.obs.addObserver(ContextMenuObserver, "content-contextmenu", true); -Services.obs.addObserver(ContextMenuObserver, "AdblockPlus:content-contextmenu", true); -onShutdown.add(() => { - Services.obs.removeObserver(ContextMenuObserver, "content-contextmenu"); - Services.obs.removeObserver(ContextMenuObserver, "AdblockPlus:content-contextmenu"); -}); diff --git a/data/extensions/spyblock@gnu.org/lib/child/dataCollector.js b/data/extensions/spyblock@gnu.org/lib/child/dataCollector.js deleted file mode 100644 index 09c334a..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/dataCollector.js +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Collects some data for a content window, to be attached to - * issue reports. - */ - -"use strict"; - -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); -let {Task} = Cu.import("resource://gre/modules/Task.jsm", {}); -let {PrivateBrowsingUtils} = Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm", {}); - -let {port} = require("messaging"); -let {Utils} = require("utils"); - -port.on("collectData", onCollectData); - -function onCollectData({outerWindowID, screenshotWidth}, sender) -{ - let window = Services.wm.getOuterWindowWithId(outerWindowID); - if (window) - { - return Task.spawn(function*() - { - let data = {}; - data.isPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window); - data.opener = window.opener ? window.opener.location.href : null; - data.referrer = window.document.referrer; - data.frames = yield scanFrames(window); - data.screenshot = yield createScreenshot(window, screenshotWidth); - return data; - }); - } -} - -function scanFrames(window) -{ - let frames = []; - for (let i = 0; i < window.frames.length; i++) - { - let frame = window.frames[i]; - frames.push({ - url: frame.location.href, - frames: scanFrames(frame) - }); - } - return frames; -} - -function* createScreenshot(window, screenshotWidth) -{ - let canvas = window.document.createElement("canvas"); - canvas.width = screenshotWidth; - - let context = canvas.getContext("2d"); - let wndWidth = window.document.documentElement.scrollWidth; - let wndHeight = window.document.documentElement.scrollHeight; - - // Copy scaled screenshot of the webpage, according to the specified width. - - // Gecko doesn't like sizes more than 64k, restrict to 30k to be on the safe side. - // Also, make sure height is at most five times the width to keep image size down. - let copyWidth = Math.min(wndWidth, 30000); - let copyHeight = Math.min(wndHeight, 30000, copyWidth * 5); - let copyX = Math.max(Math.min(window.scrollX - copyWidth / 2, wndWidth - copyWidth), 0); - let copyY = Math.max(Math.min(window.scrollY - copyHeight / 2, wndHeight - copyHeight), 0); - - let scalingFactor = screenshotWidth / copyWidth; - canvas.height = copyHeight * scalingFactor; - - context.save(); - context.scale(scalingFactor, scalingFactor); - context.drawWindow(window, copyX, copyY, copyWidth, copyHeight, "rgb(255,255,255)"); - context.restore(); - - // Reduce colors - let pixelData = context.getImageData(0, 0, canvas.width, canvas.height); - let data = pixelData.data; - let mapping = [0x00, 0x55, 0xAA, 0xFF]; - for (let i = 0; i < data.length; i++) - { - data[i] = mapping[data[i] >> 6]; - - if (i % 5000 == 0) - { - // Take a break every 5000 bytes to prevent browser hangs - yield new Promise((resolve, reject) => Utils.runAsync(resolve)); - } - } - - return pixelData; -} diff --git a/data/extensions/spyblock@gnu.org/lib/child/elemHide.js b/data/extensions/spyblock@gnu.org/lib/child/elemHide.js deleted file mode 100644 index 988adee..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/elemHide.js +++ /dev/null @@ -1,403 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Serves CSS for element hiding and processes hits. - */ - -try -{ - // Hack: SDK loader masks our Components object with a getter. - let proto = Object.getPrototypeOf(this); - let property = Object.getOwnPropertyDescriptor(proto, "Components"); - if (property && property.get) - delete proto.Components; -} -catch (e) -{ - Cu.reportError(e); -} - -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - -let {shouldAllowAsync} = require("child/contentPolicy"); -let {getFrames, isPrivate, getRequestWindow} = require("child/utils"); -let {RequestNotifier} = require("child/requestNotifier"); -let {port} = require("messaging"); -let {Utils} = require("utils"); - -const notImplemented = () => Cr.NS_ERROR_NOT_IMPLEMENTED; - -/** - * about: URL module used to count hits. - * @class - */ -let AboutHandler = -{ - classID: Components.ID("{55fb7be0-1dd2-11b2-98e6-9e97caf8ba67}"), - classDescription: "Element hiding hit registration protocol handler", - aboutPrefix: "abp-elemhide", - - /** - * Registers handler on startup. - */ - init: function() - { - let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - registrar.registerFactory(this.classID, this.classDescription, - "@mozilla.org/network/protocol/about;1?what=" + this.aboutPrefix, this); - onShutdown.add(function() - { - registrar.unregisterFactory(this.classID, this); - }.bind(this)); - }, - - // - // Factory implementation - // - - createInstance: function(outer, iid) - { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - - return this.QueryInterface(iid); - }, - - // - // About module implementation - // - - getURIFlags: function(uri) - { - return Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; - }, - - newChannel: function(uri, loadInfo) - { - let match = /\?hit(\d+)$/.exec(uri.path); - if (match) - return new HitRegistrationChannel(uri, loadInfo, match[1]); - - match = /\?css(?:=(.*?))?(&specificonly)?$/.exec(uri.path); - if (match) - { - return new StyleDataChannel(uri, loadInfo, - match[1] ? decodeURIComponent(match[1]) : null, !!match[2]); - } - - throw Cr.NS_ERROR_FAILURE; - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, Ci.nsIAboutModule]) -}; -AboutHandler.init(); - -/** - * Base class for channel implementations, subclasses usually only need to - * override BaseChannel._getResponse() method. - * @constructor - */ -function BaseChannel(uri, loadInfo) -{ - this.URI = this.originalURI = uri; - this.loadInfo = loadInfo; -} -BaseChannel.prototype = { - URI: null, - originalURI: null, - contentCharset: "utf-8", - contentLength: 0, - contentType: null, - owner: Utils.systemPrincipal, - securityInfo: null, - notificationCallbacks: null, - loadFlags: 0, - loadGroup: null, - name: null, - status: Cr.NS_OK, - - _getResponse: notImplemented, - - _checkSecurity: function() - { - if (!this.loadInfo.triggeringPrincipal.equals(Utils.systemPrincipal)) - throw Cr.NS_ERROR_FAILURE; - }, - - asyncOpen: function(listener, context) - { - Promise.resolve(this._getResponse()).then(data => - { - let stream = Cc["@mozilla.org/io/string-input-stream;1"] - .createInstance(Ci.nsIStringInputStream); - stream.setData(data, data.length); - - try - { - listener.onStartRequest(this, context); - } - catch(e) - { - // Listener failing isn't our problem - } - - try - { - listener.onDataAvailable(this, context, stream, 0, stream.available()); - } - catch(e) - { - // Listener failing isn't our problem - } - - try - { - listener.onStopRequest(this, context, Cr.NS_OK); - } - catch(e) - { - // Listener failing isn't our problem - } - }); - }, - - asyncOpen2: function(listener) - { - this._checkSecurity(); - this.asyncOpen(listener, null); - }, - - open: function() - { - let data = this._getResponse(); - if (typeof data.then == "function") - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - - let stream = Cc["@mozilla.org/io/string-input-stream;1"] - .createInstance(Ci.nsIStringInputStream); - stream.setData(data, data.length); - return stream; - }, - - open2: function() - { - this._checkSecurity(); - return this.open(); - }, - - isPending: () => false, - cancel: notImplemented, - suspend: notImplemented, - resume: notImplemented, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest]) -}; - -/** - * Channel returning CSS data for the global as well as site-specific stylesheet. - * @constructor - */ -function StyleDataChannel(uri, loadInfo, domain, specificOnly) -{ - BaseChannel.call(this, uri, loadInfo); - this._domain = domain; - this._specificOnly = specificOnly; -} -StyleDataChannel.prototype = { - __proto__: BaseChannel.prototype, - contentType: "text/css", - _domain: null, - - _getResponse: function() - { - function escapeChar(match) - { - return "\\" + match.charCodeAt(0).toString(16) + " "; - } - - // Would be great to avoid sync messaging here but nsIStyleSheetService - // insists on opening channels synchronously. - let [selectors, keys] = (this._domain ? - port.emitSync("getSelectorsForDomain", [this._domain, this._specificOnly]) : - port.emitSync("getUnconditionalSelectors")); - - let cssPrefix = "{-moz-binding: url(about:abp-elemhide?hit"; - let cssSuffix = "#dummy) !important;}\n"; - let result = []; - - for (let i = 0; i < selectors.length; i++) - { - let selector = selectors[i]; - let key = keys[i]; - result.push(selector.replace(/[^\x01-\x7F]/g, escapeChar), - cssPrefix, key, cssSuffix); - } - - return result.join(""); - } -}; - -/** - * Channel returning data for element hiding hits. - * @constructor - */ -function HitRegistrationChannel(uri, loadInfo, key) -{ - BaseChannel.call(this, uri, loadInfo); - this.key = key; -} -HitRegistrationChannel.prototype = { - __proto__: BaseChannel.prototype, - key: null, - contentType: "text/xml", - - _getResponse: function() - { - let window = getRequestWindow(this); - port.emitWithResponse("registerElemHideHit", { - key: this.key, - frames: getFrames(window), - isPrivate: isPrivate(window) - }).then(hit => - { - if (hit) - RequestNotifier.addNodeData(window.document, window.top, hit); - }); - return "<bindings xmlns='http://www.mozilla.org/xbl'/>"; - } -}; - -let observer = { - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsIObserver, Ci.nsISupportsWeakReference - ]), - - topic: "document-element-inserted", - styleURL: Utils.makeURI("about:abp-elemhide?css"), - sheet: null, - - init: function() - { - Services.obs.addObserver(this, this.topic, true); - onShutdown.add(() => - { - Services.obs.removeObserver(this, this.topic); - }); - - port.on("elemhideupdate", () => - { - this.sheet = null; - }); - }, - - observe: function(subject, topic, data) - { - if (topic != this.topic) - return; - - let window = subject.defaultView; - if (!window) - { - // This is typically XBL bindings and SVG images, but also real - // documents occasionally - probably due to speculative loading? - return; - } - let type = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .itemType; - if (type != Ci.nsIDocShellTreeItem.typeContent) - return; - - port.emitWithResponse("elemhideEnabled", { - frames: getFrames(window), - isPrivate: isPrivate(window) - }).then(({ - enabled, contentType, docDomain, thirdParty, location, filter, - filterType - }) => - { - if (Cu.isDeadWrapper(window)) - { - // We are too late, the window is gone already. - return; - } - - if (enabled) - { - let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - - // If we have a filter hit at this point then it must be a $generichide - // filter - apply only specific element hiding filters. - let specificOnly = !!filter; - if (!specificOnly) - { - if (!this.sheet) - { - this.sheet = Utils.styleService.preloadSheet(this.styleURL, - Ci.nsIStyleSheetService.USER_SHEET); - } - - try - { - utils.addSheet(this.sheet, Ci.nsIStyleSheetService.USER_SHEET); - } - catch (e) - { - // Ignore NS_ERROR_ILLEGAL_VALUE - it will be thrown if we try to add - // the stylesheet multiple times to the same document (the observer - // will be notified twice for some documents). - if (e.result != Cr.NS_ERROR_ILLEGAL_VALUE) - throw e; - } - } - - let host = window.location.hostname; - if (host) - { - try - { - let suffix = "=" + encodeURIComponent(host); - if (specificOnly) - suffix += "&specificonly"; - utils.loadSheetUsingURIString(this.styleURL.spec + suffix, - Ci.nsIStyleSheetService.USER_SHEET); - } - catch (e) - { - // Ignore NS_ERROR_ILLEGAL_VALUE - it will be thrown if we try to add - // the stylesheet multiple times to the same document (the observer - // will be notified twice for some documents). - if (e.result != Cr.NS_ERROR_ILLEGAL_VALUE) - throw e; - } - } - } - - if (filter) - { - RequestNotifier.addNodeData(window.document, window.top, { - contentType, docDomain, thirdParty, location, filter, filterType - }); - } - }); - } -}; -observer.init(); diff --git a/data/extensions/spyblock@gnu.org/lib/child/elemHideEmulation.js b/data/extensions/spyblock@gnu.org/lib/child/elemHideEmulation.js deleted file mode 100644 index 7c4ee17..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/elemHideEmulation.js +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -(function() -{ - let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - - let {port} = require("messaging"); - let {getFrames, isPrivate} = require("child/utils"); - let {RequestNotifier} = require("child/requestNotifier"); - - function getFilters(window, callback) - { - let message = { - frames: getFrames(window), - payload: { - type: "filters.get", - what: "elemhideemulation" - } - }; - port.emitWithResponse("ext_message", message).then(callback); - } - - function addUserCSS(window, cssCode) - { - let uri = Services.io.newURI("data:text/css," + encodeURIComponent(cssCode), - null, null); - let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - utils.loadSheet(uri, Ci.nsIDOMWindowUtils.USER_SHEET); - } - - function initElemHideEmulation() - { - let scope = Object.assign({}, require("common")); - Services.scriptloader.loadSubScript( - "chrome://adblockplus/content/elemHideEmulation.js", scope); - - let onContentWindow = (subject, topic, data) => - { - if (!(subject instanceof Ci.nsIDOMWindow)) - return; - - let onReady = event => - { - subject.removeEventListener("load", onReady); - let handler = new scope.ElemHideEmulation( - subject, getFilters.bind(null, subject), (selectors, filters) => - { - if (selectors.length == 0) - return; - - addUserCSS(subject, selectors.map( - selector => selector + "{display: none !important;}" - ).join("\n")); - - if (!isPrivate(subject)) - port.emit("addHits", filters); - - let docDomain = null; - try - { - // We are calling getFrames() here because it will consider - // "inheritance" for about:blank and data: frames. - docDomain = new URL(getFrames(subject)[0].location).hostname; - } - catch (e) - { - // Invalid URL? - } - - for (let i = 0; i < filters.length; i++) - { - RequestNotifier.addNodeData(subject.document, subject.top, { - contentType: "ELEMHIDE", - docDomain: docDomain, - thirdParty: false, - location: "##" + selectors[i], - filter: filters[i], - filterType: "elemhideemulation" - }); - } - } - ); - - handler.apply(); - }; - - subject.addEventListener("load", onReady); - }; - - Services.obs.addObserver(onContentWindow, "content-document-global-created", - false); - onShutdown.add(() => - { - Services.obs.removeObserver(onContentWindow, - "content-document-global-created"); - }); - } - - initElemHideEmulation(); -})(); diff --git a/data/extensions/spyblock@gnu.org/lib/child/flasher.js b/data/extensions/spyblock@gnu.org/lib/child/flasher.js deleted file mode 100644 index 492f4e0..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/flasher.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Draws a blinking border for a list of matching elements. - */ - -function Flasher(elements, scrollToItem) -{ - if (scrollToItem && elements[0].ownerDocument) - { - // Ensure that at least one element is visible when flashing - elements[0].scrollIntoView(); - } - - this.elements = elements; - this.count = 0; - - this.doFlash(); - -} -Flasher.prototype = -{ - elements: null, - count: 0, - timer: null, - - doFlash: function() - { - if (this.count >= 12) - { - this.stop(); - return; - } - - if (this.count % 2) - this.switchOff(); - else - this.switchOn(); - - this.count++; - - this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this.timer.initWithCallback(() => this.doFlash(), 300, Ci.nsITimer.TYPE_ONE_SHOT); - }, - - stop: function() - { - if (this.timer) - { - this.timer.cancel(); - this.timer = null; - } - - if (this.elements) - { - this.switchOff(); - this.elements = null; - } - }, - - setOutline: function(outline, offset) - { - for (let element of this.elements) - { - if (!Cu.isDeadWrapper(element) && "style" in element) - { - element.style.outline = outline; - element.style.outlineOffset = offset; - } - } - }, - - switchOn: function() - { - this.setOutline("#CC0000 dotted 2px", "-2px"); - }, - - switchOff: function() - { - this.setOutline("", ""); - } -}; - -exports.Flasher = Flasher; diff --git a/data/extensions/spyblock@gnu.org/lib/child/main.js b/data/extensions/spyblock@gnu.org/lib/child/main.js deleted file mode 100644 index bc21e9a..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/main.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -let {port} = require("messaging"); - -// Only initialize after receiving a "response" to a dummy message - this makes -// sure that on update the old version has enough time to receive and process -// the shutdown message. -port.emitWithResponse("ping").then(() => -{ - require("child/elemHide"); - require("child/contentPolicy"); - require("child/contextMenu"); - require("child/dataCollector"); - require("child/elemHideEmulation"); - require("child/subscribeLinks"); -}).catch(e => Cu.reportError(e)); diff --git a/data/extensions/spyblock@gnu.org/lib/child/objectTabs.js b/data/extensions/spyblock@gnu.org/lib/child/objectTabs.js deleted file mode 100644 index 74e7387..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/objectTabs.js +++ /dev/null @@ -1,405 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Code responsible for showing and hiding object tabs. - */ - -let {port} = require("messaging"); - -/** - * Class responsible for showing and hiding object tabs. - * @class - */ -var objTabs = -{ - /** - * Number of milliseconds to wait until hiding tab after the mouse moves away. - * @type Integer - */ - HIDE_DELAY: 1000, - - /** - * Document element the object tab is currently being displayed for. - * @type Element - */ - currentElement: null, - - /** - * Windows that the window event handler is currently registered for. - * @type Window[] - */ - windowListeners: null, - - /** - * Panel element currently used as object tab. - * @type Element - */ - objtabElement: null, - - /** - * Time of previous position update. - * @type Integer - */ - prevPositionUpdate: 0, - - /** - * Timer used to update position of the object tab. - * @type nsITimer - */ - positionTimer: null, - - /** - * Timer used to delay hiding of the object tab. - * @type nsITimer - */ - hideTimer: null, - - /** - * Used when hideTimer is running, time when the tab should be hidden. - * @type Integer - */ - hideTargetTime: 0, - - /** - * Localized texts and class names to be used for the tab. This will be set - * when showTabFor is called for the first time. - * @type Object - */ - texts: null, - - /** - * Called to show object tab for an element. - */ - showTabFor: function(/**Element*/ element) - { - // Object tabs aren't usable in Fennec - let {application} = require("info"); - if (application == "fennec" || application == "fennec2" || - application == "adblockbrowser") - return; - - if (!this.texts) - this.texts = port.emitWithResponse("getObjectTabsTexts"); - Promise.all([port.emitWithResponse("getObjectTabsStatus"), this.texts]) - .then(([status, texts]) => - { - this.texts = texts; - if (!status) - return; - - if (this.hideTimer) - { - this.hideTimer.cancel(); - this.hideTimer = null; - } - - if (this.objtabElement) - this.objtabElement.style.setProperty("opacity", "1", "important"); - - if (this.currentElement != element) - { - this._hideTab(); - - let {RequestNotifier} = require("child/requestNotifier"); - let data = RequestNotifier.getDataForNode(element, true, "OBJECT"); - if (data) - this._showTab(element, data[1]); - } - }); - }, - - /** - * Called to hide object tab for an element (actual hiding happens delayed). - */ - hideTabFor: function(/**Element*/ element) - { - if (element != this.currentElement || this.hideTimer) - return; - - this.hideTargetTime = Date.now() + this.HIDE_DELAY; - this.hideTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this.hideTimer.init(this, 40, Ci.nsITimer.TYPE_REPEATING_SLACK); - }, - - /** - * Makes the tab element visible. - * @param {Element} element - * @param {RequestEntry} data - */ - _showTab: function(element, data) - { - let doc = element.ownerDocument.defaultView.top.document; - - this.objtabElement = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"); - this.objtabElement.textContent = this.texts.label; - this.objtabElement.setAttribute("title", this.texts.tooltip); - this.objtabElement.setAttribute("href", data.location); - this.objtabElement.setAttribute("class", this.texts.classHidden); - this.objtabElement.style.setProperty("opacity", "1", "important"); - this.objtabElement.nodeData = data; - - this.currentElement = element; - - // Register paint listeners for the relevant windows - this.windowListeners = []; - let wnd = element.ownerDocument.defaultView; - while (wnd) - { - wnd.addEventListener("MozAfterPaint", objectWindowEventHandler, false); - this.windowListeners.push(wnd); - wnd = (wnd.parent != wnd ? wnd.parent : null); - } - - // Register mouse listeners on the object tab - this.objtabElement.addEventListener("mouseover", objectTabEventHander, false); - this.objtabElement.addEventListener("mouseout", objectTabEventHander, false); - this.objtabElement.addEventListener("click", objectTabEventHander, true); - - // Insert the tab into the document and adjust its position - doc.documentElement.appendChild(this.objtabElement); - if (!this.positionTimer) - { - this.positionTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this.positionTimer.init(this, 200, Ci.nsITimer.TYPE_REPEATING_SLACK); - } - this._positionTab(); - }, - - /** - * Hides the tab element. - */ - _hideTab: function() - { - if (this.objtabElement) - { - // Prevent recursive calls via popuphidden handler - let objtab = this.objtabElement; - this.objtabElement = null; - this.currentElement = null; - - if (this.hideTimer) - { - this.hideTimer.cancel(); - this.hideTimer = null; - } - - if (this.positionTimer) - { - this.positionTimer.cancel(); - this.positionTimer = null; - } - - try { - objtab.parentNode.removeChild(objtab); - } catch (e) {} - objtab.removeEventListener("mouseover", objectTabEventHander, false); - objtab.removeEventListener("mouseout", objectTabEventHander, false); - objtab.nodeData = null; - - for (let wnd of this.windowListeners) - wnd.removeEventListener("MozAfterPaint", objectWindowEventHandler, false); - this.windowListeners = null; - } - }, - - /** - * Updates position of the tab element. - */ - _positionTab: function() - { - // Test whether element is still in document - let elementDoc = null; - try - { - elementDoc = this.currentElement.ownerDocument; - } catch (e) {} // Ignore "can't access dead object" error - if (!elementDoc || !this.currentElement.offsetWidth || !this.currentElement.offsetHeight || - !elementDoc.defaultView || !elementDoc.documentElement) - { - this._hideTab(); - return; - } - - let objRect = this._getElementPosition(this.currentElement); - - let className = this.texts.classVisibleTop; - let left = objRect.right - this.objtabElement.offsetWidth; - let top = objRect.top - this.objtabElement.offsetHeight; - if (top < 0) - { - top = objRect.bottom; - className = this.texts.classVisibleBottom; - } - - if (this.objtabElement.style.left != left + "px") - this.objtabElement.style.setProperty("left", left + "px", "important"); - if (this.objtabElement.style.top != top + "px") - this.objtabElement.style.setProperty("top", top + "px", "important"); - - if (this.objtabElement.getAttribute("class") != className) - this.objtabElement.setAttribute("class", className); - - this.prevPositionUpdate = Date.now(); - }, - - /** - * Calculates element's position relative to the top frame and considering - * clipping due to scrolling. - * @return {{left: Number, top: Number, right: Number, bottom: Number}} - */ - _getElementPosition: function(/**Element*/ element) - { - // Restrict rectangle coordinates by the boundaries of a window's client area - function intersectRect(rect, wnd) - { - // Cannot use wnd.innerWidth/Height because they won't account for scrollbars - let doc = wnd.document; - let wndWidth = doc.documentElement.clientWidth; - let wndHeight = doc.documentElement.clientHeight; - if (doc.compatMode == "BackCompat") // clientHeight will be bogus in quirks mode - wndHeight = Math.max(doc.documentElement.offsetHeight, doc.body.offsetHeight) - wnd.scrollMaxY - 1; - - rect.left = Math.max(rect.left, 0); - rect.top = Math.max(rect.top, 0); - rect.right = Math.min(rect.right, wndWidth); - rect.bottom = Math.min(rect.bottom, wndHeight); - } - - let rect = element.getBoundingClientRect(); - let wnd = element.ownerDocument.defaultView; - - let style = wnd.getComputedStyle(element, null); - let offsets = [ - parseFloat(style.borderLeftWidth) + parseFloat(style.paddingLeft), - parseFloat(style.borderTopWidth) + parseFloat(style.paddingTop), - parseFloat(style.borderRightWidth) + parseFloat(style.paddingRight), - parseFloat(style.borderBottomWidth) + parseFloat(style.paddingBottom) - ]; - - rect = {left: rect.left + offsets[0], top: rect.top + offsets[1], - right: rect.right - offsets[2], bottom: rect.bottom - offsets[3]}; - while (true) - { - intersectRect(rect, wnd); - - if (!wnd.frameElement) - break; - - // Recalculate coordinates to be relative to frame's parent window - let frameElement = wnd.frameElement; - wnd = frameElement.ownerDocument.defaultView; - - let frameRect = frameElement.getBoundingClientRect(); - let frameStyle = wnd.getComputedStyle(frameElement, null); - let relLeft = frameRect.left + parseFloat(frameStyle.borderLeftWidth) + parseFloat(frameStyle.paddingLeft); - let relTop = frameRect.top + parseFloat(frameStyle.borderTopWidth) + parseFloat(frameStyle.paddingTop); - - rect.left += relLeft; - rect.right += relLeft; - rect.top += relTop; - rect.bottom += relTop; - } - - return rect; - }, - - doBlock: function() - { - let {storeNodes} = require("child/contentPolicy"); - let nodesID = storeNodes([this.currentElement]); - port.emit("blockItem", { - request: this.objtabElement.nodeData, - nodesID - }); - }, - - /** - * Called whenever a timer fires. - * @param {nsISupport} subject - * @param {string} topic - * @param {string} data - */ - observe: function(subject, topic, data) - { - if (subject == this.positionTimer) - { - // Don't update position if it was already updated recently (via MozAfterPaint) - if (Date.now() - this.prevPositionUpdate > 100) - this._positionTab(); - } - else if (subject == this.hideTimer) - { - let now = Date.now(); - if (now >= this.hideTargetTime) - this._hideTab(); - else if (this.hideTargetTime - now < this.HIDE_DELAY / 2) - this.objtabElement.style.setProperty("opacity", (this.hideTargetTime - now) * 2 / this.HIDE_DELAY, "important"); - } - } -}; - -onShutdown.add(objTabs._hideTab.bind(objTabs)); - -/** - * Function called whenever the mouse enters or leaves an object. - */ -function objectMouseEventHander(/**Event*/ event) -{ - if (!event.isTrusted) - return; - - if (event.type == "mouseover") - objTabs.showTabFor(event.target); - else if (event.type == "mouseout") - objTabs.hideTabFor(event.target); -} - -/** - * Function called for paint events of the object tab window. - */ -function objectWindowEventHandler(/**Event*/ event) -{ - if (!event.isTrusted) - return; - - // Don't trigger update too often, avoid overusing CPU on frequent page updates - if (event.type == "MozAfterPaint" && Date.now() - objTabs.prevPositionUpdate > 20) - objTabs._positionTab(); -} - -/** - * Function called whenever the mouse enters or leaves an object tab. - */ -function objectTabEventHander(/**Event*/ event) -{ - if (onShutdown.done || !event.isTrusted) - return; - - if (event.type == "click" && event.button == 0) - { - event.preventDefault(); - event.stopPropagation(); - - objTabs.doBlock(); - } - else if (event.type == "mouseover") - objTabs.showTabFor(objTabs.currentElement); - else if (event.type == "mouseout") - objTabs.hideTabFor(objTabs.currentElement); -} -exports.objectMouseEventHander = objectMouseEventHander; diff --git a/data/extensions/spyblock@gnu.org/lib/child/requestNotifier.js b/data/extensions/spyblock@gnu.org/lib/child/requestNotifier.js deleted file mode 100644 index fc6d314..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/requestNotifier.js +++ /dev/null @@ -1,444 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Stores Adblock Plus data to be attached to a window. - */ -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - -let {port} = require("messaging"); -let {Utils} = require("utils"); -let {Flasher} = require("child/flasher"); - -let nodeData = new WeakMap(); -let windowStats = new WeakMap(); -let windowData = new WeakMap(); -let requestEntryMaxId = 0; - -/** - * Active RequestNotifier instances by their ID - * @type Map.<number,RequestNotifier> - */ -let notifiers = new Map(); - -port.on("startWindowScan", onStartScan); -port.on("shutdownNotifier", onNotifierShutdown); -port.on("flashNodes", onFlashNodes); -port.on("retrieveNodeSize", onRetrieveNodeSize); -port.on("storeNodesForEntries", onStoreNodes); -port.on("retrieveWindowStats", onRetrieveWindowStats); -port.on("storeWindowData", onStoreWindowData); -port.on("retrieveWindowData", onRetrieveWindowData); - -function onStartScan({notifierID, outerWindowID}) -{ - let window = Services.wm.getOuterWindowWithId(outerWindowID); - if (window) - new RequestNotifier(window, notifierID); -} - -function onNotifierShutdown(notifierID) -{ - let notifier = notifiers.get(notifierID); - if (notifier) - notifier.shutdown(); -} - -function onFlashNodes({notifierID, requests, scrollToItem}) -{ - let notifier = notifiers.get(notifierID); - if (notifier) - notifier.flashNodes(requests, scrollToItem); -} - -function onRetrieveNodeSize({notifierID, requests}) -{ - let notifier = notifiers.get(notifierID); - if (notifier) - return notifier.retrieveNodeSize(requests); -} - -function onStoreNodes({notifierID, requests}) -{ - let notifier = notifiers.get(notifierID); - if (notifier) - return notifier.storeNodesForEntries(requests); -} - -function onRetrieveWindowStats(outerWindowID) -{ - let window = Services.wm.getOuterWindowWithId(outerWindowID); - if (window) - return RequestNotifier.getWindowStatistics(window); -} - -function onStoreWindowData({outerWindowID, data}) -{ - let window = Services.wm.getOuterWindowWithId(outerWindowID); - if (window) - windowData.set(window.document, data); -}; - -function onRetrieveWindowData(outerWindowID) -{ - let window = Services.wm.getOuterWindowWithId(outerWindowID); - if (window) - return windowData.get(window.document) || null; -}; - -/** - * Creates a notifier object for a particular window. After creation the window - * will first be scanned for previously saved requests. Once that scan is - * complete only new requests for this window will be reported. - * @param {Window} window window to attach the notifier to - * @param {Integer} notifierID Parent notifier ID to be messaged - */ -function RequestNotifier(window, notifierID) -{ - this.window = window; - this.id = notifierID; - notifiers.set(this.id, this); - this.nodes = new Map(); - this.startScan(window); -} -exports.RequestNotifier = RequestNotifier; - -RequestNotifier.prototype = -{ - /** - * Parent notifier ID to be messaged - * @type Integer - */ - id: null, - - /** - * The window this notifier is associated with. - * @type Window - */ - window: null, - - /** - * Nodes associated with a particular request ID. - * @type Map.<number,Node> - */ - nodes: null, - - /** - * Shuts down the notifier once it is no longer used. The listener - * will no longer be called after that. - */ - shutdown: function() - { - delete this.window; - delete this.nodes; - this.stopFlashing(); - notifiers.delete(this.id); - }, - - /** - * Notifies the parent about a new request. - * @param {Node} node DOM node that the request is associated with - * @param {Object} entry - */ - notifyListener: function(node, entry) - { - if (this.nodes) - this.nodes.set(entry.id, node); - port.emit("foundNodeData", { - notifierID: this.id, - data: entry - }); - }, - - onComplete: function() - { - port.emit("scanComplete", this.id); - }, - - /** - * Number of currently posted scan events (will be 0 when the scan finishes - * running). - */ - eventsPosted: 0, - - /** - * Starts the initial scan of the window (will recurse into frames). - * @param {Window} wnd the window to be scanned - */ - startScan: function(wnd) - { - let doc = wnd.document; - let walker = doc.createTreeWalker(doc, Ci.nsIDOMNodeFilter.SHOW_ELEMENT, null, false); - - let process = function() - { - // Don't do anything if the notifier was shut down already. - if (!this.window) - return; - - let node = walker.currentNode; - let data = nodeData.get(node); - if (typeof data != "undefined") - for (let k in data) - this.notifyListener(node, data[k]); - - if (walker.nextNode()) - Utils.runAsync(process); - else - { - // Done with the current window, start the scan for its frames - for (let i = 0; i < wnd.frames.length; i++) - this.startScan(wnd.frames[i]); - - this.eventsPosted--; - if (!this.eventsPosted) - { - this.scanComplete = true; - this.onComplete(); - } - } - }.bind(this); - - // Process each node in a separate event to allow other events to process - this.eventsPosted++; - Utils.runAsync(process); - }, - - /** - * Makes the nodes associated with the given requests blink. - * @param {number[]} requests list of request IDs that were previously - * reported by this notifier. - * @param {boolean} scrollToItem if true, scroll to first node - */ - flashNodes: function(requests, scrollToItem) - { - this.stopFlashing(); - - let nodes = []; - for (let id of requests) - { - if (!this.nodes.has(id)) - continue; - - let node = this.nodes.get(id); - if (Cu.isDeadWrapper(node)) - this.nodes.delete(node); - else if (node.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) - nodes.push(node); - } - if (nodes.length) - this.flasher = new Flasher(nodes, scrollToItem); - }, - - /** - * Stops flashing nodes after a previous flashNodes() call. - */ - stopFlashing: function() - { - if (this.flasher) - this.flasher.stop(); - this.flasher = null; - }, - - /** - * Attempts to calculate the size of the nodes associated with the requests. - * @param {number[]} requests list of request IDs that were previously - * reported by this notifier. - * @return {number[]|null} either an array containing width and height or - * null if the size could not be calculated. - */ - retrieveNodeSize: function(requests) - { - function getNodeSize(node) - { - if (node instanceof Ci.nsIDOMHTMLImageElement && (node.naturalWidth || node.naturalHeight)) - return [node.naturalWidth, node.naturalHeight]; - else if (node instanceof Ci.nsIDOMHTMLElement && (node.offsetWidth || node.offsetHeight)) - return [node.offsetWidth, node.offsetHeight]; - else - return null; - } - - let size = null; - for (let id of requests) - { - if (!this.nodes.has(id)) - continue; - - let node = this.nodes.get(id); - if (Cu.isDeadWrapper(node)) - this.nodes.delete(node); - else - { - size = getNodeSize(node); - if (size) - break; - } - } - return size; - }, - - /** - * Stores the nodes associated with the requests and generates a unique ID - * for them that can be used with Policy.refilterNodes(). - * @param {number[]} requests list of request IDs that were previously - * reported by this notifier. - * @return {string} unique identifiers associated with the nodes. - */ - storeNodesForEntries: function(requests) - { - let nodes = []; - for (let id of requests) - { - if (!this.nodes.has(id)) - continue; - - let node = this.nodes.get(id); - if (Cu.isDeadWrapper(node)) - this.nodes.delete(node); - else - nodes.push(node); - } - - let {storeNodes} = require("child/contentPolicy"); - return storeNodes(nodes); - } -}; - -/** - * Attaches request data to a DOM node. - * @param {Node} node node to attach data to - * @param {Window} topWnd top-level window the node belongs to - * @param {Object} hitData - * @param {String} hitData.contentType request type, e.g. "IMAGE" - * @param {String} hitData.docDomain domain of the document that initiated the request - * @param {Boolean} hitData.thirdParty will be true if a third-party server has been requested - * @param {String} hitData.location the address that has been requested - * @param {String} hitData.filter filter applied to the request or null if none - * @param {String} hitData.filterType type of filter applied to the request - */ -RequestNotifier.addNodeData = function(node, topWnd, {contentType, docDomain, thirdParty, location, filter, filterType}) -{ - let entry = { - id: ++requestEntryMaxId, - type: contentType, - docDomain, thirdParty, location, filter - }; - - let existingData = nodeData.get(node); - if (typeof existingData == "undefined") - { - existingData = {}; - nodeData.set(node, existingData); - } - - // Add this request to the node data - existingData[contentType + " " + location] = entry; - - // Update window statistics - if (!windowStats.has(topWnd.document)) - { - windowStats.set(topWnd.document, { - items: 0, - hidden: 0, - blocked: 0, - whitelisted: 0, - filters: {} - }); - } - - let stats = windowStats.get(topWnd.document); - if (filterType != "elemhide" && filterType != "elemhideexception" && filterType != "elemhideemulation") - stats.items++; - if (filter) - { - if (filterType == "blocking") - stats.blocked++; - else if (filterType == "whitelist" || filterType == "elemhideexception") - stats.whitelisted++; - else if (filterType == "elemhide" || filterType == "elemhideemulation") - stats.hidden++; - - if (filter in stats.filters) - stats.filters[filter]++; - else - stats.filters[filter] = 1; - } - - // Notify listeners - for (let notifier of notifiers.values()) - if (!notifier.window || notifier.window == topWnd) - notifier.notifyListener(node, entry); -} - -/** - * Retrieves the statistics for a window. - * @return {Object} Object with the properties items, blocked, whitelisted, hidden, filters containing statistics for the window (might be null) - */ -RequestNotifier.getWindowStatistics = function(/**Window*/ wnd) -{ - if (windowStats.has(wnd.document)) - return windowStats.get(wnd.document); - else - return null; -} - -/** - * Retrieves the request data associated with a DOM node. - * @param {Node} node - * @param {Boolean} noParent if missing or false, the search will extend to the parent nodes until one is found that has data associated with it - * @param {Integer} [type] request type to be looking for - * @param {String} [location] request location to be looking for - * @result {[Node, Object]} - * @static - */ -RequestNotifier.getDataForNode = function(node, noParent, type, location) -{ - while (node) - { - let data = nodeData.get(node); - if (typeof data != "undefined") - { - let entry = null; - // Look for matching entry - for (let k in data) - { - if ((!entry || entry.id < data[k].id) && - (typeof type == "undefined" || data[k].type == type) && - (typeof location == "undefined" || data[k].location == location)) - { - entry = data[k]; - } - } - if (entry) - return [node, entry]; - } - - // If we don't have any match on this node then maybe its parent will do - if ((typeof noParent != "boolean" || !noParent) && - node.parentNode instanceof Ci.nsIDOMElement) - { - node = node.parentNode; - } - else - { - node = null; - } - } - - return null; -}; diff --git a/data/extensions/spyblock@gnu.org/lib/child/subscribeLinks.js b/data/extensions/spyblock@gnu.org/lib/child/subscribeLinks.js deleted file mode 100644 index a2e729d..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/subscribeLinks.js +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - -let {port} = require("messaging"); - -Services.obs.addObserver(onContentWindow, "content-document-global-created", - false); -onShutdown.add(() => -{ - Services.obs.removeObserver(onContentWindow, - "content-document-global-created"); -}); - -function onContentWindow(subject, topic, data) -{ - if (subject instanceof Ci.nsIDOMWindow && subject.top == subject) - { - let eventTarget = subject.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler; - if (eventTarget) - eventTarget.addEventListener("click", onClick, true); - } -} - -function onClick(event) -{ - if (onShutdown.done) - return; - - // Ignore right-clicks - if (event.button == 2) - return; - - // Search the link associated with the click - let link = event.target; - while (!(link instanceof Ci.nsIDOMHTMLAnchorElement)) - { - link = link.parentNode; - - if (!link) - return; - } - - let queryString = null; - if (link.protocol == "http:" || link.protocol == "https:") - { - if (link.host == "subscribe.adblockplus.org" && link.pathname == "/") - queryString = link.search.substr(1); - } - else - { - // Firefox doesn't populate the "search" property for links with - // non-standard URL schemes so we need to extract the query string - // manually - let match = /^abp:\/*subscribe\/*\?(.*)/i.exec(link.href); - if (match) - queryString = match[1]; - } - - if (!queryString) - return; - - // This is our link - make sure the browser doesn't handle it - event.preventDefault(); - event.stopPropagation(); - - // Decode URL parameters - let title = null; - let url = null; - let mainSubscriptionTitle = null; - let mainSubscriptionURL = null; - for (let param of queryString.split("&")) - { - let parts = param.split("=", 2); - if (parts.length != 2 || !/\S/.test(parts[1])) - continue; - switch (parts[0]) - { - case "title": - title = decodeURIComponent(parts[1]); - break; - case "location": - url = decodeURIComponent(parts[1]); - break; - case "requiresTitle": - mainSubscriptionTitle = decodeURIComponent(parts[1]); - break; - case "requiresLocation": - mainSubscriptionURL = decodeURIComponent(parts[1]); - break; - } - } - - port.emit("subscribeLinkClick", { - title: title, - url: url, - mainSubscriptionTitle: mainSubscriptionTitle, - mainSubscriptionURL: mainSubscriptionURL - }); -} diff --git a/data/extensions/spyblock@gnu.org/lib/child/utils.js b/data/extensions/spyblock@gnu.org/lib/child/utils.js deleted file mode 100644 index fde649f..0000000 --- a/data/extensions/spyblock@gnu.org/lib/child/utils.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -let {PrivateBrowsingUtils} = Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm", {}); - -let {Utils} = require("utils"); - -/** - * Retrieves the effective location of a window. - */ -let getWindowLocation = exports.getWindowLocation = function(/**Window*/ window) /**String*/ -{ - let result = null; - - // Crazy Thunderbird stuff - if ("name" in window && window.name == "messagepane") - { - try - { - let mailWnd = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - - // Typically we get a wrapped mail window here, need to unwrap - try - { - mailWnd = mailWnd.wrappedJSObject; - } catch(e) {} - - if ("currentHeaderData" in mailWnd && "content-base" in mailWnd.currentHeaderData) - { - result = mailWnd.currentHeaderData["content-base"].headerValue; - } - else if ("currentHeaderData" in mailWnd && "from" in mailWnd.currentHeaderData) - { - let emailAddress = Utils.headerParser.extractHeaderAddressMailboxes(mailWnd.currentHeaderData.from.headerValue); - if (emailAddress) - result = 'mailto:' + emailAddress.replace(/^[\s"]+/, "").replace(/[\s"]+$/, "").replace(/\s/g, '%20'); - } - } catch(e) {} - } - - // Sane branch - if (!result) - result = window.location.href; - - // Remove the anchor if any - let index = result.indexOf("#"); - if (index >= 0) - result = result.substring(0, index); - - return result; -} - -/** - * Retrieves the frame hierarchy for a window. Returns an array containing - * the information for all frames, starting with the window itself up to its - * top-level window. Each entry has a location and a sitekey entry. - * @return {Array} - */ -let getFrames = exports.getFrames = function(/**Window*/ window) -{ - let frames = []; - while (window) - { - let frame = { - location: getWindowLocation(window), - sitekey: null - }; - - let documentElement = window.document && window.document.documentElement; - if (documentElement) - frame.sitekey = documentElement.getAttribute("data-adblockkey") - - frames.push(frame); - window = (window != window.parent ? window.parent : null); - } - - // URLs like about:blank inherit their security context from upper-level - // frames, resolve their URLs accordingly. - for (let i = frames.length - 2; i >= 0; i--) - { - let frame = frames[i]; - if (frame.location == "about:blank" || frame.location == "moz-safe-about:blank" || - frame.location == "about:srcdoc" || - Utils.netUtils.URIChainHasFlags(Utils.makeURI(frame.location), Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT)) - { - frame.location = frames[i + 1].location; - } - } - - return frames; -}; - -/** - * Checks whether Private Browsing mode is enabled for a content window. - * @return {Boolean} - */ -let isPrivate = exports.isPrivate = function(/**Window*/ window) -{ - return PrivateBrowsingUtils.isContentWindowPrivate(window); -}; - -/** - * Gets the DOM window associated with a particular request (if any). - */ -let getRequestWindow = exports.getRequestWindow = function(/**nsIChannel*/ channel) /**nsIDOMWindow*/ -{ - try - { - if (channel.notificationCallbacks) - return channel.notificationCallbacks.getInterface(Ci.nsILoadContext).associatedWindow; - } catch(e) {} - - try - { - if (channel.loadGroup && channel.loadGroup.notificationCallbacks) - return channel.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext).associatedWindow; - } catch(e) {} - - return null; -}; diff --git a/data/extensions/spyblock@gnu.org/lib/common.js b/data/extensions/spyblock@gnu.org/lib/common.js deleted file mode 100644 index e2c2db5..0000000 --- a/data/extensions/spyblock@gnu.org/lib/common.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -// We are currently limited to ECMAScript 5 in this file, because it is being -// used in the browser tests. See https://issues.adblockplus.org/ticket/4796 - -/** - * Converts filter text into regular expression string - * @param {string} text as in Filter() - * @return {string} regular expression representation of filter text - */ -function filterToRegExp(text) -{ - return text - // remove multiple wildcards - .replace(/\*+/g, "*") - // remove anchors following separator placeholder - .replace(/\^\|$/, "^") - // escape special symbols - .replace(/\W/g, "\\$&") - // replace wildcards by .* - .replace(/\\\*/g, ".*") - // process separator placeholders (all ANSI characters but alphanumeric - // characters and _%.-) - .replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)") - // process extended anchor at expression start - .replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") - // process anchor at expression start - .replace(/^\\\|/, "^") - // process anchor at expression end - .replace(/\\\|$/, "$") - // remove leading wildcards - .replace(/^(\.\*)/, "") - // remove trailing wildcards - .replace(/(\.\*)$/, ""); -} - -if (typeof exports != "undefined") - exports.filterToRegExp = filterToRegExp; diff --git a/data/extensions/spyblock@gnu.org/lib/contentPolicy.js b/data/extensions/spyblock@gnu.org/lib/contentPolicy.js deleted file mode 100644 index ad36655..0000000 --- a/data/extensions/spyblock@gnu.org/lib/contentPolicy.js +++ /dev/null @@ -1,415 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Content policy implementation, responsible for blocking things. - */ - -"use strict"; - -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - -let {Utils} = require("utils"); -let {port} = require("messaging"); -let {Prefs} = require("prefs"); -let {FilterStorage} = require("filterStorage"); -let {BlockingFilter, WhitelistFilter, RegExpFilter} = require("filterClasses"); -let {defaultMatcher} = require("matcher"); - -/** - * Public policy checking functions and auxiliary objects - * @class - */ -var Policy = exports.Policy = -{ - /** - * Map of content types reported by Firefox to the respecitve content types - * used by Adblock Plus. Other content types are simply mapped to OTHER. - * @type Map.<string,string> - */ - contentTypes: new Map(function* () - { - // Treat navigator.sendBeacon() the same as <a ping>, - // it's essentially the same concept - merely generalized. - yield ["BEACON", "PING"]; - - // Treat <img srcset> and <picture> the same as other images. - yield ["IMAGESET", "IMAGE"]; - - // Treat fetch() the same as XMLHttpRequest, - // it's essentially the same - merely a more modern API. - yield ["FETCH", "XMLHTTPREQUEST"]; - - // Everything else is mapped to itself - for (let contentType of ["OTHER", "SCRIPT", "IMAGE", "STYLESHEET", "OBJECT", - "SUBDOCUMENT", "DOCUMENT", "XMLHTTPREQUEST", - "OBJECT_SUBREQUEST", "FONT", "MEDIA", "PING", - "WEBSOCKET", "ELEMHIDE", "POPUP", "GENERICHIDE", - "GENERICBLOCK"]) - yield [contentType, contentType]; - }()), - - /** - * Set of content types that aren't associated with a visual document area - * @type Set.<string> - */ - nonVisualTypes: new Set([ - "SCRIPT", "STYLESHEET", "XMLHTTPREQUEST", "OBJECT_SUBREQUEST", "FONT", - "PING", "WEBSOCKET", "ELEMHIDE", "POPUP", "GENERICHIDE", "GENERICBLOCK" - ]), - - /** - * Map containing all schemes that should be ignored by content policy. - * @type Set.<string> - */ - whitelistSchemes: new Set(), - - /** - * Called on module startup, initializes various exported properties. - */ - init: function() - { - // whitelisted URL schemes - for (let scheme of Prefs.whitelistschemes.toLowerCase().split(" ")) - this.whitelistSchemes.add(scheme); - - port.on("shouldAllow", (message, sender) => this.shouldAllow(message)); - - // Generate class identifier used to collapse nodes and register - // corresponding stylesheet. - let collapsedClass = ""; - let offset = "a".charCodeAt(0); - for (let i = 0; i < 20; i++) - collapsedClass += String.fromCharCode(offset + Math.random() * 26); - port.on("getCollapsedClass", (message, sender) => collapsedClass); - - let collapseStyle = Services.io.newURI("data:text/css," + - encodeURIComponent("." + collapsedClass + - "{-moz-binding: url(chrome://global/content/bindings/general.xml#foobarbazdummy) !important;}"), null, null); - Utils.styleService.loadAndRegisterSheet(collapseStyle, Ci.nsIStyleSheetService.USER_SHEET); - onShutdown.add(() => - { - Utils.styleService.unregisterSheet(collapseStyle, Ci.nsIStyleSheetService.USER_SHEET); - }); - }, - - /** - * Checks whether a node should be blocked, hides it if necessary - * @param {Object} data request data - * @param {String} data.contentType - * @param {String} data.location location of the request - * @param {Object[]} data.frames - * @param {Boolean} data.isPrivate true if the request belongs to a private browsing window - * @return {Object} An object containing properties allow, collapse and hits - * indicating how this request should be handled. - */ - shouldAllow: function({contentType, location, frames, isPrivate}) - { - let hits = []; - - function addHit(frameIndex, contentType, docDomain, thirdParty, location, filter) - { - if (filter && !isPrivate) - FilterStorage.increaseHitCount(filter); - hits.push({ - frameIndex, contentType, docDomain, thirdParty, location, - filter: filter ? filter.text : null, - filterType: filter ? filter.type : null - }); - } - - function response(allow, collapse) - { - return {allow, collapse, hits}; - } - - // Ignore whitelisted schemes - if (contentType != "POPUP" && !this.isBlockableScheme(location)) - return response(true, false); - - // Interpret unknown types as "other" - contentType = this.contentTypes.get(contentType) || "OTHER"; - - let nogeneric = false; - if (Prefs.enabled) - { - let whitelistHit = - this.isFrameWhitelisted(frames, false); - if (whitelistHit) - { - let [frameIndex, matchType, docDomain, thirdParty, location, filter] = whitelistHit; - addHit(frameIndex, matchType, docDomain, thirdParty, location, filter); - if (matchType == "DOCUMENT") - return response(true, false); - else - nogeneric = true; - } - } - - let match = null; - let wndLocation = frames[0].location; - let docDomain = getHostname(wndLocation); - let [sitekey, sitekeyFrame] = getSitekey(frames); - - let thirdParty = isThirdParty(location, docDomain); - let collapse = false; - - if (!match && Prefs.enabled && RegExpFilter.typeMap.hasOwnProperty(contentType)) - { - match = defaultMatcher.matchesAny(location, RegExpFilter.typeMap[contentType], - docDomain, thirdParty, sitekey, nogeneric, isPrivate); - if (match instanceof BlockingFilter && !this.nonVisualTypes.has(contentType)) - collapse = (match.collapse != null ? match.collapse : !Prefs.fastcollapse); - } - addHit(null, contentType, docDomain, thirdParty, location, match); - - return response(!match || match instanceof WhitelistFilter, collapse); - }, - - /** - * Checks whether the location's scheme is blockable. - * @param location {nsIURI|String} - * @return {Boolean} - */ - isBlockableScheme: function(location) - { - let scheme; - if (typeof location == "string") - { - let match = /^([\w\-]+):/.exec(location); - scheme = match ? match[1] : null; - } - else - scheme = location.scheme; - return !this.whitelistSchemes.has(scheme); - }, - - /** - * Checks whether a top-level window is whitelisted. - * @param {String} url - * URL of the document loaded into the window - * @return {?WhitelistFilter} - * exception rule that matched the URL if any - */ - isWhitelisted: function(url) - { - if (!url) - return null; - - // Do not apply exception rules to schemes on our whitelistschemes list. - if (!this.isBlockableScheme(url)) - return null; - - // Ignore fragment identifier - let index = url.indexOf("#"); - if (index >= 0) - url = url.substring(0, index); - - let result = defaultMatcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, - getHostname(url), false, null); - return (result instanceof WhitelistFilter ? result : null); - }, - - /** - * Checks whether a frame is whitelisted. - * @param {Array} frames - * frame structure as returned by getFrames() in child/utils module. - * @param {boolean} isElemHide - * true if element hiding whitelisting should be considered - * @return {?Array} - * An array with the hit parameters: frameIndex, contentType, docDomain, - * thirdParty, location, filter. Note that the filter could be a - * genericblock/generichide exception rule. If nothing matched null is - * returned. - */ - isFrameWhitelisted: function(frames, isElemHide) - { - let [sitekey, sitekeyFrame] = getSitekey(frames); - let nogenericHit = null; - - let typeMap = RegExpFilter.typeMap.DOCUMENT; - if (isElemHide) - typeMap = typeMap | RegExpFilter.typeMap.ELEMHIDE; - let genericType = (isElemHide ? "GENERICHIDE" : "GENERICBLOCK"); - - for (let i = 0; i < frames.length; i++) - { - let frame = frames[i]; - let wndLocation = frame.location; - let parentWndLocation = frames[Math.min(i + 1, frames.length - 1)].location; - let parentDocDomain = getHostname(parentWndLocation); - - let match = defaultMatcher.matchesAny(wndLocation, typeMap, parentDocDomain, false, sitekey); - if (match instanceof WhitelistFilter) - { - let whitelistType = (match.contentType & RegExpFilter.typeMap.DOCUMENT) ? "DOCUMENT" : "ELEMHIDE"; - return [i, whitelistType, parentDocDomain, false, wndLocation, match]; - } - - if (!nogenericHit) - { - match = defaultMatcher.matchesAny(wndLocation, - RegExpFilter.typeMap[genericType], parentDocDomain, false, sitekey); - if (match instanceof WhitelistFilter) - nogenericHit = [i, genericType, parentDocDomain, false, wndLocation, match]; - } - - if (frame == sitekeyFrame) - [sitekey, sitekeyFrame] = getSitekey(frames.slice(i + 1)); - } - - return nogenericHit; - }, - - /** - * Deletes nodes that were previously stored with a - * RequestNotifier.storeNodesForEntries() call or similar. - * @param {string} id unique ID of the nodes - */ - deleteNodes: function(id) - { - port.emit("deleteNodes", id); - }, - - /** - * Asynchronously re-checks filters for nodes given by an ID previously - * returned by a RequestNotifier.storeNodesForEntries() call or similar. - * @param {string} id unique ID of the nodes - * @param {RequestEntry} entry - */ - refilterNodes: function(id, entry) - { - port.emit("refilterNodes", { - nodesID: id, - entry: entry - }); - } -}; -Policy.init(); - -/** - * Extracts the hostname from a URL (might return null). - */ -function getHostname(/**String*/ url) /**String*/ -{ - try - { - return Utils.unwrapURL(url).host; - } - catch(e) - { - return null; - } -} - -/** - * Retrieves and validates the sitekey for a frame structure. - */ -function getSitekey(frames) -{ - for (let frame of frames) - { - if (frame.sitekey && frame.sitekey.indexOf("_") >= 0) - { - let [key, signature] = frame.sitekey.split("_", 2); - key = key.replace(/=/g, ""); - - // Website specifies a key but is the signature valid? - let uri = Services.io.newURI(frame.location, null, null); - let host = uri.asciiHost; - if (uri.port > 0) - host += ":" + uri.port; - let params = [ - uri.path.replace(/#.*/, ""), // REQUEST_URI - host, // HTTP_HOST - Utils.httpProtocol.userAgent // HTTP_USER_AGENT - ]; - if (Utils.verifySignature(key, signature, params.join("\0"))) - return [key, frame]; - } - } - - return [null, null]; -} - -/** - * Retrieves the location of a window. - * @param wnd {nsIDOMWindow} - * @return {String} window location or null on failure - */ -function getWindowLocation(wnd) -{ - if ("name" in wnd && wnd.name == "messagepane") - { - // Thunderbird branch - try - { - let mailWnd = wnd.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - - // Typically we get a wrapped mail window here, need to unwrap - try - { - mailWnd = mailWnd.wrappedJSObject; - } catch(e) {} - - if ("currentHeaderData" in mailWnd && "content-base" in mailWnd.currentHeaderData) - { - return mailWnd.currentHeaderData["content-base"].headerValue; - } - else if ("currentHeaderData" in mailWnd && "from" in mailWnd.currentHeaderData) - { - let emailAddress = Utils.headerParser.extractHeaderAddressMailboxes(mailWnd.currentHeaderData.from.headerValue); - if (emailAddress) - return 'mailto:' + emailAddress.replace(/^[\s"]+/, "").replace(/[\s"]+$/, "").replace(/\s/g, '%20'); - } - } catch(e) {} - } - - // Firefox branch - return wnd.location.href; -} - -/** - * Checks whether the location's origin is different from document's origin. - */ -function isThirdParty(/**String*/location, /**String*/ docDomain) /**Boolean*/ -{ - if (!location || !docDomain) - return true; - - let uri = Utils.makeURI(location); - try - { - return Utils.effectiveTLD.getBaseDomain(uri) != Utils.effectiveTLD.getBaseDomainFromHost(docDomain); - } - catch (e) - { - // EffectiveTLDService throws on IP addresses, just compare the host name - let host = ""; - try - { - host = uri.host; - } catch (e) {} - return host != docDomain; - } -} diff --git a/data/extensions/spyblock@gnu.org/lib/coreUtils.js b/data/extensions/spyblock@gnu.org/lib/coreUtils.js deleted file mode 100644 index 98a1331..0000000 --- a/data/extensions/spyblock@gnu.org/lib/coreUtils.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -function desc(properties) -{ - let descriptor = {}; - let keys = Object.keys(properties); - - for (let key of keys) - descriptor[key] = Object.getOwnPropertyDescriptor(properties, key); - - return descriptor; -} -exports.desc = desc; - -function extend(cls, properties) -{ - return Object.create(cls.prototype, desc(properties)); -} -exports.extend = extend; diff --git a/data/extensions/spyblock@gnu.org/lib/customizableUI.js b/data/extensions/spyblock@gnu.org/lib/customizableUI.js deleted file mode 100644 index 3874256..0000000 --- a/data/extensions/spyblock@gnu.org/lib/customizableUI.js +++ /dev/null @@ -1,320 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 This emulates a subset of the CustomizableUI API from Firefox 28. - */ - -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", null); - -let {Utils} = require("utils"); - -// UI module has to be referenced lazily to avoid circular references -XPCOMUtils.defineLazyGetter(this, "UI", () => require("ui").UI); - -let widgets = new Map(); - -function getToolbox(/**Window*/ window, /**Widget*/ widget) /**Element*/ -{ - if (!("defaultArea" in widget) || !widget.defaultArea) - return null; - - let toolbar = UI.findElement(window, widget.defaultArea); - if (!toolbar) - return null; - - let toolbox = toolbar.toolbox; - if (toolbox && ("palette" in toolbox) && toolbox.palette) - return toolbox; - else - return null; -} - -function getToolbar(/**Element*/ element) /**Element*/ -{ - for (let parent = element.parentNode; parent; parent = parent.parentNode) - if (parent.localName == "toolbar") - return parent; - return null; -} - -function getPaletteItem(/**Element*/ toolbox, /**String*/ id) /**Element*/ -{ - for (let child of toolbox.palette.children) - if (child.id == id) - return child; - - return null; -} - -function restoreWidget(/**Element*/ toolbox, /**Widget*/ widget) -{ - // Create node - let node = widget.onBuild(toolbox.ownerDocument); - - // Insert into the palette first - toolbox.palette.insertBefore(node, toolbox.palette.firstChild); - - // Now find out where we should put it - let position = toolbox.getAttribute(widget.positionAttribute); - if (!/^\S*,\S*,\S*$/.test(position)) - position = null; - - if (position == null) - { - // No explicitly saved position but maybe we can find it in a currentset - // attribute somewhere. - let toolbars = toolbox.externalToolbars.slice(); - for (let child of toolbox.children) - if (child.localName == "toolbar") - toolbars.push(child); - for (let toolbar of toolbars) - { - let currentSet = toolbar.getAttribute("currentset"); - if (currentSet) - { - let items = currentSet.split(","); - let index = items.indexOf(widget.id); - if (index >= 0) - { - let before = (index + 1 < items.length ? items[index + 1] : ""); - position = "visible," + toolbar.id + "," + before; - toolbox.setAttribute(widget.positionAttribute, position); - toolbox.ownerDocument.persist(toolbox.id, widget.positionAttribute); - break; - } - } - } - } - - showWidget(toolbox, widget, position); -} - -function showWidget(/**Element*/ toolbox, /**Widget*/ widget, /**String*/ position) -{ - let visible = "visible", parent = null, before = null; - if (position) - { - [visible, parent, before] = position.split(",", 3); - parent = toolbox.ownerDocument.getElementById(parent); - if (before == "") - before = null; - else - before = toolbox.ownerDocument.getElementById(before); - if (before && before.parentNode != parent) - before = null; - } - - if (visible == "visible" && !parent) - { - let insertionPoint = { - parent: widget.defaultArea - }; - if (typeof widget.defaultBefore != "undefined") - insertionPoint.before = widget.defaultBefore; - if (typeof widget.defaultAfter != "undefined") - insertionPoint.after = widget.defaultAfter; - - [parent, before] = UI.resolveInsertionPoint(toolbox.ownerDocument.defaultView, insertionPoint); - } - - if (parent && parent.localName != "toolbar") - parent = null; - - if (visible != "visible") - { - // Move to palette if the item is currently visible - let node = toolbox.ownerDocument.getElementById(widget.id); - if (node) - toolbox.palette.appendChild(node); - } - else if (parent) - { - // Add the item to the toolbar - let items = parent.currentSet.split(","); - let index = (before ? items.indexOf(before.id) : -1); - if (index < 0) - before = null; - parent.insertItem(widget.id, before, null, false); - } - - saveState(toolbox, widget); -} - -function removeWidget(/**Window*/ window, /**Widget*/ widget) -{ - let element = window.document.getElementById(widget.id); - if (element) - element.parentNode.removeChild(element); - - let toolbox = getToolbox(window, widget); - if (toolbox) - { - let paletteItem = getPaletteItem(toolbox, widget.id); - if (paletteItem) - paletteItem.parentNode.removeChild(paletteItem); - } -} - -function onToolbarCustomization(/**Event*/ event) -{ - let toolbox = event.currentTarget; - for (let [id, widget] of widgets) - saveState(toolbox, widget); -} - -function saveState(/**Element*/ toolbox, /**Widget*/ widget) -{ - let node = toolbox.ownerDocument.getElementById(widget.id); - - let position = toolbox.getAttribute(widget.positionAttribute) || "hidden,,"; - if (node && node.parentNode.localName != "toolbarpalette") - { - if (typeof widget.onAdded == "function") - widget.onAdded(node) - - let toolbar = getToolbar(node); - position = "visible," + toolbar.id + "," + (node.nextSibling ? node.nextSibling.id : ""); - } - else - position = position.replace(/^visible,/, "hidden,") - - toolbox.setAttribute(widget.positionAttribute, position); - toolbox.ownerDocument.persist(toolbox.id, widget.positionAttribute); -} - -let CustomizableUI = exports.CustomizableUI = -{ - createWidget: function(widget) - { - if (typeof widget.id == "undefined" || - typeof widget.defaultArea == "undefined" || - typeof widget.positionAttribute == "undefined") - { - throw new Error("Unexpected: required property missing from the widget data"); - } - widgets.set(widget.id, widget); - - // Show widget in any existing windows - for (let window of UI.applicationWindows) - { - let toolbox = getToolbox(window, widget); - if (toolbox) - { - toolbox.addEventListener("aftercustomization", onToolbarCustomization, false); - restoreWidget(toolbox, widget); - } - } - }, - - destroyWidget: function(id) - { - // Don't do anything here. This function is called on shutdown, - // removeFromWindow will take care of cleaning up already. - }, - - getPlacementOfWidget: function(id) - { - let window = UI.currentWindow; - if (!window) - return null; - - let widget = window.document.getElementById(id); - if (!widget) - return null; - - let toolbar = getToolbar(widget); - if (!toolbar) - return null; - - return {area: toolbar.id}; - }, - - addWidgetToArea: function(id) - { - // Note: the official API function also has area and position parameters. - // We ignore those here and simply restore the previous position instead. - let widget = widgets.get(id); - for (let window of UI.applicationWindows) - { - let toolbox = getToolbox(window, widget); - if (!toolbox) - continue; - - let position = toolbox.getAttribute(widget.positionAttribute); - if (position) - position = position.replace(/^hidden,/, "visible,"); - showWidget(toolbox, widget, position); - } - }, - - removeWidgetFromArea: function(id) - { - let widget = widgets.get(id); - for (let window of UI.applicationWindows) - { - let toolbox = getToolbox(window, widget); - if (!toolbox) - continue; - - let position = toolbox.getAttribute(widget.positionAttribute); - if (position) - position = position.replace(/^visible,/, "hidden,"); - else - position = "hidden,,"; - showWidget(toolbox, widget, position); - } - } -}; - -let {WindowObserver} = require("windowObserver"); -new WindowObserver({ - applyToWindow: function(window) - { - let {isKnownWindow} = require("appSupport"); - if (!isKnownWindow(window)) - return; - - for (let [id, widget] of widgets) - { - let toolbox = getToolbox(window, widget); - if (toolbox) - { - toolbox.addEventListener("aftercustomization", onToolbarCustomization, false); - - // Restore widget asynchronously to allow the stylesheet to load - Utils.runAsync(restoreWidget.bind(null, toolbox, widget)); - } - } - }, - - removeFromWindow: function(window) - { - let {isKnownWindow} = require("appSupport"); - if (!isKnownWindow(window)) - return; - - for (let [id, widget] of widgets) - { - let toolbox = getToolbox(window, widget); - if (toolbox) - toolbox.removeEventListener("aftercustomization", onToolbarCustomization, false); - - removeWidget(window, widget); - } - } -}); diff --git a/data/extensions/spyblock@gnu.org/lib/downloader.js b/data/extensions/spyblock@gnu.org/lib/downloader.js deleted file mode 100644 index fd760a7..0000000 --- a/data/extensions/spyblock@gnu.org/lib/downloader.js +++ /dev/null @@ -1,421 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Downloads a set of URLs in regular time intervals. - */ - -const {Utils} = require("utils"); - -const MILLIS_IN_SECOND = exports.MILLIS_IN_SECOND = 1000; -const MILLIS_IN_MINUTE = exports.MILLIS_IN_MINUTE = 60 * MILLIS_IN_SECOND; -const MILLIS_IN_HOUR = exports.MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE; -const MILLIS_IN_DAY = exports.MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; - -let Downloader = -/** - * Creates a new downloader instance. - * @param {Function} dataSource - * Function that will yield downloadable objects on each check - * @param {number} initialDelay - * Number of milliseconds to wait before the first check - * @param {number} checkInterval - * Interval between the checks - * @constructor - */ -exports.Downloader = function(dataSource, initialDelay, checkInterval) -{ - this.dataSource = dataSource; - this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._timer.initWithCallback(() => - { - this._timer.delay = checkInterval; - this._doCheck(); - }, initialDelay, Ci.nsITimer.TYPE_REPEATING_SLACK); - this._downloading = Object.create(null); -}; -Downloader.prototype = -{ - /** - * Timer triggering the downloads. - * @type {nsITimer} - */ - _timer: null, - - /** - * Map containing the URLs of objects currently being downloaded as its keys. - */ - _downloading: null, - - /** - * Function that will yield downloadable objects on each check. - * @type {Function} - */ - dataSource: null, - - /** - * Maximal time interval that the checks can be left out until the soft - * expiration interval increases. - * @type {number} - */ - maxAbsenceInterval: 1 * MILLIS_IN_DAY, - - /** - * Minimal time interval before retrying a download after an error. - * @type {number} - */ - minRetryInterval: 1 * MILLIS_IN_DAY, - - /** - * Maximal allowed expiration interval, larger expiration intervals will be - * corrected. - * @type {number} - */ - maxExpirationInterval: 14 * MILLIS_IN_DAY, - - /** - * Maximal number of redirects before the download is considered as failed. - * @type {number} - */ - maxRedirects: 5, - - /** - * Called whenever expiration intervals for an object need to be adapted. - * @type {Function} - */ - onExpirationChange: null, - - /** - * Callback to be triggered whenever a download starts. - * @type {Function} - */ - onDownloadStarted: null, - - /** - * Callback to be triggered whenever a download finishes successfully. The - * callback can return an error code to indicate that the data is wrong. - * @type {Function} - */ - onDownloadSuccess: null, - - /** - * Callback to be triggered whenever a download fails. - * @type {Function} - */ - onDownloadError: null, - - /** - * Checks whether anything needs downloading. - */ - _doCheck() - { - let now = Date.now(); - for (let downloadable of this.dataSource()) - { - if (downloadable.lastCheck && - now - downloadable.lastCheck > this.maxAbsenceInterval) - { - // No checks for a long time interval - user must have been offline, - // e.g. during a weekend. Increase soft expiration to prevent load - // peaks on the server. - downloadable.softExpiration += now - downloadable.lastCheck; - } - downloadable.lastCheck = now; - - // Sanity check: do expiration times make sense? Make sure people changing - // system clock don't get stuck with outdated subscriptions. - if (downloadable.hardExpiration - now > this.maxExpirationInterval) - downloadable.hardExpiration = now + this.maxExpirationInterval; - if (downloadable.softExpiration - now > this.maxExpirationInterval) - downloadable.softExpiration = now + this.maxExpirationInterval; - - // Notify the caller about changes to expiration parameters - if (this.onExpirationChange) - this.onExpirationChange(downloadable); - - // Does that object need downloading? - if (downloadable.softExpiration > now && - downloadable.hardExpiration > now) - { - continue; - } - - // Do not retry downloads too often - if (downloadable.lastError && - now - downloadable.lastError < this.minRetryInterval) - { - continue; - } - - this._download(downloadable, 0); - } - }, - - /** - * Stops the periodic checks. - */ - cancel() - { - this._timer.cancel(); - }, - - /** - * Checks whether an address is currently being downloaded. - * @param {string} url - * @return {boolean} - */ - isDownloading(url) - { - return url in this._downloading; - }, - - /** - * Starts downloading for an object. - * @param {Downloadable} downloadable - */ - download(downloadable) - { - // Make sure to detach download from the current execution context - Utils.runAsync(this._download.bind(this, downloadable, 0)); - }, - - /** - * Generates the real download URL for an object by appending various - * parameters. - * @param {Downloadable} downloadable - * @return {string} - */ - getDownloadUrl(downloadable) - { - const {addonName, addonVersion, application, applicationVersion, - platform, platformVersion} = require("info"); - let url = downloadable.redirectURL || downloadable.url; - if (url.indexOf("?") >= 0) - url += "&"; - else - url += "?"; - // We limit the download count to 4+ to keep the request anonymized - let {downloadCount} = downloadable; - if (downloadCount > 4) - downloadCount = "4+"; - url += "addonName=" + encodeURIComponent(addonName) + - "&addonVersion=" + encodeURIComponent(addonVersion) + - "&application=" + encodeURIComponent(application) + - "&applicationVersion=" + encodeURIComponent(applicationVersion) + - "&platform=" + encodeURIComponent(platform) + - "&platformVersion=" + encodeURIComponent(platformVersion) + - "&lastVersion=" + encodeURIComponent(downloadable.lastVersion) + - "&downloadCount=" + encodeURIComponent(downloadCount); - return url; - }, - - _download(downloadable, redirects) - { - if (this.isDownloading(downloadable.url)) - return; - - let downloadUrl = this.getDownloadUrl(downloadable); - let request = null; - - let errorCallback = function errorCallback(error) - { - let channelStatus = -1; - try - { - channelStatus = request.channel.status; - } - catch (e) {} - - let responseStatus = request.status; - - Cu.reportError("Adblock Plus: Downloading URL " + downloadable.url + - " failed (" + error + ")\n" + - "Download address: " + downloadUrl + "\n" + - "Channel status: " + channelStatus + "\n" + - "Server response: " + responseStatus); - - if (this.onDownloadError) - { - // Allow one extra redirect if the error handler gives us a redirect URL - let redirectCallback = null; - if (redirects <= this.maxRedirects) - { - redirectCallback = url => - { - downloadable.redirectURL = url; - this._download(downloadable, redirects + 1); - }; - } - - this.onDownloadError(downloadable, downloadUrl, error, channelStatus, - responseStatus, redirectCallback); - } - }.bind(this); - - try - { - request = new XMLHttpRequest(); - request.mozBackgroundRequest = true; - request.open("GET", downloadUrl); - } - catch (e) - { - errorCallback("synchronize_invalid_url"); - return; - } - - try - { - request.overrideMimeType("text/plain"); - request.channel.loadFlags = request.channel.loadFlags | - request.channel.INHIBIT_CACHING | - request.channel.VALIDATE_ALWAYS; - - // Override redirect limit from preferences, user might have set it to 1 - if (request.channel instanceof Ci.nsIHttpChannel) - request.channel.redirectionLimit = this.maxRedirects; - } - catch (e) - { - Cu.reportError(e); - } - - request.addEventListener("error", event => - { - if (onShutdown.done) - return; - - delete this._downloading[downloadable.url]; - errorCallback("synchronize_connection_error"); - }, false); - - request.addEventListener("load", event => - { - if (onShutdown.done) - return; - - delete this._downloading[downloadable.url]; - - // Status will be 0 for non-HTTP requests - if (request.status && request.status != 200) - { - errorCallback("synchronize_connection_error"); - return; - } - - downloadable.downloadCount++; - - this.onDownloadSuccess( - downloadable, request.responseText, errorCallback, - url => - { - if (redirects >= this.maxRedirects) - errorCallback("synchronize_connection_error"); - else - { - downloadable.redirectURL = url; - this._download(downloadable, redirects + 1); - } - } - ); - }); - - request.send(null); - - this._downloading[downloadable.url] = true; - if (this.onDownloadStarted) - this.onDownloadStarted(downloadable); - }, - - /** - * Produces a soft and a hard expiration interval for a given supplied - * expiration interval. - * @param {number} interval - * @return {Array} soft and hard expiration interval - */ - processExpirationInterval(interval) - { - interval = Math.min(Math.max(interval, 0), this.maxExpirationInterval); - let soft = Math.round(interval * (Math.random() * 0.4 + 0.8)); - let hard = interval * 2; - let now = Date.now(); - return [now + soft, now + hard]; - } -}; - -/** - * An object that can be downloaded by the downloadable - * @param {string} url URL that has to be requested for the object - * @constructor - */ -let Downloadable = exports.Downloadable = function Downloadable(url) -{ - this.url = url; -}; -Downloadable.prototype = -{ - /** - * URL that has to be requested for the object. - * @type {string} - */ - url: null, - - /** - * URL that the download was redirected to if any. - * @type {string} - */ - redirectURL: null, - - /** - * Time of last download error or 0 if the last download was successful. - * @type {number} - */ - lastError: 0, - - /** - * Time of last check whether the object needs downloading. - * @type {number} - */ - lastCheck: 0, - - /** - * Object version corresponding to the last successful download. - * @type {number} - */ - lastVersion: 0, - - /** - * Soft expiration interval, will increase if no checks are performed for a - * while. - * @type {number} - */ - softExpiration: 0, - - /** - * Hard expiration interval, this is fixed. - * @type {number} - */ - hardExpiration: 0, - - /** - * Number indicating how often the object was downloaded. - * @type {number} - */ - downloadCount: 0 -}; diff --git a/data/extensions/spyblock@gnu.org/lib/elemHide.js b/data/extensions/spyblock@gnu.org/lib/elemHide.js deleted file mode 100644 index a91f1d4..0000000 --- a/data/extensions/spyblock@gnu.org/lib/elemHide.js +++ /dev/null @@ -1,396 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Element hiding implementation. - */ - -const {ElemHideException} = require("filterClasses"); -const {FilterNotifier} = require("filterNotifier"); - -/** - * Lookup table, filters by their associated key - * @type {Object} - */ -let filterByKey = []; - -/** - * Lookup table, keys of the filters by filter text - * @type {Object} - */ -let keyByFilter = Object.create(null); - -/** - * Nested lookup table, filter (or false if inactive) by filter key by domain. - * (Only contains filters that aren't unconditionally matched for all domains.) - * @type {Object} - */ -let filtersByDomain = Object.create(null); - -/** - * Lookup table, filter key by selector. (Only used for selectors that are - * unconditionally matched for all domains.) - */ -let filterKeyBySelector = Object.create(null); - -/** - * This array caches the keys of filterKeyBySelector table (selectors which - * unconditionally apply on all domains). It will be null if the cache needs to - * be rebuilt. - */ -let unconditionalSelectors = null; - -/** - * This array caches the values of filterKeyBySelector table (filterIds for - * selectors which unconditionally apply on all domains). It will be null if the - * cache needs to be rebuilt. - */ -let unconditionalFilterKeys = null; - -/** - * Object to be used instead when a filter has a blank domains property. - */ -let defaultDomains = Object.create(null); -defaultDomains[""] = true; - -/** - * Lookup table, keys are known element hiding exceptions - * @type {Object} - */ -let knownExceptions = Object.create(null); - -/** - * Lookup table, lists of element hiding exceptions by selector - * @type {Object} - */ -let exceptions = Object.create(null); - -/** - * Container for element hiding filters - * @class - */ -let ElemHide = exports.ElemHide = { - /** - * Removes all known filters - */ - clear() - { - filterByKey = []; - keyByFilter = Object.create(null); - filtersByDomain = Object.create(null); - filterKeyBySelector = Object.create(null); - unconditionalSelectors = unconditionalFilterKeys = null; - knownExceptions = Object.create(null); - exceptions = Object.create(null); - FilterNotifier.emit("elemhideupdate"); - }, - - _addToFiltersByDomain(key, filter) - { - let domains = filter.domains || defaultDomains; - for (let domain in domains) - { - let filters = filtersByDomain[domain]; - if (!filters) - filters = filtersByDomain[domain] = Object.create(null); - - if (domains[domain]) - filters[key] = filter; - else - filters[key] = false; - } - }, - - /** - * Add a new element hiding filter - * @param {ElemHideFilter} filter - */ - add(filter) - { - if (filter instanceof ElemHideException) - { - if (filter.text in knownExceptions) - return; - - let {selector} = filter; - if (!(selector in exceptions)) - exceptions[selector] = []; - exceptions[selector].push(filter); - - // If this is the first exception for a previously unconditionally - // applied element hiding selector we need to take care to update the - // lookups. - let filterKey = filterKeyBySelector[selector]; - if (typeof filterKey != "undefined") - { - this._addToFiltersByDomain(filterKey, filterByKey[filterKey]); - delete filterKeyBySelector[selector]; - unconditionalSelectors = unconditionalFilterKeys = null; - } - - knownExceptions[filter.text] = true; - } - else - { - if (filter.text in keyByFilter) - return; - - let key = filterByKey.push(filter) - 1; - keyByFilter[filter.text] = key; - - if (!(filter.domains || filter.selector in exceptions)) - { - // The new filter's selector is unconditionally applied to all domains - filterKeyBySelector[filter.selector] = key; - unconditionalSelectors = unconditionalFilterKeys = null; - } - else - { - // The new filter's selector only applies to some domains - this._addToFiltersByDomain(key, filter); - } - } - - FilterNotifier.emit("elemhideupdate"); - }, - - _removeFilterKey(key, filter) - { - if (filterKeyBySelector[filter.selector] == key) - { - delete filterKeyBySelector[filter.selector]; - unconditionalSelectors = unconditionalFilterKeys = null; - return; - } - - // We haven't found this filter in unconditional filters, look in - // filtersByDomain. - let domains = filter.domains || defaultDomains; - for (let domain in domains) - { - let filters = filtersByDomain[domain]; - if (filters) - delete filters[key]; - } - }, - - /** - * Removes an element hiding filter - * @param {ElemHideFilter} filter - */ - remove(filter) - { - if (filter instanceof ElemHideException) - { - if (!(filter.text in knownExceptions)) - return; - - let list = exceptions[filter.selector]; - let index = list.indexOf(filter); - if (index >= 0) - list.splice(index, 1); - delete knownExceptions[filter.text]; - } - else - { - if (!(filter.text in keyByFilter)) - return; - - let key = keyByFilter[filter.text]; - delete filterByKey[key]; - delete keyByFilter[filter.text]; - this._removeFilterKey(key, filter); - } - - FilterNotifier.emit("elemhideupdate"); - }, - - /** - * Checks whether an exception rule is registered for a filter on a particular - * domain. - * @param {Filter} filter - * @param {string} docDomain - * @return {ElemHideException} - */ - getException(filter, docDomain) - { - if (!(filter.selector in exceptions)) - return null; - - let list = exceptions[filter.selector]; - for (let i = list.length - 1; i >= 0; i--) - { - if (list[i].isActiveOnDomain(docDomain)) - return list[i]; - } - - return null; - }, - - /** - * Retrieves an element hiding filter by the corresponding protocol key - * @param {number} key - * @return {Filter} - */ - getFilterByKey(key) - { - return (key in filterByKey ? filterByKey[key] : null); - }, - - /** - * Returns a list of all selectors as a nested map. On first level, the keys - * are all values of `ElemHideBase.selectorDomain` (domains on which these - * selectors should apply, ignoring exceptions). The values are maps again, - * with the keys being selectors and values the corresponding filter keys. - * @returns {Map.<String,Map<String,String>>} - */ - getSelectors() - { - let domains = new Map(); - for (let key in filterByKey) - { - let filter = filterByKey[key]; - if (!filter.selector) - continue; - - let domain = filter.selectorDomain || ""; - - if (!domains.has(domain)) - domains.set(domain, new Map()); - domains.get(domain).set(filter.selector, key); - } - - return domains; - }, - - /** - * Returns a list of selectors that apply on each website unconditionally. - * @returns {string[]} - */ - getUnconditionalSelectors() - { - if (!unconditionalSelectors) - unconditionalSelectors = Object.keys(filterKeyBySelector); - return unconditionalSelectors.slice(); - }, - - /** - * Returns a list of filter keys for selectors which apply to all websites - * without exception. - * @returns {number[]} - */ - getUnconditionalFilterKeys() - { - if (!unconditionalFilterKeys) - { - let selectors = this.getUnconditionalSelectors(); - unconditionalFilterKeys = []; - for (let selector of selectors) - unconditionalFilterKeys.push(filterKeyBySelector[selector]); - } - return unconditionalFilterKeys.slice(); - }, - - - /** - * Constant used by getSelectorsForDomain to return all selectors applying to - * a particular hostname. - */ - ALL_MATCHING: 0, - - /** - * Constant used by getSelectorsForDomain to exclude selectors which apply to - * all websites without exception. - */ - NO_UNCONDITIONAL: 1, - - /** - * Constant used by getSelectorsForDomain to return only selectors for filters - * which specifically match the given host name. - */ - SPECIFIC_ONLY: 2, - - /** - * Determines from the current filter list which selectors should be applied - * on a particular host name. Optionally returns the corresponding filter - * keys. - * @param {string} domain - * @param {number} [criteria] - * One of the following: ElemHide.ALL_MATCHING, ElemHide.NO_UNCONDITIONAL or - * ElemHide.SPECIFIC_ONLY. - * @param {boolean} [provideFilterKeys] - * If true, the function will return a list of corresponding filter keys in - * addition to selectors. - * @returns {string[]|Array.<string[]>} - * List of selectors or an array with two elements (list of selectors and - * list of corresponding keys) if provideFilterKeys is true. - */ - getSelectorsForDomain(domain, criteria, provideFilterKeys) - { - let filterKeys = []; - let selectors = []; - - if (typeof criteria == "undefined") - criteria = ElemHide.ALL_MATCHING; - if (criteria < ElemHide.NO_UNCONDITIONAL) - { - selectors = this.getUnconditionalSelectors(); - if (provideFilterKeys) - filterKeys = this.getUnconditionalFilterKeys(); - } - - let specificOnly = (criteria >= ElemHide.SPECIFIC_ONLY); - let seenFilters = Object.create(null); - let currentDomain = domain ? domain.toUpperCase() : ""; - while (true) - { - if (specificOnly && currentDomain == "") - break; - - let filters = filtersByDomain[currentDomain]; - if (filters) - { - for (let filterKey in filters) - { - if (filterKey in seenFilters) - continue; - seenFilters[filterKey] = true; - - let filter = filters[filterKey]; - if (filter && !this.getException(filter, domain)) - { - selectors.push(filter.selector); - // It is faster to always push the key, even if not required. - filterKeys.push(filterKey); - } - } - } - - if (currentDomain == "") - break; - - let nextDot = currentDomain.indexOf("."); - currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); - } - - if (provideFilterKeys) - return [selectors, filterKeys]; - return selectors; - } -}; diff --git a/data/extensions/spyblock@gnu.org/lib/elemHideEmulation.js b/data/extensions/spyblock@gnu.org/lib/elemHideEmulation.js deleted file mode 100644 index edf2082..0000000 --- a/data/extensions/spyblock@gnu.org/lib/elemHideEmulation.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Element hiding emulation implementation. - */ - -const {ElemHide} = require("elemHide"); -const {Filter} = require("filterClasses"); - -let filters = Object.create(null); - -/** - * Container for element hiding emulation filters - * @class - */ -let ElemHideEmulation = { - /** - * Removes all known filters - */ - clear() - { - filters = Object.create(null); - }, - - /** - * Add a new element hiding emulation filter - * @param {ElemHideEmulationFilter} filter - */ - add(filter) - { - filters[filter.text] = true; - }, - - /** - * Removes an element hiding emulation filter - * @param {ElemHideEmulationFilter} filter - */ - remove(filter) - { - delete filters[filter.text]; - }, - - /** - * Returns a list of all rules active on a particular domain - * @param {string} domain - * @return {ElemHideEmulationFilter[]} - */ - getRulesForDomain(domain) - { - let result = []; - let keys = Object.getOwnPropertyNames(filters); - for (let key of keys) - { - let filter = Filter.fromText(key); - if (filter.isActiveOnDomain(domain) && - !ElemHide.getException(filter, domain)) - { - result.push(filter); - } - } - return result; - } -}; -exports.ElemHideEmulation = ElemHideEmulation; diff --git a/data/extensions/spyblock@gnu.org/lib/elemHideFF.js b/data/extensions/spyblock@gnu.org/lib/elemHideFF.js deleted file mode 100644 index fe42e11..0000000 --- a/data/extensions/spyblock@gnu.org/lib/elemHideFF.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - -let {port} = require("messaging"); -let {ElemHide} = require("elemHide"); -let {FilterNotifier} = require("filterNotifier"); -let {FilterStorage} = require("filterStorage"); -let {Prefs} = require("prefs"); -let {Policy} = require("contentPolicy"); -let {Utils} = require("utils"); - -let isDirty = false; -FilterNotifier.on("elemhideupdate", () => -{ - // Notify content process asynchronously, only one message per update batch. - if (!isDirty) - { - isDirty = true; - Utils.runAsync(() => { - isDirty = false; - port.emit("elemhideupdate") - }); - } -}); - -port.on("getUnconditionalSelectors", () => -{ - return [ - ElemHide.getUnconditionalSelectors(), - ElemHide.getUnconditionalFilterKeys() - ]; -}); - -port.on("getSelectorsForDomain", ([domain, specificOnly]) => -{ - let type = specificOnly ? ElemHide.SPECIFIC_ONLY : ElemHide.NO_UNCONDITIONAL; - return ElemHide.getSelectorsForDomain(domain, type, true); -}); - -port.on("elemhideEnabled", ({frames, isPrivate}) => -{ - if (!Prefs.enabled || !Policy.isBlockableScheme(frames[0].location)) - return {enabled: false}; - - let hit = Policy.isFrameWhitelisted(frames, true); - if (hit) - { - let [frameIndex, contentType, docDomain, thirdParty, location, filter] = hit; - if (!isPrivate) - FilterStorage.increaseHitCount(filter); - return { - enabled: contentType == "GENERICHIDE", - contentType, docDomain, thirdParty, location, - filter: filter.text, filterType: filter.type - }; - } - - return {enabled: true}; -}); - -port.on("registerElemHideHit", ({key, frames, isPrivate}) => -{ - let filter = ElemHide.getFilterByKey(key); - if (!filter) - return null; - - if (!isPrivate) - FilterStorage.increaseHitCount(filter); - - let docDomain; - try - { - docDomain = Utils.unwrapURL(frames[0].location).host; - } - catch(e) - { - docDomain = null; - } - - return { - contentType: "ELEMHIDE", - docDomain, - thirdParty: false, - location: filter.text.replace(/^.*?#/, '#'), - filter: filter.text, - filterType: filter.type - }; -}); diff --git a/data/extensions/spyblock@gnu.org/lib/events.js b/data/extensions/spyblock@gnu.org/lib/events.js deleted file mode 100644 index 8d11f7c..0000000 --- a/data/extensions/spyblock@gnu.org/lib/events.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * Registers and emits named events. - * - * @constructor - */ -exports.EventEmitter = function() -{ - this._listeners = Object.create(null); -}; - -exports.EventEmitter.prototype = { - /** - * Adds a listener for the specified event name. - * - * @param {string} name - * @param {function} listener - */ - on(name, listener) - { - if (name in this._listeners) - this._listeners[name].push(listener); - else - this._listeners[name] = [listener]; - }, - - /** - * Removes a listener for the specified event name. - * - * @param {string} name - * @param {function} listener - */ - off(name, listener) - { - let listeners = this._listeners[name]; - if (listeners) - { - let idx = listeners.indexOf(listener); - if (idx != -1) - listeners.splice(idx, 1); - } - }, - - /** - * Adds a one time listener and returns a promise that - * is resolved the next time the specified event is emitted. - * @param {string} name - * @return {Promise} - */ - once(name) - { - return new Promise(resolve => - { - let listener = () => - { - this.off(name, listener); - resolve(); - }; - - this.on(name, listener); - }); - }, - - /** - * Returns a copy of the array of listeners for the specified event. - * - * @param {string} name - * @return {function[]} - */ - listeners(name) - { - let listeners = this._listeners[name]; - return listeners ? listeners.slice() : []; - }, - - /** - * Calls all previously added listeners for the given event name. - * - * @param {string} name - * @param {...*} [arg] - */ - emit(name, ...args) - { - let listeners = this.listeners(name); - for (let listener of listeners) - listener(...args); - } -}; diff --git a/data/extensions/spyblock@gnu.org/lib/ext_background.js b/data/extensions/spyblock@gnu.org/lib/ext_background.js deleted file mode 100644 index b57f96c..0000000 --- a/data/extensions/spyblock@gnu.org/lib/ext_background.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", null); -let {Services} = Cu.import("resource://gre/modules/Services.jsm", null); - -let {_EventTarget: EventTarget, i18n} = require("ext_common"); -let {port} = require("messaging"); - -exports.onMessage = new EventTarget(port); -exports.i18n = i18n; - -function Page(windowID) -{ - this._windowID = windowID; -} -Page.prototype = { - sendMessage: function(payload) - { - port.emit("ext_message", {targetID: this._windowID, payload}); - } -}; -exports.Page = Page; - -function PageMap() -{ - this._map = new Map(); - - port.on("ext_disconnect", windowID => this._map.delete(windowID)); -} -PageMap.prototype = { - keys: function() - { - let result = []; - for (let windowID of this._map.keys()) - result.push(new Page(windowID)); - return result; - }, - - get: function(page) - { - return this._map.get(page._windowID); - }, - - set: function(page, value) - { - this._map.set(page._windowID, value); - }, - - has: function(page) - { - return this._map.has(page._windowID); - }, - - delete: function(page) - { - return this._map.delete(page._windowID); - } -}; -exports.PageMap = PageMap; - -exports.showOptions = function() -{ - require("ui").UI.openFiltersDialog(); -}; diff --git a/data/extensions/spyblock@gnu.org/lib/ext_common.js b/data/extensions/spyblock@gnu.org/lib/ext_common.js deleted file mode 100644 index 296c00f..0000000 --- a/data/extensions/spyblock@gnu.org/lib/ext_common.js +++ /dev/null @@ -1,124 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -(function(global) -{ - const Cu = Components.utils; - - let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - - if (!global.ext) - global.ext = {}; - - var wrapperSymbol = Symbol("ext-wrapper"); - - function wrapFrames(frames) - { - if (!frames.length) - return null; - - // We have frames as an array, non-Firefox code expects url and parent - // properties however. - Object.defineProperty(frames, "url", { - enumerable: true, - get: () => new URL(frames[0].location) - }); - - Object.defineProperty(frames, "parent", { - enumerable: true, - get: () => wrapFrames(frames.slice(1)) - }); - - return frames; - } - - var EventTarget = global.ext._EventTarget = function(port, windowID) - { - this._port = port; - this._windowID = windowID; - this.addListener((payload, sender, resolve) => - { - if (payload.type) - { - let result = this._port._dispatch(payload.type, payload, sender); - if (typeof result != "undefined") - resolve(result); - } - }); - }; - EventTarget.prototype = { - addListener: function(listener) - { - var wrapper = (message, sender) => - { - if (this._windowID && this._windowID != message.targetID) - return undefined; - - return new Promise((resolve, reject) => - { - var sender = {}; - if (message.senderID) - { - // We will only get here on the background side so we can access - // the Page object. - const Page = require("ext_background").Page; - sender.page = new Page(message.senderID); - } - if (message.frames) - sender.frame = wrapFrames(message.frames); - if (!listener(message.payload, sender, resolve)) - resolve(undefined); - }); - }; - listener[wrapperSymbol] = wrapper; - this._port.on("ext_message", wrapper); - }, - - removeListener: function(listener) - { - if (listener[wrapperSymbol]) - this._port.off("ext_message", listener[wrapperSymbol]); - } - }; - - let pageName = "global"; - if (typeof location !== "undefined") - pageName = location.pathname.replace(/.*\//, "").replace(/\..*?$/, ""); - - let stringBundle = Services.strings.createBundle( - "chrome://adblockplus/locale/" + pageName + ".properties?" + Math.random()); - - global.ext.i18n = { - getMessage(key, args) - { - try { - return stringBundle.GetStringFromName(key); - } - catch(e) - { - // Don't report errors for special strings, these are expected to be - // missing. - if (key[0] != "@") - Cu.reportError(e); - return ""; - } - } - }; - - if (typeof exports == "object") - exports = global.ext; -})(this); diff --git a/data/extensions/spyblock@gnu.org/lib/filterClasses.js b/data/extensions/spyblock@gnu.org/lib/filterClasses.js deleted file mode 100644 index 3612728..0000000 --- a/data/extensions/spyblock@gnu.org/lib/filterClasses.js +++ /dev/null @@ -1,1061 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Definition of Filter class and its subclasses. - */ - -const {FilterNotifier} = require("filterNotifier"); -const {extend} = require("coreUtils"); -const {filterToRegExp} = require("common"); - -/** - * Abstract base class for filters - * - * @param {string} text string representation of the filter - * @constructor - */ -function Filter(text) -{ - this.text = text; - this.subscriptions = []; -} -exports.Filter = Filter; - -Filter.prototype = -{ - /** - * String representation of the filter - * @type {string} - */ - text: null, - - /** - * Filter subscriptions the filter belongs to - * @type {Subscription[]} - */ - subscriptions: null, - - /** - * Filter type as a string, e.g. "blocking". - * @type {string} - */ - get type() - { - throw new Error("Please define filter type in the subclass"); - }, - - /** - * Serializes the filter to an array of strings for writing out on the disk. - * @param {string[]} buffer buffer to push the serialization results into - */ - serialize(buffer) - { - buffer.push("[Filter]"); - buffer.push("text=" + this.text); - }, - - toString() - { - return this.text; - } -}; - -/** - * Cache for known filters, maps string representation to filter objects. - * @type {Object} - */ -Filter.knownFilters = Object.create(null); - -/** - * Regular expression that element hiding filters should match - * @type {RegExp} - */ -Filter.elemhideRegExp = /^([^/*|@"!]*?)#(@)?(?:([\w-]+|\*)((?:\([\w-]+(?:[$^*]?=[^()"]*)?\))*)|#(.+))$/; -/** - * Regular expression that RegExp filters specified as RegExps should match - * @type {RegExp} - */ -Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^,\s]+)?)*)?$/; -/** - * Regular expression that options on a RegExp filter should match - * @type {RegExp} - */ -Filter.optionsRegExp = /\$(~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^,\s]+)?)*)$/; - -/** - * Creates a filter of correct type from its text representation - does the - * basic parsing and calls the right constructor then. - * - * @param {string} text as in Filter() - * @return {Filter} - */ -Filter.fromText = function(text) -{ - if (text in Filter.knownFilters) - return Filter.knownFilters[text]; - - let ret; - let match = (text.includes("#") ? Filter.elemhideRegExp.exec(text) : null); - if (match) - { - ret = ElemHideBase.fromText( - text, match[1], !!match[2], match[3], match[4], match[5] - ); - } - else if (text[0] == "!") - ret = new CommentFilter(text); - else - ret = RegExpFilter.fromText(text); - - Filter.knownFilters[ret.text] = ret; - return ret; -}; - -/** - * Deserializes a filter - * - * @param {Object} obj map of serialized properties and their values - * @return {Filter} filter or null if the filter couldn't be created - */ -Filter.fromObject = function(obj) -{ - let ret = Filter.fromText(obj.text); - if (ret instanceof ActiveFilter) - { - if ("disabled" in obj) - ret._disabled = (obj.disabled == "true"); - if ("hitCount" in obj) - ret._hitCount = parseInt(obj.hitCount, 10) || 0; - if ("lastHit" in obj) - ret._lastHit = parseInt(obj.lastHit, 10) || 0; - } - return ret; -}; - -/** - * Removes unnecessary whitespaces from filter text, will only return null if - * the input parameter is null. - * @param {string} text - * @return {string} - */ -Filter.normalize = function(text) -{ - if (!text) - return text; - - // Remove line breaks and such - text = text.replace(/[^\S ]/g, ""); - - if (/^\s*!/.test(text)) - { - // Don't remove spaces inside comments - return text.trim(); - } - else if (Filter.elemhideRegExp.test(text)) - { - // Special treatment for element hiding filters, right side is allowed to - // contain spaces - let [, domain, separator, selector] = /^(.*?)(#@?#?)(.*)$/.exec(text); - return domain.replace(/\s/g, "") + separator + selector.trim(); - } - return text.replace(/\s/g, ""); -}; - -/** - * @see filterToRegExp - */ -Filter.toRegExp = filterToRegExp; - -/** - * Class for invalid filters - * @param {string} text see Filter() - * @param {string} reason Reason why this filter is invalid - * @constructor - * @augments Filter - */ -function InvalidFilter(text, reason) -{ - Filter.call(this, text); - - this.reason = reason; -} -exports.InvalidFilter = InvalidFilter; - -InvalidFilter.prototype = extend(Filter, { - type: "invalid", - - /** - * Reason why this filter is invalid - * @type {string} - */ - reason: null, - - /** - * See Filter.serialize() - * @inheritdoc - */ - serialize(buffer) {} -}); - -/** - * Class for comments - * @param {string} text see Filter() - * @constructor - * @augments Filter - */ -function CommentFilter(text) -{ - Filter.call(this, text); -} -exports.CommentFilter = CommentFilter; - -CommentFilter.prototype = extend(Filter, { - type: "comment", - - /** - * See Filter.serialize() - * @inheritdoc - */ - serialize(buffer) {} -}); - -/** - * Abstract base class for filters that can get hits - * @param {string} text - * see Filter() - * @param {string} [domains] - * Domains that the filter is restricted to separated by domainSeparator - * e.g. "foo.com|bar.com|~baz.com" - * @constructor - * @augments Filter - */ -function ActiveFilter(text, domains) -{ - Filter.call(this, text); - - this.domainSource = domains; -} -exports.ActiveFilter = ActiveFilter; - -ActiveFilter.prototype = extend(Filter, { - _disabled: false, - _hitCount: 0, - _lastHit: 0, - - /** - * Defines whether the filter is disabled - * @type {boolean} - */ - get disabled() - { - return this._disabled; - }, - set disabled(value) - { - if (value != this._disabled) - { - let oldValue = this._disabled; - this._disabled = value; - FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); - } - return this._disabled; - }, - - /** - * Number of hits on the filter since the last reset - * @type {number} - */ - get hitCount() - { - return this._hitCount; - }, - set hitCount(value) - { - if (value != this._hitCount) - { - let oldValue = this._hitCount; - this._hitCount = value; - FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); - } - return this._hitCount; - }, - - /** - * Last time the filter had a hit (in milliseconds since the beginning of the - * epoch) - * @type {number} - */ - get lastHit() - { - return this._lastHit; - }, - set lastHit(value) - { - if (value != this._lastHit) - { - let oldValue = this._lastHit; - this._lastHit = value; - FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); - } - return this._lastHit; - }, - - /** - * String that the domains property should be generated from - * @type {string} - */ - domainSource: null, - - /** - * Separator character used in domainSource property, must be - * overridden by subclasses - * @type {string} - */ - domainSeparator: null, - - /** - * Determines whether the trailing dot in domain names isn't important and - * should be ignored, must be overridden by subclasses. - * @type {boolean} - */ - ignoreTrailingDot: true, - - /** - * Determines whether domainSource is already upper-case, - * can be overridden by subclasses. - * @type {boolean} - */ - domainSourceIsUpperCase: false, - - /** - * Map containing domains that this filter should match on/not match - * on or null if the filter should match on all domains - * @type {Object} - */ - get domains() - { - // Despite this property being cached, the getter is called - // several times on Safari, due to WebKit bug 132872 - let prop = Object.getOwnPropertyDescriptor(this, "domains"); - if (prop) - return prop.value; - - let domains = null; - - if (this.domainSource) - { - let source = this.domainSource; - if (!this.domainSourceIsUpperCase) - { - // RegExpFilter already have uppercase domains - source = source.toUpperCase(); - } - let list = source.split(this.domainSeparator); - if (list.length == 1 && list[0][0] != "~") - { - // Fast track for the common one-domain scenario - domains = Object.create(null); - domains[""] = false; - if (this.ignoreTrailingDot) - list[0] = list[0].replace(/\.+$/, ""); - domains[list[0]] = true; - } - else - { - let hasIncludes = false; - for (let i = 0; i < list.length; i++) - { - let domain = list[i]; - if (this.ignoreTrailingDot) - domain = domain.replace(/\.+$/, ""); - if (domain == "") - continue; - - let include; - if (domain[0] == "~") - { - include = false; - domain = domain.substr(1); - } - else - { - include = true; - hasIncludes = true; - } - - if (!domains) - domains = Object.create(null); - - domains[domain] = include; - } - if (domains) - domains[""] = !hasIncludes; - } - - this.domainSource = null; - } - - Object.defineProperty(this, "domains", {value: domains, enumerable: true}); - return this.domains; - }, - - /** - * Array containing public keys of websites that this filter should apply to - * @type {string[]} - */ - sitekeys: null, - - /** - * Checks whether this filter is active on a domain. - * @param {string} docDomain domain name of the document that loads the URL - * @param {string} [sitekey] public key provided by the document - * @return {boolean} true in case of the filter being active - */ - isActiveOnDomain(docDomain, sitekey) - { - // Sitekeys are case-sensitive so we shouldn't convert them to - // upper-case to avoid false positives here. Instead we need to - // change the way filter options are parsed. - if (this.sitekeys && - (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase()) < 0)) - { - return false; - } - - // If no domains are set the rule matches everywhere - if (!this.domains) - return true; - - // If the document has no host name, match only if the filter - // isn't restricted to specific domains - if (!docDomain) - return this.domains[""]; - - if (this.ignoreTrailingDot) - docDomain = docDomain.replace(/\.+$/, ""); - docDomain = docDomain.toUpperCase(); - - while (true) - { - if (docDomain in this.domains) - return this.domains[docDomain]; - - let nextDot = docDomain.indexOf("."); - if (nextDot < 0) - break; - docDomain = docDomain.substr(nextDot + 1); - } - return this.domains[""]; - }, - - /** - * Checks whether this filter is active only on a domain and its subdomains. - * @param {string} docDomain - * @return {boolean} - */ - isActiveOnlyOnDomain(docDomain) - { - if (!docDomain || !this.domains || this.domains[""]) - return false; - - if (this.ignoreTrailingDot) - docDomain = docDomain.replace(/\.+$/, ""); - docDomain = docDomain.toUpperCase(); - - for (let domain in this.domains) - { - if (this.domains[domain] && domain != docDomain) - { - if (domain.length <= docDomain.length) - return false; - - if (!domain.endsWith("." + docDomain)) - return false; - } - } - - return true; - }, - - /** - * Checks whether this filter is generic or specific - * @return {boolean} - */ - isGeneric() - { - return !(this.sitekeys && this.sitekeys.length) && - (!this.domains || this.domains[""]); - }, - - /** - * See Filter.serialize() - * @inheritdoc - */ - serialize(buffer) - { - if (this._disabled || this._hitCount || this._lastHit) - { - Filter.prototype.serialize.call(this, buffer); - if (this._disabled) - buffer.push("disabled=true"); - if (this._hitCount) - buffer.push("hitCount=" + this._hitCount); - if (this._lastHit) - buffer.push("lastHit=" + this._lastHit); - } - } -}); - -/** - * Abstract base class for RegExp-based filters - * @param {string} text see Filter() - * @param {string} regexpSource - * filter part that the regular expression should be build from - * @param {number} [contentType] - * Content types the filter applies to, combination of values from - * RegExpFilter.typeMap - * @param {boolean} [matchCase] - * Defines whether the filter should distinguish between lower and upper case - * letters - * @param {string} [domains] - * Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" - * @param {boolean} [thirdParty] - * Defines whether the filter should apply to third-party or first-party - * content only - * @param {string} [sitekeys] - * Public keys of websites that this filter should apply to - * @constructor - * @augments ActiveFilter - */ -function RegExpFilter(text, regexpSource, contentType, matchCase, domains, - thirdParty, sitekeys) -{ - ActiveFilter.call(this, text, domains, sitekeys); - - if (contentType != null) - this.contentType = contentType; - if (matchCase) - this.matchCase = matchCase; - if (thirdParty != null) - this.thirdParty = thirdParty; - if (sitekeys != null) - this.sitekeySource = sitekeys; - - if (regexpSource.length >= 2 && - regexpSource[0] == "/" && - regexpSource[regexpSource.length - 1] == "/") - { - // The filter is a regular expression - convert it immediately to - // catch syntax errors - let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), - this.matchCase ? "" : "i"); - Object.defineProperty(this, "regexp", {value: regexp}); - } - else - { - // No need to convert this filter to regular expression yet, do it on demand - this.regexpSource = regexpSource; - } -} -exports.RegExpFilter = RegExpFilter; - -RegExpFilter.prototype = extend(ActiveFilter, { - /** - * @see ActiveFilter.domainSourceIsUpperCase - */ - domainSourceIsUpperCase: true, - - /** - * Number of filters contained, will always be 1 (required to - * optimize Matcher). - * @type {number} - */ - length: 1, - - /** - * @see ActiveFilter.domainSeparator - */ - domainSeparator: "|", - - /** - * Expression from which a regular expression should be generated - - * for delayed creation of the regexp property - * @type {string} - */ - regexpSource: null, - /** - * Regular expression to be used when testing against this filter - * @type {RegExp} - */ - get regexp() - { - // Despite this property being cached, the getter is called - // several times on Safari, due to WebKit bug 132872 - let prop = Object.getOwnPropertyDescriptor(this, "regexp"); - if (prop) - return prop.value; - - let source = Filter.toRegExp(this.regexpSource); - let regexp = new RegExp(source, this.matchCase ? "" : "i"); - Object.defineProperty(this, "regexp", {value: regexp}); - return regexp; - }, - /** - * Content types the filter applies to, combination of values from - * RegExpFilter.typeMap - * @type {number} - */ - contentType: 0x7FFFFFFF, - /** - * Defines whether the filter should distinguish between lower and - * upper case letters - * @type {boolean} - */ - matchCase: false, - /** - * Defines whether the filter should apply to third-party or - * first-party content only. Can be null (apply to all content). - * @type {boolean} - */ - thirdParty: null, - - /** - * String that the sitekey property should be generated from - * @type {string} - */ - sitekeySource: null, - - /** - * Array containing public keys of websites that this filter should apply to - * @type {string[]} - */ - get sitekeys() - { - // Despite this property being cached, the getter is called - // several times on Safari, due to WebKit bug 132872 - let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); - if (prop) - return prop.value; - - let sitekeys = null; - - if (this.sitekeySource) - { - sitekeys = this.sitekeySource.split("|"); - this.sitekeySource = null; - } - - Object.defineProperty( - this, "sitekeys", {value: sitekeys, enumerable: true} - ); - return this.sitekeys; - }, - - /** - * Tests whether the URL matches this filter - * @param {string} location URL to be tested - * @param {number} typeMask bitmask of content / request types to match - * @param {string} docDomain domain name of the document that loads the URL - * @param {boolean} thirdParty should be true if the URL is a third-party - * request - * @param {string} sitekey public key provided by the document - * @return {boolean} true in case of a match - */ - matches(location, typeMask, docDomain, thirdParty, sitekey, privatenode) - { - if(this.subscriptions[0]) - if (this.subscriptions[0].privateMode) - if (privatenode==false) - return false; - - if (this.contentType & typeMask && - (this.thirdParty == null || this.thirdParty == thirdParty) && - this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) - { - return true; - } - return false; - } -}); - -// Required to optimize Matcher, see also RegExpFilter.prototype.length -Object.defineProperty(RegExpFilter.prototype, "0", { - get() { return this; } -}); - -/** - * Creates a RegExp filter from its text representation - * @param {string} text same as in Filter() - * @return {Filter} - */ -RegExpFilter.fromText = function(text) -{ - let blocking = true; - let origText = text; - if (text.indexOf("@@") == 0) - { - blocking = false; - text = text.substr(2); - } - - let contentType = null; - let matchCase = null; - let domains = null; - let sitekeys = null; - let thirdParty = null; - let collapse = null; - let options; - let match = (text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null); - if (match) - { - options = match[1].toUpperCase().split(","); - text = match.input.substr(0, match.index); - for (let option of options) - { - let value = null; - let separatorIndex = option.indexOf("="); - if (separatorIndex >= 0) - { - value = option.substr(separatorIndex + 1); - option = option.substr(0, separatorIndex); - } - option = option.replace(/-/, "_"); - if (option in RegExpFilter.typeMap) - { - if (contentType == null) - contentType = 0; - contentType |= RegExpFilter.typeMap[option]; - } - else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) - { - if (contentType == null) - ({contentType} = RegExpFilter.prototype); - contentType &= ~RegExpFilter.typeMap[option.substr(1)]; - } - else if (option == "MATCH_CASE") - matchCase = true; - else if (option == "~MATCH_CASE") - matchCase = false; - else if (option == "DOMAIN" && typeof value != "undefined") - domains = value; - else if (option == "THIRD_PARTY") - thirdParty = true; - else if (option == "~THIRD_PARTY") - thirdParty = false; - else if (option == "COLLAPSE") - collapse = true; - else if (option == "~COLLAPSE") - collapse = false; - else if (option == "SITEKEY" && typeof value != "undefined") - sitekeys = value; - else - return new InvalidFilter(origText, "filter_unknown_option"); - } - } - - try - { - if (blocking) - { - return new BlockingFilter(origText, text, contentType, matchCase, domains, - thirdParty, sitekeys, collapse); - } - return new WhitelistFilter(origText, text, contentType, matchCase, domains, - thirdParty, sitekeys); - } - catch (e) - { - return new InvalidFilter(origText, "filter_invalid_regexp"); - } -}; - -/** - * Maps type strings like "SCRIPT" or "OBJECT" to bit masks - */ -RegExpFilter.typeMap = { - OTHER: 1, - SCRIPT: 2, - IMAGE: 4, - STYLESHEET: 8, - OBJECT: 16, - SUBDOCUMENT: 32, - DOCUMENT: 64, - WEBSOCKET: 128, - WEBRTC: 256, - XBL: 1, - PING: 1024, - XMLHTTPREQUEST: 2048, - OBJECT_SUBREQUEST: 4096, - DTD: 1, - MEDIA: 16384, - FONT: 32768, - - BACKGROUND: 4, // Backwards compat, same as IMAGE - - POPUP: 0x10000000, - GENERICBLOCK: 0x20000000, - ELEMHIDE: 0x40000000, - GENERICHIDE: 0x80000000 -}; - -// DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't -// be there by default -RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.DOCUMENT | - RegExpFilter.typeMap.ELEMHIDE | - RegExpFilter.typeMap.POPUP | - RegExpFilter.typeMap.GENERICHIDE | - RegExpFilter.typeMap.GENERICBLOCK); - -/** - * Class for blocking filters - * @param {string} text see Filter() - * @param {string} regexpSource see RegExpFilter() - * @param {number} contentType see RegExpFilter() - * @param {boolean} matchCase see RegExpFilter() - * @param {string} domains see RegExpFilter() - * @param {boolean} thirdParty see RegExpFilter() - * @param {string} sitekeys see RegExpFilter() - * @param {boolean} collapse - * defines whether the filter should collapse blocked content, can be null - * @constructor - * @augments RegExpFilter - */ -function BlockingFilter(text, regexpSource, contentType, matchCase, domains, - thirdParty, sitekeys, collapse) -{ - RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, - thirdParty, sitekeys); - - this.collapse = collapse; -} -exports.BlockingFilter = BlockingFilter; - -BlockingFilter.prototype = extend(RegExpFilter, { - type: "blocking", - - /** - * Defines whether the filter should collapse blocked content. - * Can be null (use the global preference). - * @type {boolean} - */ - collapse: null -}); - -/** - * Class for whitelist filters - * @param {string} text see Filter() - * @param {string} regexpSource see RegExpFilter() - * @param {number} contentType see RegExpFilter() - * @param {boolean} matchCase see RegExpFilter() - * @param {string} domains see RegExpFilter() - * @param {boolean} thirdParty see RegExpFilter() - * @param {string} sitekeys see RegExpFilter() - * @constructor - * @augments RegExpFilter - */ -function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, - thirdParty, sitekeys) -{ - RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, - thirdParty, sitekeys); -} -exports.WhitelistFilter = WhitelistFilter; - -WhitelistFilter.prototype = extend(RegExpFilter, { - type: "whitelist" -}); - -/** - * Base class for element hiding filters - * @param {string} text see Filter() - * @param {string} [domains] Host names or domains the filter should be - * restricted to - * @param {string} selector CSS selector for the HTML elements that should be - * hidden - * @constructor - * @augments ActiveFilter - */ -function ElemHideBase(text, domains, selector) -{ - ActiveFilter.call(this, text, domains || null); - - if (domains) - { - this.selectorDomain = domains.replace(/,~[^,]+/g, "") - .replace(/^~[^,]+,?/, "").toLowerCase(); - } - - // Braces are being escaped to prevent CSS rule injection. - this.selector = selector.replace("{", "\\x7B ").replace("}", "\\x7D "); -} -exports.ElemHideBase = ElemHideBase; - -ElemHideBase.prototype = extend(ActiveFilter, { - /** - * @see ActiveFilter.domainSeparator - */ - domainSeparator: ",", - - /** - * @see ActiveFilter.ignoreTrailingDot - */ - ignoreTrailingDot: false, - - /** - * Host name or domain the filter should be restricted to (can be null for - * no restriction) - * @type {string} - */ - selectorDomain: null, - /** - * CSS selector for the HTML elements that should be hidden - * @type {string} - */ - selector: null -}); - -/** - * Creates an element hiding filter from a pre-parsed text representation - * - * @param {string} text same as in Filter() - * @param {string} domain - * domain part of the text representation (can be empty) - * @param {boolean} isException exception rule indicator - * @param {string} tagName tag name part (can be empty) - * @param {string} attrRules attribute matching rules (can be empty) - * @param {string} selector raw CSS selector (can be empty) - * @return {ElemHideFilter|ElemHideException| - * ElemHideEmulationFilter|InvalidFilter} - */ -ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, - selector) -{ - if (!selector) - { - if (tagName == "*") - tagName = ""; - - let id = null; - let additional = ""; - if (attrRules) - { - attrRules = attrRules.match(/\([\w-]+(?:[$^*]?=[^()"]*)?\)/g); - for (let rule of attrRules) - { - rule = rule.substr(1, rule.length - 2); - let separatorPos = rule.indexOf("="); - if (separatorPos > 0) - { - rule = rule.replace(/=/, '="') + '"'; - additional += "[" + rule + "]"; - } - else - { - if (id) - return new InvalidFilter(text, "filter_elemhide_duplicate_id"); - - id = rule; - } - } - } - - if (id) - selector = `${tagName}.${id}${additional},${tagName}#${id}${additional}`; - else if (tagName || additional) - selector = tagName + additional; - else - return new InvalidFilter(text, "filter_elemhide_nocriteria"); - } - - // We don't allow ElemHide filters which have any empty domains. - // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that - // changes this must be changed too. - if (domain && /(^|,)~?(,|$)/.test(domain)) - return new InvalidFilter(text, "filter_invalid_domain"); - - if (isException) - return new ElemHideException(text, domain, selector); - - if (selector.indexOf("[-abp-properties=") != -1) - { - // Element hiding emulation filters are inefficient so we need to make sure - // that they're only applied if they specify active domains - if (!/,[^~][^,.]*\.[^,]/.test("," + domain)) - return new InvalidFilter(text, "filter_elemhideemulation_nodomain"); - - return new ElemHideEmulationFilter(text, domain, selector); - } - - return new ElemHideFilter(text, domain, selector); -}; - -/** - * Class for element hiding filters - * @param {string} text see Filter() - * @param {string} domains see ElemHideBase() - * @param {string} selector see ElemHideBase() - * @constructor - * @augments ElemHideBase - */ -function ElemHideFilter(text, domains, selector) -{ - ElemHideBase.call(this, text, domains, selector); -} -exports.ElemHideFilter = ElemHideFilter; - -ElemHideFilter.prototype = extend(ElemHideBase, { - type: "elemhide" -}); - -/** - * Class for element hiding exceptions - * @param {string} text see Filter() - * @param {string} domains see ElemHideBase() - * @param {string} selector see ElemHideBase() - * @constructor - * @augments ElemHideBase - */ -function ElemHideException(text, domains, selector) -{ - ElemHideBase.call(this, text, domains, selector); -} -exports.ElemHideException = ElemHideException; - -ElemHideException.prototype = extend(ElemHideBase, { - type: "elemhideexception" -}); - -/** - * Class for element hiding emulation filters - * @param {string} text see Filter() - * @param {string} domains see ElemHideBase() - * @param {string} selector see ElemHideBase() - * @constructor - * @augments ElemHideBase - */ -function ElemHideEmulationFilter(text, domains, selector) -{ - ElemHideBase.call(this, text, domains, selector); -} -exports.ElemHideEmulationFilter = ElemHideEmulationFilter; - -ElemHideEmulationFilter.prototype = extend(ElemHideBase, { - type: "elemhideemulation" -}); diff --git a/data/extensions/spyblock@gnu.org/lib/filterListener.js b/data/extensions/spyblock@gnu.org/lib/filterListener.js deleted file mode 100644 index 8700602..0000000 --- a/data/extensions/spyblock@gnu.org/lib/filterListener.js +++ /dev/null @@ -1,320 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Component synchronizing filter storage with Matcher - * instances and ElemHide. - */ - -const {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); -const {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); - -const {FilterStorage} = require("filterStorage"); -const {FilterNotifier} = require("filterNotifier"); -const {ElemHide} = require("elemHide"); -const {ElemHideEmulation} = require("elemHideEmulation"); -const {defaultMatcher} = require("matcher"); -const {ActiveFilter, RegExpFilter, - ElemHideBase, ElemHideEmulationFilter} = require("filterClasses"); -const {Prefs} = require("prefs"); - -/** - * Increases on filter changes, filters will be saved if it exceeds 1. - * @type {number} - */ -let isDirty = 0; - -/** - * This object can be used to change properties of the filter change listeners. - * @class - */ -let FilterListener = { - /** - * Increases "dirty factor" of the filters and calls - * FilterStorage.saveToDisk() if it becomes 1 or more. Save is - * executed delayed to prevent multiple subsequent calls. If the - * parameter is 0 it forces saving filters if any changes were - * recorded after the previous save. - * @param {number} factor - */ - setDirty(factor) - { - if (factor == 0 && isDirty > 0) - isDirty = 1; - else - isDirty += factor; - if (isDirty >= 1) - { - isDirty = 0; - FilterStorage.saveToDisk(); - } - } -}; - -/** - * Observer listening to history purge actions. - * @class - */ -let HistoryPurgeObserver = { - observe(subject, topic, data) - { - if (topic == "browser:purge-session-history" && - Prefs.clearStatsOnHistoryPurge) - { - FilterStorage.resetHitCounts(); - FilterListener.setDirty(0); // Force saving to disk - - Prefs.recentReports = []; - } - }, - QueryInterface: XPCOMUtils.generateQI( - [Ci.nsISupportsWeakReference, Ci.nsIObserver] - ) -}; - -/** - * Initializes filter listener on startup, registers the necessary hooks. - */ -function init() -{ - FilterNotifier.on("filter.hitCount", onFilterHitCount); - FilterNotifier.on("filter.lastHit", onFilterLastHit); - FilterNotifier.on("filter.added", onFilterAdded); - FilterNotifier.on("filter.removed", onFilterRemoved); - FilterNotifier.on("filter.disabled", onFilterDisabled); - FilterNotifier.on("filter.moved", onGenericChange); - - FilterNotifier.on("subscription.added", onSubscriptionAdded); - FilterNotifier.on("subscription.removed", onSubscriptionRemoved); - FilterNotifier.on("subscription.disabled", onSubscriptionDisabled); - FilterNotifier.on("subscription.updated", onSubscriptionUpdated); - FilterNotifier.on("subscription.moved", onGenericChange); - FilterNotifier.on("subscription.title", onGenericChange); - FilterNotifier.on("subscription.fixedTitle", onGenericChange); - FilterNotifier.on("subscription.homepage", onGenericChange); - FilterNotifier.on("subscription.downloadStatus", onGenericChange); - FilterNotifier.on("subscription.lastCheck", onGenericChange); - FilterNotifier.on("subscription.errors", onGenericChange); - - FilterNotifier.on("load", onLoad); - FilterNotifier.on("save", onSave); - - FilterStorage.loadFromDisk(); - - Services.obs.addObserver(HistoryPurgeObserver, - "browser:purge-session-history", true); - onShutdown.add(() => - { - Services.obs.removeObserver(HistoryPurgeObserver, - "browser:purge-session-history"); - }); -} -init(); - -/** - * Notifies Matcher instances or ElemHide object about a new filter - * if necessary. - * @param {Filter} filter filter that has been added - */ -function addFilter(filter) -{ - if (!(filter instanceof ActiveFilter) || filter.disabled) - return; - - let hasEnabled = false; - for (let i = 0; i < filter.subscriptions.length; i++) - { - if (!filter.subscriptions[i].disabled) - hasEnabled = true; - } - if (!hasEnabled) - return; - - if (filter instanceof RegExpFilter) - defaultMatcher.add(filter); - else if (filter instanceof ElemHideBase) - { - if (filter instanceof ElemHideEmulationFilter) - ElemHideEmulation.add(filter); - else - ElemHide.add(filter); - } -} - -/** - * Notifies Matcher instances or ElemHide object about removal of a filter - * if necessary. - * @param {Filter} filter filter that has been removed - */ -function removeFilter(filter) -{ - if (!(filter instanceof ActiveFilter)) - return; - - if (!filter.disabled) - { - let hasEnabled = false; - for (let i = 0; i < filter.subscriptions.length; i++) - { - if (!filter.subscriptions[i].disabled) - hasEnabled = true; - } - if (hasEnabled) - return; - } - - if (filter instanceof RegExpFilter) - defaultMatcher.remove(filter); - else if (filter instanceof ElemHideBase) - { - if (filter instanceof ElemHideEmulationFilter) - ElemHideEmulation.remove(filter); - else - ElemHide.remove(filter); - } -} - -const primes = [101, 109, 131, 149, 163, 179, 193, 211, 229, 241]; - -function addFilters(filters) -{ - // We add filters using pseudo-random ordering. Reason is that ElemHide will - // assign consecutive filter IDs that might be visible to the website. The - // randomization makes sure that no conclusion can be made about the actual - // filters applying there. We have ten prime numbers to use as iteration step, - // any of those can be chosen as long as the array length isn't divisible by - // it. - let len = filters.length; - if (!len) - return; - - let current = (Math.random() * len) | 0; - let step; - do - { - step = primes[(Math.random() * primes.length) | 0]; - } while (len % step == 0); - - for (let i = 0; i < len; i++, current = (current + step) % len) - addFilter(filters[current]); -} - -function onSubscriptionAdded(subscription) -{ - FilterListener.setDirty(1); - - if (!subscription.disabled) - addFilters(subscription.filters); -} - -function onSubscriptionRemoved(subscription) -{ - FilterListener.setDirty(1); - - if (!subscription.disabled) - subscription.filters.forEach(removeFilter); -} - -function onSubscriptionDisabled(subscription, newValue) -{ - FilterListener.setDirty(1); - - if (subscription.url in FilterStorage.knownSubscriptions) - { - if (newValue == false) - addFilters(subscription.filters); - else - subscription.filters.forEach(removeFilter); - } -} - -function onSubscriptionUpdated(subscription) -{ - FilterListener.setDirty(1); - - if (subscription.url in FilterStorage.knownSubscriptions && - !subscription.disabled) - { - subscription.oldFilters.forEach(removeFilter); - addFilters(subscription.filters); - } -} - -function onFilterHitCount(filter, newValue) -{ - if (newValue == 0) - FilterListener.setDirty(0); - else - FilterListener.setDirty(0.002); -} - -function onFilterLastHit() -{ - FilterListener.setDirty(0.002); -} - -function onFilterAdded(filter) -{ - FilterListener.setDirty(1); - - if (!filter.disabled) - addFilter(filter); -} - -function onFilterRemoved(filter) -{ - FilterListener.setDirty(1); - - if (!filter.disabled) - removeFilter(filter); -} - -function onFilterDisabled(filter, newValue) -{ - FilterListener.setDirty(1); - - if (newValue == false) - addFilter(filter); - else - removeFilter(filter); -} - -function onGenericChange() -{ - FilterListener.setDirty(1); -} - -function onLoad() -{ - isDirty = 0; - - defaultMatcher.clear(); - ElemHide.clear(); - ElemHideEmulation.clear(); - for (let subscription of FilterStorage.subscriptions) - { - if (!subscription.disabled) - addFilters(subscription.filters); - } -} - -function onSave() -{ - isDirty = 0; -} diff --git a/data/extensions/spyblock@gnu.org/lib/filterNotifier.js b/data/extensions/spyblock@gnu.org/lib/filterNotifier.js deleted file mode 100644 index 66697cc..0000000 --- a/data/extensions/spyblock@gnu.org/lib/filterNotifier.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview This component manages listeners and calls them to distributes - * messages about filter changes. - */ - -const {EventEmitter} = require("events"); -const {desc} = require("coreUtils"); - -const CATCH_ALL = "__all"; - -/** - * @callback FilterNotifierCatchAllListener - * @param {string} action - * @param {Subscription|Filter} item - * @param {...*} additionalInfo - */ - -/** - * This class allows registering and triggering listeners for filter events. - * @class - */ -exports.FilterNotifier = Object.create(new EventEmitter(), desc({ - /** - * Adds a listener - * - * @deprecated use FilterNotifier.on(action, callback) - * @param {FilterNotifierCatchAllListener} listener - */ - addListener(listener) - { - let listeners = this._listeners[CATCH_ALL]; - if (!listeners || listeners.indexOf(listener) == -1) - this.on(CATCH_ALL, listener); - }, - - /** - * Removes a listener that was previosly added via addListener - * - * @deprecated use FilterNotifier.off(action, callback) - * @param {FilterNotifierCatchAllListener} listener - */ - removeListener(listener) - { - this.off(CATCH_ALL, listener); - }, - - /** - * Notifies listeners about an event - * @param {string} action event code ("load", "save", "elemhideupdate", - * "subscription.added", "subscription.removed", - * "subscription.disabled", "subscription.title", - * "subscription.lastDownload", "subscription.downloadStatus", - * "subscription.homepage", "subscription.updated", - * "filter.added", "filter.removed", "filter.moved", - * "filter.disabled", "filter.hitCount", "filter.lastHit") - * @param {Subscription|Filter} item item that the change applies to - * @param {*} param1 - * @param {*} param2 - * @param {*} param3 - * @deprecated use FilterNotifier.emit(action) - */ - triggerListeners(action, item, param1, param2, param3) - { - this.emit(action, item, param1, param2, param3); - this.emit(CATCH_ALL, action, item, param1, param2, param3); - } -})); diff --git a/data/extensions/spyblock@gnu.org/lib/filterStorage.js b/data/extensions/spyblock@gnu.org/lib/filterStorage.js deleted file mode 100644 index 5bbf76c..0000000 --- a/data/extensions/spyblock@gnu.org/lib/filterStorage.js +++ /dev/null @@ -1,786 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview FilterStorage class responsible for managing user's - * subscriptions and filters. - */ - -const {IO} = require("io"); -const {Prefs} = require("prefs"); -const {Filter, ActiveFilter} = require("filterClasses"); -const {Subscription, SpecialSubscription, - ExternalSubscription} = require("subscriptionClasses"); -const {FilterNotifier} = require("filterNotifier"); - -/** - * Version number of the filter storage file format. - * @type {number} - */ -let formatVersion = 5; - -/** - * This class reads user's filters from disk, manages them in memory - * and writes them back. - * @class - */ -let FilterStorage = exports.FilterStorage = -{ - /** - * Will be set to true after the initial loadFromDisk() call completes. - * @type {boolean} - */ - initialized: false, - - /** - * Version number of the patterns.ini format used. - * @type {number} - */ - get formatVersion() - { - return formatVersion; - }, - - /** - * File containing the filter list - * @type {string} - */ - get sourceFile() - { - return "patterns.ini"; - }, - - /** - * 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. - * @param {Filter} filter - * @return {?SpecialSubscription} - */ - getGroupForFilter(filter) - { - 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 - */ - addSubscription(subscription) - { - if (subscription.url in FilterStorage.knownSubscriptions) - return; - - FilterStorage.subscriptions.push(subscription); - FilterStorage.knownSubscriptions[subscription.url] = subscription; - addSubscriptionFilters(subscription); - - FilterNotifier.triggerListeners("subscription.added", subscription); - }, - - /** - * Removes a filter subscription from the list - * @param {Subscription} subscription filter subscription to be removed - */ - removeSubscription(subscription) - { - 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]; - 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(subscription, insertBefore) - { - let currentPos = FilterStorage.subscriptions.indexOf(subscription); - if (currentPos < 0) - return; - - let newPos = -1; - if (insertBefore) - newPos = FilterStorage.subscriptions.indexOf(insertBefore); - - 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(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 {number} [position] - * position within the subscription at which the filter should be added - */ - addFilter(filter, subscription, position) - { - 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); - 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 {number} [position] position inside the filter group at which the - * filter should be removed (if ommited all instances will be removed) - */ - removeFilter(filter, subscription, position) - { - let subscriptions = ( - subscription ? [subscription] : filter.subscriptions.slice() - ); - for (let i = 0; i < subscriptions.length; i++) - { - let currentSubscription = subscriptions[i]; - if (currentSubscription instanceof SpecialSubscription) - { - let positions = []; - if (typeof position == "undefined") - { - let index = -1; - do - { - index = currentSubscription.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 currentPosition = positions[j]; - if (currentSubscription.filters[currentPosition] == filter) - { - currentSubscription.filters.splice(currentPosition, 1); - if (currentSubscription.filters.indexOf(filter) < 0) - { - let index = filter.subscriptions.indexOf(currentSubscription); - if (index >= 0) - filter.subscriptions.splice(index, 1); - } - FilterNotifier.triggerListeners( - "filter.removed", filter, currentSubscription, currentPosition - ); - } - } - } - } - }, - - /** - * Moves a user-defined filter to a new position - * @param {Filter} filter - * @param {SpecialSubscription} subscription filter group where the filter is - * located - * @param {number} oldPosition current position of the filter - * @param {number} newPosition new position of the filter - */ - moveFilter(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(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(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; - } - }, - - /** - * @callback TextSink - * @param {string?} line - */ - - /** - * Allows importing previously serialized filter data. - * @param {boolean} silent - * If true, no "load" notification will be sent out. - * @return {TextSink} - * Function to be called for each line of data. Calling it with null as - * parameter finalizes the import and replaces existing data. No changes - * will be applied before finalization, so import can be "aborted" by - * forgetting this callback. - */ - importData(silent) - { - let parser = new INIParser(); - return line => - { - parser.process(line); - if (line === null) - { - let knownSubscriptions = Object.create(null); - for (let subscription of parser.subscriptions) - knownSubscriptions[subscription.url] = subscription; - - this.fileProperties = parser.fileProperties; - this.subscriptions = parser.subscriptions; - this.knownSubscriptions = knownSubscriptions; - Filter.knownFilters = parser.knownFilters; - Subscription.knownSubscriptions = parser.knownSubscriptions; - - if (!silent) - FilterNotifier.triggerListeners("load"); - } - }; - }, - - /** - * Loads all subscriptions from the disk. - * @return {Promise} promise resolved or rejected when loading is complete - */ - loadFromDisk() - { - let tryBackup = backupIndex => - { - return this.restoreBackup(backupIndex, true).then(() => - { - if (this.subscriptions.length == 0) - return tryBackup(backupIndex + 1); - }).catch(error => - { - // Give up - }); - }; - - return IO.statFile(this.sourceFile).then(statData => - { - if (!statData.exists) - { - this.firstRun = true; - return; - } - - let parser = this.importData(true); - return IO.readFromFile(this.sourceFile, parser).then(() => - { - parser(null); - if (this.subscriptions.length == 0) - { - // No filter subscriptions in the file, this isn't right. - throw new Error("No data in the file"); - } - }); - }).catch(error => - { - Cu.reportError(error); - return tryBackup(1); - }).then(() => - { - this.initialized = true; - FilterNotifier.triggerListeners("load"); - }); - }, - - /** - * Constructs the file name for a patterns.ini backup. - * @param {number} backupIndex - * number of the backup file (1 being the most recent) - * @return {string} backup file name - */ - getBackupName(backupIndex) - { - let [name, extension] = this.sourceFile.split(".", 2); - return (name + "-backup" + backupIndex + "." + extension); - }, - - /** - * Restores an automatically created backup. - * @param {number} backupIndex - * number of the backup to restore (1 being the most recent) - * @param {boolean} silent - * If true, no "load" notification will be sent out. - * @return {Promise} promise resolved or rejected when restoring is complete - */ - restoreBackup(backupIndex, silent) - { - let backupFile = this.getBackupName(backupIndex); - let parser = this.importData(silent); - return IO.readFromFile(backupFile, parser).then(() => - { - parser(null); - return this.saveToDisk(); - }); - }, - - /** - * Generator serializing filter data and yielding it line by line. - */ - *exportData() - { - // Do not persist external subscriptions - let subscriptions = this.subscriptions.filter( - s => !(s instanceof ExternalSubscription) - ); - - yield "# Adblock Plus preferences"; - yield "version=" + formatVersion; - - let saved = new Set(); - let buf = []; - - // Save subscriptions - for (let subscription of subscriptions) - { - yield ""; - - subscription.serialize(buf); - if (subscription.filters.length) - { - buf.push("", "[Subscription filters]"); - subscription.serializeFilters(buf); - } - for (let line of buf) - yield line; - buf.splice(0); - } - - // Save filter data - for (let subscription of subscriptions) - { - for (let filter of subscription.filters) - { - if (!saved.has(filter.text)) - { - filter.serialize(buf); - saved.add(filter.text); - for (let line of buf) - yield line; - 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 - * @return {Promise} promise resolved or rejected when saving is complete - */ - saveToDisk() - { - if (this._saving) - { - this._needsSave = true; - return; - } - - this._saving = true; - - return Promise.resolve().then(() => - { - // First check whether we need to create a backup - if (Prefs.patternsbackups <= 0) - return false; - - return IO.statFile(this.sourceFile).then(statData => - { - if (!statData.exists) - return false; - - return IO.statFile(this.getBackupName(1)).then(backupStatData => - { - if (backupStatData.exists && - (Date.now() - backupStatData.lastModified) / 3600000 < - Prefs.patternsbackupinterval) - { - return false; - } - return true; - }); - }); - }).then(backupRequired => - { - if (!backupRequired) - return; - - let ignoreErrors = error => - { - // Expected error, backup file doesn't exist. - }; - - let renameBackup = index => - { - if (index > 0) - { - return IO.renameFile(this.getBackupName(index), - this.getBackupName(index + 1)) - .catch(ignoreErrors) - .then(() => renameBackup(index - 1)); - } - - return IO.renameFile(this.sourceFile, this.getBackupName(1)) - .catch(ignoreErrors); - }; - - // Rename existing files - return renameBackup(Prefs.patternsbackups - 1); - }).catch(error => - { - // Errors during backup creation shouldn't prevent writing filters. - Cu.reportError(error); - }).then(() => - { - return IO.writeToFile(this.sourceFile, this.exportData()); - }).then(() => - { - FilterNotifier.triggerListeners("save"); - }).catch(error => - { - // If saving failed, report error but continue - we still have to process - // flags. - Cu.reportError(error); - }).then(() => - { - this._saving = false; - if (this._needsSave) - { - this._needsSave = false; - this.saveToDisk(); - } - }); - }, - - /** - * @typedef FileInfo - * @type {object} - * @property {nsIFile} file - * @property {number} lastModified - */ - - /** - * Returns a promise resolving in a list of existing backup files. - * @return {Promise.<FileInfo[]>} - */ - getBackupFiles() - { - let backups = []; - - let checkBackupFile = index => - { - return IO.statFile(this.getBackupName(index)).then(statData => - { - if (!statData.exists) - return backups; - - backups.push({ - index, - lastModified: statData.lastModified - }); - return checkBackupFile(index + 1); - }).catch(error => - { - // Something went wrong, return whatever data we got so far. - Cu.reportError(error); - return backups; - }); - }; - - return checkBackupFile(1); - } -}; - -/** - * 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); - } -} - -/** - * Listener returned by FilterStorage.importData(), parses 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, - - process(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": - 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": - 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; - } - } - - if (val === null) - return; - - this.curSection = match[1].toLowerCase(); - switch (this.curSection) - { - case "filter": - case "subscription": - this.wantObj = true; - this.curObj = {}; - break; - case "subscription filters": - 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; - } - } -}; diff --git a/data/extensions/spyblock@gnu.org/lib/io.js b/data/extensions/spyblock@gnu.org/lib/io.js deleted file mode 100644 index 0a22513..0000000 --- a/data/extensions/spyblock@gnu.org/lib/io.js +++ /dev/null @@ -1,279 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -let {IO: LegacyIO} = require("legacyIO"); -let {Utils} = require("utils"); - -let webextension = require("webextension"); -let messageID = 0; -let messageCallbacks = new Map(); - -webextension.then(port => -{ - port.onMessage.addListener(message => - { - let {id} = message; - let callbacks = messageCallbacks.get(id); - if (callbacks) - { - messageCallbacks.delete(id); - - if (message.success) - callbacks.resolve(message.result); - else - callbacks.reject(message.result); - } - }); -}); - -function callWebExt(method, ...args) -{ - return webextension.then(port => - { - return new Promise((resolve, reject) => - { - let id = ++messageID; - messageCallbacks.set(id, {resolve, reject}); - port.postMessage({id, method, args}); - }); - }); -} - -function callLegacy(method, ...args) -{ - return new Promise((resolve, reject) => - { - LegacyIO[method](...args, (error, result) => - { - if (error) - reject(error); - else - resolve(result); - }); - }); -} - -function legacyFile(fileName) -{ - let file = LegacyIO.resolveFilePath("adblockplus"); - file.append(fileName); - return file; -} - -function ensureDirExists(file) -{ - if (!file.exists()) - { - ensureDirExists(file.parent); - file.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); - } -} - -let fallback = { - readFromFile(fileName, listener) - { - let wrapper = { - process(line) - { - if (line !== null) - listener(line); - } - }; - return callLegacy("readFromFile", legacyFile(fileName), wrapper); - }, - - writeToFile(fileName, data) - { - let file = legacyFile(fileName); - ensureDirExists(file.parent); - return callLegacy("writeToFile", file, data); - }, - - copyFile(fromFile, toFile) - { - return callLegacy("copyFile", legacyFile(fromFile), legacyFile(toFile)); - }, - - renameFile(fromFile, newName) - { - return callLegacy("renameFile", legacyFile(fromFile), newName); - }, - - removeFile(fileName) - { - return callLegacy("removeFile", legacyFile(fileName)); - }, - - statFile(fileName) - { - return callLegacy("statFile", legacyFile(fileName)); - } -}; - -exports.IO = -{ - /** - * @callback TextSink - * @param {string} line - */ - - /** - * Reads text lines from a file. - * @param {string} fileName - * Name of the file to be read - * @param {TextSink} listener - * Function that will be called for each line in the file - * @return {Promise} - * Promise to be resolved or rejected once the operation is completed - */ - readFromFile(fileName, listener) - { - return callWebExt("readFromFile", fileName).then(contents => - { - return new Promise((resolve, reject) => - { - let lineIndex = 0; - - function processBatch() - { - while (lineIndex < contents.length) - { - listener(contents[lineIndex++]); - if (lineIndex % 1000 == 0) - { - Utils.runAsync(processBatch); - return; - } - } - resolve(); - } - - processBatch(); - }); - }); - }, - - /** - * Writes text lines to a file. - * @param {string} fileName - * Name of the file to be written - * @param {Iterable.<string>} data - * An array-like or iterable object containing the lines (without line - * endings) - * @return {Promise} - * Promise to be resolved or rejected once the operation is completed - */ - writeToFile(fileName, data) - { - return callWebExt("writeToFile", fileName, Array.from(data)); - }, - - /** - * Copies a file. - * @param {string} fromFile - * Name of the file to be copied - * @param {string} toFile - * Name of the file to be written, will be overwritten if exists - * @return {Promise} - * Promise to be resolved or rejected once the operation is completed - */ - copyFile(fromFile, toFile) - { - return callWebExt("copyFile", fromFile, toFile); - }, - - /** - * Renames a file. - * @param {string} fromFile - * Name of the file to be renamed - * @param {string} newName - * New file name, will be overwritten if exists - * @return {Promise} - * Promise to be resolved or rejected once the operation is completed - */ - renameFile(fromFile, newName) - { - return callWebExt("renameFile", fromFile, newName); - }, - - /** - * Removes a file. - * @param {string} fileName - * Name of the file to be removed - * @return {Promise} - * Promise to be resolved or rejected once the operation is completed - */ - removeFile(fileName) - { - return callWebExt("removeFile", fileName); - }, - - /** - * @typedef StatData - * @type {object} - * @property {boolean} exists - * true if the file exists - * @property {number} lastModified - * file modification time in milliseconds - */ - - /** - * Retrieves file metadata. - * @param {string} fileName - * Name of the file to be looked up - * @return {Promise.<StatData>} - * Promise to be resolved with file metadata once the operation is - * completed - */ - statFile(fileName) - { - return callWebExt("statFile", fileName); - } -}; - -let {application} = require("info"); -if (application != "firefox" && application != "fennec2") -{ - // Currently, only Firefox has a working WebExtensions implementation, other - // applications should just use the fallback. - exports.IO = fallback; -} -else -{ - // Add fallbacks to IO methods - fall back to legacy I/O if file wasn't found. - for (let name of Object.getOwnPropertyNames(exports.IO)) - { - // No fallback for writeToFile method, new data should always be stored to - // new storage only. - if (name == "writeToFile") - continue; - - let method = exports.IO[name]; - let fallbackMethod = fallback[name]; - exports.IO[name] = (...args) => - { - return method(...args).catch(error => - { - if (error == "NoSuchFile") - return fallbackMethod(...args); - throw error; - }); - }; - } -} diff --git a/data/extensions/spyblock@gnu.org/lib/keySelector.js b/data/extensions/spyblock@gnu.org/lib/keySelector.js deleted file mode 100644 index 151f50d..0000000 --- a/data/extensions/spyblock@gnu.org/lib/keySelector.js +++ /dev/null @@ -1,227 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Cu.import("resource://gre/modules/Services.jsm"); - -let validModifiers = Object.create(null); -validModifiers.ACCEL = null; -validModifiers.CTRL = "control"; -validModifiers.CONTROL = "control"; -validModifiers.SHIFT = "shift"; -validModifiers.ALT = "alt"; -validModifiers.META = "meta"; - -let bindingsKeys = null; -(function() -{ - let request = new XMLHttpRequest(); - request.open("GET", "chrome://global/content/platformHTMLBindings.xml"); - request.addEventListener("load", () => - { - bindingsKeys = request.responseXML.getElementsByTagName("handler"); - }); - request.send(); -})(); - - -/** - * Sets the correct value of validModifiers.ACCEL. - */ -function initAccelKey() -{ - validModifiers.ACCEL = "control"; - try - { - let accelKey = Services.prefs.getIntPref("ui.key.accelKey"); - if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_CONTROL) - validModifiers.ACCEL = "control"; - else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_ALT) - validModifiers.ACCEL = "alt"; - else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_META) - validModifiers.ACCEL = "meta"; - } - catch(e) - { - Cu.reportError(e); - } -} - -exports.KeySelector = KeySelector; - -/** - * This class provides capabilities to find and use available keyboard shortcut - * keys. - * @param {ChromeWindow} window the window where to look up existing shortcut - * keys - * @constructor - */ -function KeySelector(window) -{ - this._initExistingShortcuts(window); -} -KeySelector.prototype = -{ - /** - * Map listing existing shortcut keys as its keys. - * @type Object - */ - _existingShortcuts: null, - - /** - * Sets up _existingShortcuts property for a window. - */ - _initExistingShortcuts: function(/**ChromeWindow*/ window) - { - if (!validModifiers.ACCEL) - initAccelKey(); - - this._existingShortcuts = Object.create(null); - - let keys = Array.prototype.slice.apply(window.document.getElementsByTagName("key")); - if (bindingsKeys) - keys.push.apply(keys, bindingsKeys); - for (let i = 0; i < keys.length; i++) - { - let key = keys[i]; - let keyData = - { - shift: false, - meta: false, - alt: false, - control: false, - char: null, - code: null - }; - - let keyChar = key.getAttribute("key"); - if (keyChar && keyChar.length == 1) - keyData.char = keyChar.toUpperCase(); - - let keyCode = key.getAttribute("keycode"); - if (keyCode && "DOM_" + keyCode.toUpperCase() in Ci.nsIDOMKeyEvent) - keyData.code = Ci.nsIDOMKeyEvent["DOM_" + keyCode.toUpperCase()]; - - if (!keyData.char && !keyData.code) - continue; - - let keyModifiers = key.getAttribute("modifiers"); - if (keyModifiers) - for (let modifier of keyModifiers.toUpperCase().match(/\w+/g)) - if (modifier in validModifiers) - keyData[validModifiers[modifier]] = true; - - let canonical = [keyData.shift, keyData.meta, keyData.alt, keyData.control, keyData.char || keyData.code].join(" "); - this._existingShortcuts[canonical] = true; - } - }, - - /** - * Selects a keyboard shortcut variant that isn't already taken, - * parses it into an object. - */ - selectKey: function(/**String*/ variants) /**Object*/ - { - for (let variant of variants.split(/\s*,\s*/)) - { - if (!variant) - continue; - - let keyData = - { - shift: false, - meta: false, - alt: false, - control: false, - char: null, - code: null, - codeName: null - }; - for (let part of variant.toUpperCase().split(/\s+/)) - { - if (part in validModifiers) - keyData[validModifiers[part]] = true; - else if (part.length == 1) - keyData.char = part; - else if ("DOM_VK_" + part in Ci.nsIDOMKeyEvent) - { - keyData.code = Ci.nsIDOMKeyEvent["DOM_VK_" + part]; - keyData.codeName = "VK_" + part; - } - } - - if (!keyData.char && !keyData.code) - continue; - - let canonical = [keyData.shift, keyData.meta, keyData.alt, keyData.control, keyData.char || keyData.code].join(" "); - if (canonical in this._existingShortcuts) - continue; - - return keyData; - } - - return null; - } -}; - -/** - * Creates the text representation for a key. - * @static - */ -KeySelector.getTextForKey = function (/**Object*/ key) /**String*/ -{ - if (!key) - return null; - - if (!("text" in key)) - { - key.text = null; - try - { - let stringBundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties"); - let parts = []; - if (key.control) - parts.push(stringBundle.GetStringFromName("VK_CONTROL")); - if (key.alt) - parts.push(stringBundle.GetStringFromName("VK_ALT")); - if (key.meta) - parts.push(stringBundle.GetStringFromName("VK_META")); - if (key.shift) - parts.push(stringBundle.GetStringFromName("VK_SHIFT")); - if (key.char) - parts.push(key.char.toUpperCase()); - else - { - let stringBundle2 = Services.strings.createBundle("chrome://global/locale/keys.properties"); - parts.push(stringBundle2.GetStringFromName(key.codeName)); - } - key.text = parts.join(stringBundle.GetStringFromName("MODIFIER_SEPARATOR")); - } - catch (e) - { - Cu.reportError(e); - return null; - } - } - return key.text; -}; - -/** - * Tests whether a keypress event matches the given key. - * @static - */ -KeySelector.matchesKey = function(/**Event*/ event, /**Object*/ key) /**Boolean*/ -{ - if (event.defaultPrevented || !key) - return false; - if (key.shift != event.shiftKey || key.alt != event.altKey) - return false; - if (key.meta != event.metaKey || key.control != event.ctrlKey) - return false; - - if (key.char && event.charCode && String.fromCharCode(event.charCode).toUpperCase() == key.char) - return true; - if (key.code && event.keyCode && event.keyCode == key.code) - return true; - return false; -}; diff --git a/data/extensions/spyblock@gnu.org/lib/legacyIO.js b/data/extensions/spyblock@gnu.org/lib/legacyIO.js deleted file mode 100644 index 5549d96..0000000 --- a/data/extensions/spyblock@gnu.org/lib/legacyIO.js +++ /dev/null @@ -1,335 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Module containing file I/O helpers. - */ - -let {Services} = Cu.import("resource://gre/modules/Services.jsm", null); -let {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", null); -let {OS} = Cu.import("resource://gre/modules/osfile.jsm", null); -let {Task} = Cu.import("resource://gre/modules/Task.jsm", null); - -let {Prefs} = require("prefs"); -let {Utils} = require("utils"); - -let firstRead = true; -const BUFFER_SIZE = 0x80000; // 512kB - -let IO = exports.IO = -{ - /** - * Retrieves the platform-dependent line break string. - */ - get lineBreak() - { - let lineBreak = (Services.appinfo.OS == "WINNT" ? "\r\n" : "\n"); - Object.defineProperty(this, "lineBreak", {value: lineBreak}); - return lineBreak; - }, - - /** - * Tries to interpret a file path as an absolute path or a path relative to - * user's profile. Returns a file or null on failure. - */ - resolveFilePath: function(/**String*/ path) /**nsIFile*/ - { - if (!path) - return null; - - try { - // Assume an absolute path first - return new FileUtils.File(path); - } catch (e) {} - - try { - // Try relative path now - return FileUtils.getFile("ProfD", path.split("/")); - } catch (e) {} - - return null; - }, - - /** - * Reads strings from a file asynchronously, calls listener.process() with - * each line read and with a null parameter once the read operation is done. - * The callback will be called when the operation is done. - */ - readFromFile: function(/**nsIFile*/ file, /**Object*/ listener, /**Function*/ callback) - { - try - { - let processing = false; - let buffer = ""; - let loaded = false; - let error = null; - - let onProgress = function*(data) - { - let index = (processing ? -1 : Math.max(data.lastIndexOf("\n"), data.lastIndexOf("\r"))); - if (index >= 0) - { - // Protect against reentrance in case the listener processes events. - processing = true; - try - { - let oldBuffer = buffer; - buffer = data.substr(index + 1); - data = data.substr(0, index + 1); - let lines = data.split(/[\r\n]+/); - lines.pop(); - lines[0] = oldBuffer + lines[0]; - for (let i = 0; i < lines.length; i++) - listener.process(lines[i]); - } - finally - { - processing = false; - data = buffer; - buffer = ""; - yield* onProgress(data); - - if (loaded) - { - loaded = false; - onSuccess(); - } - - if (error) - { - let param = error; - error = null; - onError(param); - } - } - } - else - buffer += data; - }; - - let onSuccess = function() - { - if (processing) - { - // Still processing data, delay processing this event. - loaded = true; - return; - } - - // We are ignoring return value of listener.process() here because - // turning this callback into a generator would be complicated, and - // delaying isn't really necessary for the last two calls. - if (buffer !== "") - listener.process(buffer); - listener.process(null); - - callback(null); - }; - - let onError = function(e) - { - if (processing) - { - // Still processing data, delay processing this event. - error = e; - return; - } - - callback(e); - }; - - let decoder = new TextDecoder(); - Task.spawn(function*() - { - if (firstRead && Services.vc.compare(Utils.platformVersion, "23.0a1") <= 0) - { - // See https://issues.adblockplus.org/ticket/530 - the first file - // opened cannot be closed due to Gecko bug 858723. Make sure that - // our patterns.ini file doesn't stay locked by opening a dummy file - // first. - try - { - let dummyPath = IO.resolveFilePath(Prefs.data_directory + "/dummy").path; - let dummy = yield OS.File.open(dummyPath, {write: true, truncate: true}); - yield dummy.close(); - } - catch (e) - { - // Dummy might be locked already, we don't care - } - } - firstRead = false; - - let f = yield OS.File.open(file.path, {read: true}); - while (true) - { - let array = yield f.read(BUFFER_SIZE); - if (!array.length) - break; - - let data = decoder.decode(array, {stream: true}); - yield* onProgress(data); - } - yield f.close(); - }.bind(this)).then(onSuccess, onError); - } - catch (e) - { - callback(e); - } - }, - - /** - * Writes string data to a file in UTF-8 format asynchronously. The callback - * will be called when the write operation is done. - */ - writeToFile: function(/**nsIFile*/ file, /**Iterator*/ data, /**Function*/ callback) - { - try - { - let encoder = new TextEncoder(); - - Task.spawn(function*() - { - // This mimics OS.File.writeAtomic() but writes in chunks. - let tmpPath = file.path + ".tmp"; - let f = yield OS.File.open(tmpPath, {write: true, truncate: true}); - - let buf = []; - let bufLen = 0; - let lineBreak = this.lineBreak; - - function writeChunk() - { - let array = encoder.encode(buf.join(lineBreak) + lineBreak); - buf = []; - bufLen = 0; - return f.write(array); - } - - for (let line of data) - { - buf.push(line); - bufLen += line.length; - if (bufLen >= BUFFER_SIZE) - yield writeChunk(); - } - - if (bufLen) - yield writeChunk(); - - // OS.File.flush() isn't exposed prior to Gecko 27, see bug 912457. - if (typeof f.flush == "function") - yield f.flush(); - yield f.close(); - yield OS.File.move(tmpPath, file.path, {noCopy: true}); - }.bind(this)).then(callback.bind(null, null), callback); - } - catch (e) - { - callback(e); - } - }, - - /** - * Copies a file asynchronously. The callback will be called when the copy - * operation is done. - */ - copyFile: function(/**nsIFile*/ fromFile, /**nsIFile*/ toFile, /**Function*/ callback) - { - try - { - let promise = OS.File.copy(fromFile.path, toFile.path); - promise.then(callback.bind(null, null), callback); - } - catch (e) - { - callback(e); - } - }, - - /** - * Renames a file within the same directory, will call callback when done. - */ - renameFile: function(/**nsIFile*/ fromFile, /**String*/ newName, /**Function*/ callback) - { - try - { - let toFile = fromFile.clone(); - toFile.leafName = newName; - let promise = OS.File.move(fromFile.path, toFile.path); - promise.then(callback.bind(null, null), callback); - } - catch(e) - { - callback(e); - } - }, - - /** - * Removes a file, will call callback when done. - */ - removeFile: function(/**nsIFile*/ file, /**Function*/ callback) - { - try - { - let promise = OS.File.remove(file.path); - promise.then(callback.bind(null, null), callback); - } - catch(e) - { - callback(e); - } - }, - - /** - * Gets file information such as whether the file exists. - */ - statFile: function(/**nsIFile*/ file, /**Function*/ callback) - { - try - { - let promise = OS.File.stat(file.path); - promise.then(function onSuccess(info) - { - callback(null, { - exists: true, - isDirectory: info.isDir, - isFile: !info.isDir, - lastModified: info.lastModificationDate.getTime() - }); - }, function onError(e) - { - if (e.becauseNoSuchFile) - { - callback(null, { - exists: false, - isDirectory: false, - isFile: false, - lastModified: 0 - }); - } - else - callback(e); - }); - } - catch(e) - { - callback(e); - } - } -} diff --git a/data/extensions/spyblock@gnu.org/lib/main.js b/data/extensions/spyblock@gnu.org/lib/main.js deleted file mode 100644 index c0d4733..0000000 --- a/data/extensions/spyblock@gnu.org/lib/main.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Starts up Adblock Plus - */ - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -bootstrapChildProcesses(); -registerPublicAPI(); -require("filterListener"); -require("contentPolicy"); -require("synchronizer"); -require("notification"); -require("sync"); -require("messageResponder"); -require("ui"); -require("objectTabs"); -require("elemHideFF"); -require("elemHideEmulation"); - -function bootstrapChildProcesses() -{ - let info = require("info"); - - let processScript = info.addonRoot + "lib/child/bootstrap.js?" + - Math.random() + "&info=" + encodeURIComponent(JSON.stringify(info)); - let messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"] - .getService(Ci.nsIProcessScriptLoader) - .QueryInterface(Ci.nsIMessageBroadcaster); - messageManager.loadProcessScript(processScript, true); - - onShutdown.add(() => { - messageManager.broadcastAsyncMessage("AdblockPlus:Shutdown", processScript); - messageManager.removeDelayedProcessScript(processScript); - }); -} - -function registerPublicAPI() -{ - let {addonRoot} = require("info"); - - let uri = Services.io.newURI(addonRoot + "lib/Public.jsm", null, null); - if (uri instanceof Ci.nsIMutable) - uri.mutable = false; - - let classID = Components.ID("5e447bce-1dd2-11b2-b151-ec21c2b6a135"); - let contractID = "@adblockplus.org/abp/public;1"; - let factory = - { - createInstance: function(outer, iid) - { - if (outer) - throw Cr.NS_ERROR_NO_AGGREGATION; - return uri.QueryInterface(iid); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) - }; - - let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - registrar.registerFactory(classID, "Adblock Plus public API URL", contractID, factory); - - onShutdown.add(function() - { - registrar.unregisterFactory(classID, factory); - Cu.unload(uri.spec); - }); -} diff --git a/data/extensions/spyblock@gnu.org/lib/matcher.js b/data/extensions/spyblock@gnu.org/lib/matcher.js deleted file mode 100644 index 02573bd..0000000 --- a/data/extensions/spyblock@gnu.org/lib/matcher.js +++ /dev/null @@ -1,458 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Matcher class implementing matching addresses against - * a list of filters. - */ - -const {Filter, WhitelistFilter} = require("filterClasses"); - -/** - * Blacklist/whitelist filter matching - * @constructor - */ -function Matcher() -{ - this.clear(); -} -exports.Matcher = Matcher; - -Matcher.prototype = { - /** - * Lookup table for filters by their associated keyword - * @type {Object} - */ - filterByKeyword: null, - - /** - * Lookup table for keywords by the filter text - * @type {Object} - */ - keywordByFilter: null, - - /** - * Removes all known filters - */ - clear() - { - this.filterByKeyword = Object.create(null); - this.keywordByFilter = Object.create(null); - }, - - /** - * Adds a filter to the matcher - * @param {RegExpFilter} filter - */ - add(filter) - { - if (filter.text in this.keywordByFilter) - return; - - // Look for a suitable keyword - let keyword = this.findKeyword(filter); - let oldEntry = this.filterByKeyword[keyword]; - if (typeof oldEntry == "undefined") - this.filterByKeyword[keyword] = filter; - else if (oldEntry.length == 1) - this.filterByKeyword[keyword] = [oldEntry, filter]; - else - oldEntry.push(filter); - this.keywordByFilter[filter.text] = keyword; - }, - - /** - * Removes a filter from the matcher - * @param {RegExpFilter} filter - */ - remove(filter) - { - if (!(filter.text in this.keywordByFilter)) - return; - - let keyword = this.keywordByFilter[filter.text]; - let list = this.filterByKeyword[keyword]; - if (list.length <= 1) - delete this.filterByKeyword[keyword]; - else - { - let index = list.indexOf(filter); - if (index >= 0) - { - list.splice(index, 1); - if (list.length == 1) - this.filterByKeyword[keyword] = list[0]; - } - } - - delete this.keywordByFilter[filter.text]; - }, - - /** - * Chooses a keyword to be associated with the filter - * @param {Filter} filter - * @return {string} keyword or an empty string if no keyword could be found - */ - findKeyword(filter) - { - let result = ""; - let {text} = filter; - if (Filter.regexpRegExp.test(text)) - return result; - - // Remove options - let match = Filter.optionsRegExp.exec(text); - if (match) - text = match.input.substr(0, match.index); - - // Remove whitelist marker - if (text.substr(0, 2) == "@@") - text = text.substr(2); - - let candidates = text.toLowerCase().match( - /[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g - ); - if (!candidates) - return result; - - let hash = this.filterByKeyword; - let resultCount = 0xFFFFFF; - let resultLength = 0; - for (let i = 0, l = candidates.length; i < l; i++) - { - let candidate = candidates[i].substr(1); - let count = (candidate in hash ? hash[candidate].length : 0); - if (count < resultCount || - (count == resultCount && candidate.length > resultLength)) - { - result = candidate; - resultCount = count; - resultLength = candidate.length; - } - } - return result; - }, - - /** - * Checks whether a particular filter is being matched against. - * @param {RegExpFilter} filter - * @return {boolean} - */ - hasFilter(filter) - { - return (filter.text in this.keywordByFilter); - }, - - /** - * Returns the keyword used for a filter, null for unknown filters. - * @param {RegExpFilter} filter - * @return {string} - */ - getKeywordForFilter(filter) - { - if (filter.text in this.keywordByFilter) - return this.keywordByFilter[filter.text]; - return null; - }, - - /** - * Checks whether the entries for a particular keyword match a URL - * @param {string} keyword - * @param {string} location - * @param {number} typeMask - * @param {string} docDomain - * @param {boolean} thirdParty - * @param {string} sitekey - * @param {boolean} specificOnly - * @return {?Filter} - */ - _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey, - specificOnly, privatenode) - { - let list = this.filterByKeyword[keyword]; - for (let i = 0; i < list.length; i++) - { - let filter = list[i]; - - if (specificOnly && filter.isGeneric() && - !(filter instanceof WhitelistFilter)) - continue; - - if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey, privatenode)) - return filter; - } - return null; - }, - - /** - * Tests whether the URL matches any of the known filters - * @param {string} location - * URL to be tested - * @param {number} typeMask - * bitmask of content / request types to match - * @param {string} docDomain - * domain name of the document that loads the URL - * @param {boolean} thirdParty - * should be true if the URL is a third-party request - * @param {string} sitekey - * public key provided by the document - * @param {boolean} specificOnly - * should be true if generic matches should be ignored - * @return {?RegExpFilter} - * matching filter or null - */ - matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly) - { - let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); - if (candidates === null) - candidates = []; - candidates.push(""); - for (let i = 0, l = candidates.length; i < l; i++) - { - let substr = candidates[i]; - if (substr in this.filterByKeyword) - { - let result = this._checkEntryMatch(substr, location, typeMask, - docDomain, thirdParty, sitekey, - specificOnly); - if (result) - return result; - } - } - - return null; - } -}; - -/** - * Combines a matcher for blocking and exception rules, automatically sorts - * rules into two Matcher instances. - * @constructor - * @augments Matcher - */ -function CombinedMatcher() -{ - this.blacklist = new Matcher(); - this.whitelist = new Matcher(); - this.resultCache = Object.create(null); -} -exports.CombinedMatcher = CombinedMatcher; - -/** - * Maximal number of matching cache entries to be kept - * @type {number} - */ -CombinedMatcher.maxCacheEntries = 1000; - -CombinedMatcher.prototype = -{ - /** - * Matcher for blocking rules. - * @type {Matcher} - */ - blacklist: null, - - /** - * Matcher for exception rules. - * @type {Matcher} - */ - whitelist: null, - - /** - * Lookup table of previous matchesAny results - * @type {Object} - */ - resultCache: null, - - /** - * Number of entries in resultCache - * @type {number} - */ - cacheEntries: 0, - - /** - * @see Matcher#clear - */ - clear() - { - this.blacklist.clear(); - this.whitelist.clear(); - this.resultCache = Object.create(null); - this.cacheEntries = 0; - }, - - /** - * @see Matcher#add - * @param {Filter} filter - */ - add(filter) - { - if (filter instanceof WhitelistFilter) - this.whitelist.add(filter); - else - this.blacklist.add(filter); - - if (this.cacheEntries > 0) - { - this.resultCache = Object.create(null); - this.cacheEntries = 0; - } - }, - - /** - * @see Matcher#remove - * @param {Filter} filter - */ - remove(filter) - { - if (filter instanceof WhitelistFilter) - this.whitelist.remove(filter); - else - this.blacklist.remove(filter); - - if (this.cacheEntries > 0) - { - this.resultCache = Object.create(null); - this.cacheEntries = 0; - } - }, - - /** - * @see Matcher#findKeyword - * @param {Filter} filter - * @return {string} keyword - */ - findKeyword(filter) - { - if (filter instanceof WhitelistFilter) - return this.whitelist.findKeyword(filter); - return this.blacklist.findKeyword(filter); - }, - - /** - * @see Matcher#hasFilter - * @param {Filter} filter - * @return {boolean} - */ - hasFilter(filter) - { - if (filter instanceof WhitelistFilter) - return this.whitelist.hasFilter(filter); - return this.blacklist.hasFilter(filter); - }, - - /** - * @see Matcher#getKeywordForFilter - * @param {Filter} filter - * @return {string} keyword - */ - getKeywordForFilter(filter) - { - if (filter instanceof WhitelistFilter) - return this.whitelist.getKeywordForFilter(filter); - return this.blacklist.getKeywordForFilter(filter); - }, - - /** - * Checks whether a particular filter is slow - * @param {RegExpFilter} filter - * @return {boolean} - */ - isSlowFilter(filter) - { - let matcher = ( - filter instanceof WhitelistFilter ? this.whitelist : this.blacklist - ); - if (matcher.hasFilter(filter)) - return !matcher.getKeywordForFilter(filter); - return !matcher.findKeyword(filter); - }, - - /** - * Optimized filter matching testing both whitelist and blacklist matchers - * simultaneously. For parameters see Matcher.matchesAny(). - * @see Matcher#matchesAny - * @inheritdoc - */ - matchesAnyInternal(location, typeMask, docDomain, thirdParty, sitekey, - specificOnly, privatenode) - { - let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); - if (candidates === null) - candidates = []; - candidates.push(""); - - let blacklistHit = null; - for (let i = 0, l = candidates.length; i < l; i++) - { - let substr = candidates[i]; - if (substr in this.whitelist.filterByKeyword) - { - let result = this.whitelist._checkEntryMatch( - substr, location, typeMask, docDomain, thirdParty, sitekey, privatenode - ); - if (result) - return result; - } - if (substr in this.blacklist.filterByKeyword && blacklistHit === null) - { - blacklistHit = this.blacklist._checkEntryMatch( - substr, location, typeMask, docDomain, thirdParty, sitekey, - specificOnly, privatenode - ); - } - } - return blacklistHit; - }, - - /** - * @see Matcher#matchesAny - * @inheritdoc - */ - matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly, privatenode) - { - let key = location + " " + typeMask + " " + docDomain + " " + thirdParty + - " " + sitekey + " " + specificOnly; - if (!privatenode) - if (key in this.resultCache) - return this.resultCache[key]; - - let result = this.matchesAnyInternal(location, typeMask, docDomain, - thirdParty, sitekey, specificOnly, privatenode); - - if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) - { - this.resultCache = Object.create(null); - this.cacheEntries = 0; - } - - if (!privatenode){ - this.resultCache[key] = result; - this.cacheEntries++; - } - - return result; - } -}; - -/** - * Shared CombinedMatcher instance that should usually be used. - * @type {CombinedMatcher} - */ -exports.defaultMatcher = new CombinedMatcher(); diff --git a/data/extensions/spyblock@gnu.org/lib/messageResponder.js b/data/extensions/spyblock@gnu.org/lib/messageResponder.js deleted file mode 100644 index 5dfc2e9..0000000 --- a/data/extensions/spyblock@gnu.org/lib/messageResponder.js +++ /dev/null @@ -1,431 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -/* globals require */ - -"use strict"; - -(function(global) -{ - let ext = global.ext || require("ext_background"); - - const {port} = require("messaging"); - const {Prefs} = require("prefs"); - const {Utils} = require("utils"); - const {FilterStorage} = require("filterStorage"); - const {FilterNotifier} = require("filterNotifier"); - const {defaultMatcher} = require("matcher"); - const {ElemHideEmulation} = require("elemHideEmulation"); - const {Notification: NotificationStorage} = require("notification"); - - const {Filter, BlockingFilter, RegExpFilter} = require("filterClasses"); - const {Synchronizer} = require("synchronizer"); - - const info = require("info"); - const {Subscription, - DownloadableSubscription, - SpecialSubscription} = require("subscriptionClasses"); - - // Some modules doesn't exist on Firefox. Moreover, - // require() throws an exception on Firefox in that case. - // However, try/catch causes the whole function to to be - // deoptimized on V8. So we wrap it into another function. - function tryRequire(module) - { - try - { - return require(module); - } - catch (e) - { - return null; - } - } - - function convertObject(keys, obj) - { - let result = {}; - for (let key of keys) - { - if (key in obj) - result[key] = obj[key]; - } - return result; - } - - function convertSubscription(subscription) - { - let obj = convertObject(["disabled", "downloadStatus", "homepage", - "lastDownload", "title", "url"], subscription); - obj.isDownloading = Synchronizer.isExecuting(subscription.url); - return obj; - } - - let convertFilter = convertObject.bind(null, ["text"]); - - let changeListeners = new ext.PageMap(); - let listenedPreferences = Object.create(null); - let listenedFilterChanges = Object.create(null); - let messageTypes = new Map([ - ["app", "app.respond"], - ["filter", "filters.respond"], - ["pref", "prefs.respond"], - ["subscription", "subscriptions.respond"] - ]); - - function sendMessage(type, action, ...args) - { - let pages = changeListeners.keys(); - if (pages.length == 0) - return; - - let convertedArgs = []; - for (let arg of args) - { - if (arg instanceof Subscription) - convertedArgs.push(convertSubscription(arg)); - else if (arg instanceof Filter) - convertedArgs.push(convertFilter(arg)); - else - convertedArgs.push(arg); - } - - for (let page of pages) - { - let filters = changeListeners.get(page); - let actions = filters[type]; - if (actions && actions.indexOf(action) != -1) - { - page.sendMessage({ - type: messageTypes.get(type), - action, - args: convertedArgs - }); - } - } - } - - function addFilterListeners(type, actions) - { - for (let action of actions) - { - let name; - if (type == "filter" && action == "loaded") - name = "load"; - else - name = type + "." + action; - - if (!(name in listenedFilterChanges)) - { - listenedFilterChanges[name] = null; - FilterNotifier.on(name, (...args) => - { - sendMessage(type, action, ...args); - }); - } - } - } - - function getListenerFilters(page) - { - let listenerFilters = changeListeners.get(page); - if (!listenerFilters) - { - listenerFilters = Object.create(null); - changeListeners.set(page, listenerFilters); - } - return listenerFilters; - } - - port.on("app.get", (message, sender) => - { - if (message.what == "issues") - { - let subscriptionInit = tryRequire("subscriptionInit"); - let result = subscriptionInit ? subscriptionInit.reinitialized : false; - return {filterlistsReinitialized: result}; - } - - if (message.what == "doclink") - return Utils.getDocLink(message.link); - - if (message.what == "localeInfo") - { - let bidiDir; - if ("chromeRegistry" in Utils) - { - let isRtl = Utils.chromeRegistry.isLocaleRTL("adblockplus"); - bidiDir = isRtl ? "rtl" : "ltr"; - } - else - bidiDir = ext.i18n.getMessage("@@bidi_dir"); - - return {locale: Utils.appLocale, bidiDir}; - } - - if (message.what == "features") - { - return { - devToolsPanel: info.platform == "chromium" - }; - } - - return info[message.what]; - }); - - port.on("app.listen", (message, sender) => - { - getListenerFilters(sender.page).app = message.filter; - }); - - port.on("app.open", (message, sender) => - { - if (message.what == "options") - ext.showOptions(); - }); - - port.on("filters.add", (message, sender) => - { - let result = require("filterValidation").parseFilter(message.text); - let errors = []; - if (result.error) - errors.push(result.error.toString()); - else if (result.filter) - FilterStorage.addFilter(result.filter); - - return errors; - }); - - port.on("filters.blocked", (message, sender) => - { - let filter = defaultMatcher.matchesAny(message.url, - RegExpFilter.typeMap[message.requestType], message.docDomain, - message.thirdParty); - - return filter instanceof BlockingFilter; - }); - - port.on("filters.get", (message, sender) => - { - if (message.what == "elemhideemulation") - { - let filters = []; - const {checkWhitelisted} = require("whitelisting"); - - if (Prefs.enabled && !checkWhitelisted(sender.page, sender.frame, - RegExpFilter.typeMap.DOCUMENT | - RegExpFilter.typeMap.ELEMHIDE)) - { - let {hostname} = sender.frame.url; - filters = ElemHideEmulation.getRulesForDomain(hostname); - filters = filters.map((filter) => - { - return { - selector: filter.selector, - text: filter.text - }; - }); - } - return filters; - } - - let subscription = Subscription.fromURL(message.subscriptionUrl); - if (!subscription) - return []; - - return subscription.filters.map(convertFilter); - }); - - port.on("filters.importRaw", (message, sender) => - { - let result = require("filterValidation").parseFilters(message.text); - let errors = []; - for (let error of result.errors) - { - if (error.type != "unexpected-filter-list-header") - errors.push(error.toString()); - } - - if (errors.length > 0) - return errors; - - let seenFilter = Object.create(null); - for (let filter of result.filters) - { - FilterStorage.addFilter(filter); - seenFilter[filter.text] = null; - } - - if (!message.removeExisting) - return errors; - - for (let subscription of FilterStorage.subscriptions) - { - if (!(subscription instanceof SpecialSubscription)) - continue; - - for (let j = subscription.filters.length - 1; j >= 0; j--) - { - let filter = subscription.filters[j]; - if (/^@@\|\|([^/:]+)\^\$document$/.test(filter.text)) - continue; - - if (!(filter.text in seenFilter)) - FilterStorage.removeFilter(filter); - } - } - - return errors; - }); - - port.on("filters.listen", (message, sender) => - { - getListenerFilters(sender.page).filter = message.filter; - addFilterListeners("filter", message.filter); - }); - - port.on("filters.remove", (message, sender) => - { - let filter = Filter.fromText(message.text); - let subscription = null; - if (message.subscriptionUrl) - subscription = Subscription.fromURL(message.subscriptionUrl); - - if (!subscription) - FilterStorage.removeFilter(filter); - else - FilterStorage.removeFilter(filter, subscription, message.index); - }); - - port.on("prefs.get", (message, sender) => - { - return Prefs[message.key]; - }); - - port.on("prefs.listen", (message, sender) => - { - getListenerFilters(sender.page).pref = message.filter; - for (let preference of message.filter) - { - if (!(preference in listenedPreferences)) - { - listenedPreferences[preference] = null; - Prefs.on(preference, () => - { - sendMessage("pref", preference, Prefs[preference]); - }); - } - } - }); - - port.on("prefs.toggle", (message, sender) => - { - if (message.key == "notifications_ignoredcategories") - NotificationStorage.toggleIgnoreCategory("*"); - else - Prefs[message.key] = !Prefs[message.key]; - }); - - port.on("subscriptions.add", (message, sender) => - { - let subscription = Subscription.fromURL(message.url); - if ("title" in message) - subscription.title = message.title; - if ("homepage" in message) - subscription.homepage = message.homepage; - - if (message.confirm) - { - ext.showOptions(() => - { - sendMessage("app", "addSubscription", subscription); - }); - } - else - { - subscription.disabled = false; - FilterStorage.addSubscription(subscription); - - if (subscription instanceof DownloadableSubscription && - !subscription.lastDownload) - Synchronizer.execute(subscription); - } - }); - - port.on("subscriptions.get", (message, sender) => - { - let subscriptions = FilterStorage.subscriptions.filter((s) => - { - if (message.ignoreDisabled && s.disabled) - return false; - if (s instanceof DownloadableSubscription && message.downloadable) - return true; - if (s instanceof SpecialSubscription && message.special) - return true; - return false; - }); - - return subscriptions.map(convertSubscription); - }); - - port.on("subscriptions.listen", (message, sender) => - { - getListenerFilters(sender.page).subscription = message.filter; - addFilterListeners("subscription", message.filter); - }); - - port.on("subscriptions.remove", (message, sender) => - { - let subscription = Subscription.fromURL(message.url); - if (subscription.url in FilterStorage.knownSubscriptions) - FilterStorage.removeSubscription(subscription); - }); - - port.on("subscriptions.toggle", (message, sender) => - { - let subscription = Subscription.fromURL(message.url); - if (subscription.url in FilterStorage.knownSubscriptions) - { - if (subscription.disabled || message.keepInstalled) - subscription.disabled = !subscription.disabled; - else - FilterStorage.removeSubscription(subscription); - } - else - { - subscription.disabled = false; - subscription.title = message.title; - subscription.homepage = message.homepage; - FilterStorage.addSubscription(subscription); - if (!subscription.lastDownload) - Synchronizer.execute(subscription); - } - }); - - port.on("subscriptions.update", (message, sender) => - { - let {subscriptions} = FilterStorage; - if (message.url) - subscriptions = [Subscription.fromURL(message.url)]; - - for (let subscription of subscriptions) - { - if (subscription instanceof DownloadableSubscription) - Synchronizer.execute(subscription, true); - } - }); -})(this); diff --git a/data/extensions/spyblock@gnu.org/lib/messaging.js b/data/extensions/spyblock@gnu.org/lib/messaging.js deleted file mode 100644 index 63d061e..0000000 --- a/data/extensions/spyblock@gnu.org/lib/messaging.js +++ /dev/null @@ -1,316 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -let {EventEmitter} = require("events"); - -const MESSAGE_NAME = "AdblockPlus:Message"; -const RESPONSE_NAME = "AdblockPlus:Response"; - -function isPromise(value) -{ - // value instanceof Promise won't work - there can be different Promise - // classes (e.g. in different contexts) and there can also be promise-like - // classes (e.g. Task). - return (value && typeof value.then == "function"); -} - -function sendMessage(messageManager, messageName, payload, callbackID) -{ - let request = {messageName, payload, callbackID}; - if (messageManager instanceof Ci.nsIMessageSender) - { - messageManager.sendAsyncMessage(MESSAGE_NAME, request); - return 1; - } - else if (messageManager instanceof Ci.nsIMessageBroadcaster) - { - messageManager.broadcastAsyncMessage(MESSAGE_NAME, request); - return messageManager.childCount; - } - else - { - Cu.reportError("Unexpected message manager, impossible to send message"); - return 0; - } -} - -function sendSyncMessage(messageManager, messageName, payload) -{ - let request = {messageName, payload}; - let responses = messageManager.sendRpcMessage(MESSAGE_NAME, request); - let processor = new ResponseProcessor(messageName); - for (let response of responses) - processor.add(response); - return processor.value; -} - -function ResponseProcessor(messageName) -{ - this.value = undefined; - this.add = function(response) - { - if (typeof response == "undefined") - return; - - if (typeof this.value == "undefined") - this.value = response; - else - Cu.reportError("Got multiple responses to message '" + messageName + "', only first response was accepted."); - }; -} - -function getSender(origin) -{ - if (origin instanceof Ci.nsIDOMXULElement) - origin = origin.messageManager; - - if (origin instanceof Ci.nsIMessageSender) - return new LightWeightPort(origin); - else - return null; -} - -/** - * Lightweight communication port allowing only sending messages. - * @param {nsIMessageManager} messageManager - * @constructor - */ -function LightWeightPort(messageManager) -{ - this._messageManager = messageManager; -} -LightWeightPort.prototype = -{ - /** - * @see Port#emit - */ - emit: function(messageName, payload) - { - sendMessage(this._messageManager, messageName, payload); - }, - - /** - * @see Port#emitSync - */ - emitSync: function(messageName, payload) - { - return sendSyncMessage(this._messageManager, messageName, payload); - } -}; - -/** - * Communication port wrapping the message manager API to send and receive - * messages. - * @param {nsIMessageManager} messageManager - * @constructor - */ -function Port(messageManager) -{ - this._messageManager = messageManager; - this._eventEmitter = new EventEmitter(); - - this._responseCallbacks = new Map(); - this._responseCallbackCounter = 0; - - this._handleRequest = this._handleRequest.bind(this); - this._handleResponse = this._handleResponse.bind(this); - this._messageManager.addMessageListener(MESSAGE_NAME, this._handleRequest); - this._messageManager.addMessageListener(RESPONSE_NAME, this._handleResponse); -} -Port.prototype = { - /** - * Disables the port and makes it stop listening to incoming messages. - */ - disconnect: function() - { - this._messageManager.removeMessageListener(MESSAGE_NAME, this._handleRequest); - this._messageManager.removeMessageListener(RESPONSE_NAME, this._handleResponse); - }, - - _sendResponse: function(sender, callbackID, payload) - { - if (!sender || typeof callbackID == "undefined") - return; - - let response = {callbackID, payload}; - sender._messageManager.sendAsyncMessage(RESPONSE_NAME, response); - }, - - _handleRequest: function(message) - { - let sender = getSender(message.target); - let {callbackID, messageName, payload} = message.data; - - let result = this._dispatch(messageName, payload, sender); - if (isPromise(result)) - { - // This is a promise - asynchronous response - if (message.sync) - { - Cu.reportError("Asynchronous response to the synchronous message '" + messageName + "' is not possible"); - return undefined; - } - - result.then(result => - { - this._sendResponse(sender, callbackID, result) - }, e => - { - Cu.reportError(e); - this._sendResponse(sender, callbackID, undefined); - }); - } - else - this._sendResponse(sender, callbackID, result); - - return result; - }, - - _handleResponse: function(message) - { - let {callbackID, payload} = message.data; - let callbackData = this._responseCallbacks.get(callbackID); - if (!callbackData) - return; - - let [callback, processor, expectedResponses] = callbackData; - - try - { - processor.add(payload); - } - catch (e) - { - Cu.reportError(e); - } - - callbackData[2] = --expectedResponses; - if (expectedResponses <= 0) - { - this._responseCallbacks.delete(callbackID); - callback(processor.value); - } - }, - - _dispatch: function(messageName, payload, sender) - { - let callbacks = this._eventEmitter.listeners(messageName); - let processor = new ResponseProcessor(messageName); - for (let callback of callbacks) - { - try - { - processor.add(callback(payload, sender)); - } - catch (e) - { - Cu.reportError(e); - } - } - return processor.value; - }, - - /** - * Function to be called when a particular message is received - * @callback Port~messageHandler - * @param payload data attached to the message if any - * @param {LightWeightPort} sender object that can be used to communicate with - * the sender of the message, could be null - * @return the handler can return undefined (no response), a value (response - * to be sent to sender immediately) or a promise (asynchronous - * response). - */ - - /** - * Adds a handler for the specified message. - * @param {string} messageName message that would trigger the callback - * @param {Port~messageHandler} callback - */ - on: function(messageName, callback) - { - this._eventEmitter.on(messageName, callback); - }, - - /** - * Removes a handler for the specified message. - * @param {string} messageName message that would trigger the callback - * @param {Port~messageHandler} callback - */ - off: function(messageName, callback) - { - this._eventEmitter.off(messageName, callback); - }, - - /** - * Sends a message. - * @param {string} messageName message identifier - * @param [payload] data to attach to the message - */ - emit: function(messageName, payload) - { - sendMessage(this._messageManager, messageName, payload, undefined); - }, - - /** - * Sends a message and expects a response. - * @param {string} messageName message identifier - * @param [payload] data to attach to the message - * @return {Promise} promise that will be resolved with the response - */ - emitWithResponse: function(messageName, payload) - { - let callbackID = ++this._responseCallbackCounter; - let expectedResponses = sendMessage( - this._messageManager, messageName, payload, callbackID); - return new Promise((resolve, reject) => - { - this._responseCallbacks.set(callbackID, - [resolve, new ResponseProcessor(messageName), expectedResponses]); - }); - }, - - /** - * Sends a synchonous message (DO NOT USE unless absolutely unavoidable). - * @param {string} messageName message identifier - * @param [payload] data to attach to the message - * @return response returned by the handler - */ - emitSync: function(messageName, payload) - { - return sendSyncMessage(this._messageManager, messageName, payload); - } -}; -exports.Port = Port; - -let messageManager; -try -{ - // Child - messageManager = require("messageManager"); -} -catch (e) -{ - // Parent - messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"] - .getService(Ci.nsIMessageListenerManager); -} - -let port = new Port(messageManager); -onShutdown.add(() => port.disconnect()); -exports.port = port; diff --git a/data/extensions/spyblock@gnu.org/lib/notification.js b/data/extensions/spyblock@gnu.org/lib/notification.js deleted file mode 100644 index 311e4e8..0000000 --- a/data/extensions/spyblock@gnu.org/lib/notification.js +++ /dev/null @@ -1,475 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Handles notifications. - */ - -const {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); - -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"); - -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, - relentless: 2, - critical: 3 -}; - -let showListeners = []; -let questionListeners = {}; - -function getNumericalSeverity(notification) -{ - if (notification.type in TYPE) - return TYPE[notification.type]; - return 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} - */ -let downloader = null; -let localData = []; - -/** - * Regularly fetches notifications and decides which to show. - * @class - */ -let Notification = exports.Notification = -{ - /** - * Called on module startup. - */ - init() - { - 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() - { - 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(downloadable) - { - Prefs.notificationdata.lastCheck = downloadable.lastCheck; - Prefs.notificationdata.softExpiration = downloadable.softExpiration; - Prefs.notificationdata.hardExpiration = downloadable.hardExpiration; - saveNotificationData(); - }, - - _onDownloadSuccess(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(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(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) - * @return {Object} notification to be shown, or null if there is none - */ - _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)); - } - - 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; - - 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") - { - 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 (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, 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(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(id) - { - 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; - - 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(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(notification) - { - if (localData.indexOf(notification) == -1) - localData.push(notification); - }, - - /** - * Removes an existing local notification. - * @param {Object} notification notification to remove - */ - removeNotification(notification) - { - let index = localData.indexOf(notification); - if (index > -1) - localData.splice(index, 1); - }, - - /** - * 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(id, 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(id, 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 - */ - triggerQuestionListeners(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(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(); diff --git a/data/extensions/spyblock@gnu.org/lib/objectTabs.js b/data/extensions/spyblock@gnu.org/lib/objectTabs.js deleted file mode 100644 index 3ee92bc..0000000 --- a/data/extensions/spyblock@gnu.org/lib/objectTabs.js +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Code responsible for showing and hiding object tabs. - */ - -let {Prefs} = require("prefs"); -let {Utils} = require("utils"); -let {port} = require("messaging"); - -/** - * Random element class, to be used for object tabs displayed on top of the - * plugin content. - * @type string - */ -let classVisibleTop = null; - -/** - * Random element class, to be used for object tabs displayed at the bottom of - * the plugin content. - * @type string - */ -let classVisibleBottom = null; - -/** - * Random element class, to be used for object tabs that are hidden. - * @type string - */ -let classHidden = null; - -port.on("getObjectTabsStatus", function(message, sender) -{ - let {UI} = require("ui"); - - return !!(Prefs.enabled && Prefs.frameobjects && UI.overlay && classHidden); -}); - -port.on("getObjectTabsTexts", function(message, sender) -{ - let {UI} = require("ui"); - - return { - label: UI.overlay.attributes.objtabtext, - tooltip: UI.overlay.attributes.objtabtooltip, - classVisibleTop, classVisibleBottom, classHidden - }; -}); - -port.on("blockItem", function({request, nodesID}, sender) -{ - let {UI} = require("ui"); - UI.blockItem(UI.currentWindow, nodesID, request); -}); - -function init() -{ - function processCSSData(event) - { - if (onShutdown.done) - return; - - let data = event.target.responseText; - - let rnd = []; - let offset = "a".charCodeAt(0); - for (let i = 0; i < 60; i++) - rnd.push(offset + Math.random() * 26); - - classVisibleTop = String.fromCharCode.apply(String, rnd.slice(0, 20)); - classVisibleBottom = String.fromCharCode.apply(String, rnd.slice(20, 40)); - classHidden = String.fromCharCode.apply(String, rnd.slice(40, 60)); - - let url = Utils.makeURI("data:text/css," + encodeURIComponent(data.replace(/%%CLASSVISIBLETOP%%/g, classVisibleTop) - .replace(/%%CLASSVISIBLEBOTTOM%%/g, classVisibleBottom) - .replace(/%%CLASSHIDDEN%%/g, classHidden))); - Utils.styleService.loadAndRegisterSheet(url, Ci.nsIStyleSheetService.USER_SHEET); - onShutdown.add(function() - { - Utils.styleService.unregisterSheet(url, Ci.nsIStyleSheetService.USER_SHEET); - }); - } - - // Load CSS asynchronously - try - { - let request = new XMLHttpRequest(); - request.mozBackgroundRequest = true; - request.open("GET", "chrome://adblockplus/content/objtabs.css"); - request.overrideMimeType("text/plain"); - request.addEventListener("load", processCSSData, false); - request.send(null); - } - catch (e) - { - Cu.reportError(e); - } -} -init(); diff --git a/data/extensions/spyblock@gnu.org/lib/prefs.js b/data/extensions/spyblock@gnu.org/lib/prefs.js deleted file mode 100644 index d1ebb95..0000000 --- a/data/extensions/spyblock@gnu.org/lib/prefs.js +++ /dev/null @@ -1,197 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); - -let {addonRoot, addonName} = require("info"); -let branchName = "extensions." + addonName + "."; -let branch = Services.prefs.getBranch(branchName); -let preconfiguredBranch = - Services.prefs.getBranch(branchName + "preconfigured."); -let ignorePrefChanges = false; - -function init() -{ - // Load default preferences and set up properties for them - let defaultBranch = Services.prefs.getDefaultBranch(branchName); - - let prefsData = require("prefs.json"); - let defaults = prefsData.defaults; - let preconfigurable = new Set(prefsData.preconfigurable); - for (let pref in defaults) - { - let value = defaults[pref]; - let [getter, setter] = typeMap[typeof value]; - if (preconfigurable.has(pref)) - { - try - { - value = getter(preconfiguredBranch, pref); - } - catch (e) {} - } - setter(defaultBranch, pref, value); - defineProperty(pref, false, getter, setter); - } - - // Add preference change observer - try - { - branch.QueryInterface(Ci.nsIPrefBranch2).addObserver("", Prefs, true); - onShutdown.add(() => branch.removeObserver("", Prefs)); - } - catch (e) - { - Cu.reportError(e); - } -} - -/** - * Sets up getter/setter on Prefs object for preference. - */ -function defineProperty(/**String*/ name, defaultValue, /**Function*/ readFunc, /**Function*/ writeFunc) -{ - let value = defaultValue; - Prefs["_update_" + name] = () => - { - try - { - value = readFunc(branch, name); - triggerListeners(name); - } - catch(e) - { - Cu.reportError(e); - } - }; - Object.defineProperty(Prefs, name, { - enumerable: true, - get: () => value, - set: (newValue) => - { - if (value == newValue) - return value; - - try - { - ignorePrefChanges = true; - writeFunc(branch, name, newValue); - value = newValue; - Services.prefs.savePrefFile(null); - triggerListeners(name); - } - catch(e) - { - Cu.reportError(e); - } - finally - { - ignorePrefChanges = false; - } - return value; - } - }); - Prefs["_update_" + name](); -} - -let listeners = []; -function triggerListeners(/**String*/ name) -{ - for (let i = 0; i < listeners.length; i++) - { - try - { - listeners[i](name); - } - catch(e) - { - Cu.reportError(e); - } - } -} - -/** - * Manages the preferences for an extension, object properties corresponding - * to extension's preferences are added automatically. Setting the property - * will automatically change the preference, external preference changes are - * also recognized automatically. - */ -let Prefs = exports.Prefs = -{ - /** - * Migrates an old preference to a new name. - */ - migrate: function(/**String*/ oldName, /**String*/ newName) - { - if (newName in this && Services.prefs.prefHasUserValue(oldName)) - { - let [getter, setter] = typeMap[typeof this[newName]]; - try - { - this[newName] = getter(Services.prefs, oldName); - } catch(e) {} - Services.prefs.clearUserPref(oldName); - } - }, - - /** - * Adds a preferences listener that will be fired whenever a preference - * changes. - */ - addListener: function(/**Function*/ listener) - { - if (listeners.indexOf(listener) < 0) - listeners.push(listener); - }, - - /** - * Removes a preferences listener. - */ - removeListener: function(/**Function*/ listener) - { - let index = listeners.indexOf(listener); - if (index >= 0) - listeners.splice(index, 1); - }, - - observe: function(subject, topic, data) - { - if (ignorePrefChanges || topic != "nsPref:changed") - return; - - if ("_update_" + data in this) - this["_update_" + data](); - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIObserver]) -}; - -let getIntPref = (branch, pref) => branch.getIntPref(pref); -let setIntPref = (branch, pref, newValue) => branch.setIntPref(pref, newValue); - -let getBoolPref = (branch, pref) => branch.getBoolPref(pref); -let setBoolPref = (branch, pref, newValue) => branch.setBoolPref(pref, newValue); - -let getCharPref = (branch, pref) => branch.getComplexValue(pref, Ci.nsISupportsString).data; -let setCharPref = (branch, pref, newValue) => -{ - let str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); - str.data = newValue; - branch.setComplexValue(pref, Ci.nsISupportsString, str); -}; - -let getJSONPref = (branch, pref) => JSON.parse(getCharPref(branch, pref)); -let setJSONPref = (branch, pref, newValue) => setCharPref(branch, pref, JSON.stringify(newValue)); - -// Getter/setter functions for difference preference types -let typeMap = -{ - boolean: [getBoolPref, setBoolPref], - number: [getIntPref, setIntPref], - string: [getCharPref, setCharPref], - object: [getJSONPref, setJSONPref] -}; - -init(); diff --git a/data/extensions/spyblock@gnu.org/lib/requestNotifier.js b/data/extensions/spyblock@gnu.org/lib/requestNotifier.js deleted file mode 100644 index f42eaac..0000000 --- a/data/extensions/spyblock@gnu.org/lib/requestNotifier.js +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Stores Adblock Plus data to be attached to a window. - */ - -let {port} = require("messaging"); - -let requestNotifierMaxId = 0; - -/** - * Active RequestNotifier instances by their ID - * @type Map.<number,RequestNotifier> - */ -let notifiers = new Map(); - -port.on("foundNodeData", ({notifierID, data}, sender) => -{ - let notifier = notifiers.get(notifierID); - if (notifier) - notifier.notifyListener(data); -}); - -port.on("scanComplete", (notifierID, sender) => -{ - let notifier = notifiers.get(notifierID); - if (notifier) - notifier.onComplete(); -}); - -/** - * Creates a notifier object for a particular window. After creation the window - * will first be scanned for previously saved requests. Once that scan is - * complete only new requests for this window will be reported. - * @param {Integer} outerWindowID ID of the window to attach the notifier to - * @param {Function} listener listener to be called whenever a new request is found - * @param {Object} [listenerObj] "this" pointer to be used when calling the listener - */ -function RequestNotifier(outerWindowID, listener, listenerObj) -{ - this.listener = listener; - this.listenerObj = listenerObj || null; - this.id = ++requestNotifierMaxId; - notifiers.set(this.id, this); - - port.emit("startWindowScan", { - notifierID: this.id, - outerWindowID: outerWindowID - }); -} -exports.RequestNotifier = RequestNotifier; - -RequestNotifier.prototype = -{ - /** - * The unique ID of this notifier. - * @type Integer - */ - id: null, - - /** - * The listener to be called when a new request is found. - * @type Function - */ - listener: null, - - /** - * "this" pointer to be used when calling the listener. - * @type Object - */ - listenerObj: null, - - /** - * Will be set to true once the initial window scan is complete. - * @type Boolean - */ - scanComplete: false, - - /** - * Shuts down the notifier once it is no longer used. The listener - * will no longer be called after that. - */ - shutdown: function() - { - notifiers.delete(this.id); - port.emit("shutdownNotifier", this.id); - }, - - /** - * Notifies listener about a new request. - * @param {Object} entry - */ - notifyListener: function(entry) - { - this.listener.call(this.listenerObj, entry, this.scanComplete); - }, - - onComplete: function() - { - this.scanComplete = true; - this.notifyListener(null); - }, - - /** - * Makes the nodes associated with the given requests blink. - * @param {number[]} requests list of request IDs that were previously - * reported by this notifier. - * @param {Boolean} scrollToItem if true, scroll to first node - */ - flashNodes: function(requests, scrollToItem) - { - if (!requests) - requests = []; - - port.emit("flashNodes", { - notifierID: this.id, - requests, - scrollToItem - }); - }, - - /** - * Attempts to calculate the size of the nodes associated with the requests. - * @param {number[]} requests list of request IDs that were previously - * reported by this notifier. - * @param {Function} callback function to be called with two parameters (x,y) - */ - retrieveNodeSize: function(requests, callback) - { - if (!requests) - requests = []; - - port.emitWithResponse("retrieveNodeSize", { - notifierID: this.id, - requests - }).then(callback); - }, - - /** - * Stores the nodes associated with the requests and generates a unique ID - * for them that can be used with Policy.refilterNodes(). Note that - * Policy.deleteNodes() always has to be called to release the memory. - * @param {number[]} requests list of request IDs that were previously - * reported by this notifier. - * @param {Function} callback function to be called with the nodes ID. - */ - storeNodesForEntries: function(requests, callback) - { - if (!requests) - requests = []; - - port.emitWithResponse("storeNodesForEntries", { - notifierID: this.id, - requests - }).then(callback); - } -}; - -/** - * Associates a piece of data with a particular window. - * @param {number} outerWindowID the ID of the window - * @static - */ -RequestNotifier.storeWindowData = function(outerWindowID, data) -{ - port.emit("storeWindowData", { - outerWindowID, - data - }); -}; - -/** - * Retrieves a piece of data previously associated with the window by calling - * storeWindowData. - * @param {number} outerWindowID the ID of the window - * @param {Function} callback function to be called with the data. - * @static - */ -RequestNotifier.retrieveWindowData = function(outerWindowID, callback) -{ - port.emitWithResponse("retrieveWindowData", outerWindowID).then(callback); -}; - -/** - * Retrieves the statistics for a window. - * @param {number} outerWindowID the ID of the window - * @param {Function} callback the callback to be called with the resulting - * object (object properties will be items, blocked, - * whitelisted, hidden, filters) or null. - */ -RequestNotifier.getWindowStatistics = function(outerWindowID, callback) -{ - port.emitWithResponse("retrieveWindowStats", outerWindowID).then(callback); -}; diff --git a/data/extensions/spyblock@gnu.org/lib/subscriptionClasses.js b/data/extensions/spyblock@gnu.org/lib/subscriptionClasses.js deleted file mode 100644 index 5fe1eb8..0000000 --- a/data/extensions/spyblock@gnu.org/lib/subscriptionClasses.js +++ /dev/null @@ -1,584 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Definition of Subscription class and its subclasses. - */ - -const {ActiveFilter, BlockingFilter, - WhitelistFilter, ElemHideBase} = require("filterClasses"); -const {FilterNotifier} = require("filterNotifier"); -const {desc, extend} = require("coreUtils"); - -/** - * Abstract base class for filter subscriptions - * - * @param {string} url download location of the subscription - * @param {string} [title] title of the filter subscription - * @constructor - */ -function Subscription(url, title) -{ - this.url = url; - this.filters = []; - if (title) - this._title = title; - Subscription.knownSubscriptions[url] = this; -} -exports.Subscription = Subscription; - -Subscription.prototype = -{ - /** - * Download location of the subscription - * @type {string} - */ - url: null, - - /** - * Filters contained in the filter subscription - * @type {Filter[]} - */ - filters: null, - - _title: null, - _fixedTitle: false, - _disabled: false, - - /** - * Title of the filter subscription - * @type {string} - */ - get title() - { - return this._title; - }, - set title(value) - { - if (value != this._title) - { - let oldValue = this._title; - this._title = value; - FilterNotifier.triggerListeners("subscription.title", - this, value, oldValue); - } - return this._title; - }, - - /** - * Determines whether the title should be editable - * @type {boolean} - */ - get fixedTitle() - { - return this._fixedTitle; - }, - set fixedTitle(value) - { - if (value != this._fixedTitle) - { - let oldValue = this._fixedTitle; - this._fixedTitle = value; - FilterNotifier.triggerListeners("subscription.fixedTitle", - this, value, oldValue); - } - return this._fixedTitle; - }, - - /** - * Defines whether the filters in the subscription should be disabled - * @type {boolean} - */ - get disabled() - { - return this._disabled; - }, - set disabled(value) - { - if (value != this._disabled) - { - let oldValue = this._disabled; - this._disabled = value; - FilterNotifier.triggerListeners("subscription.disabled", - this, value, oldValue); - } - return this._disabled; - }, - - /** - * Serializes the subscription to an array of strings for writing - * out on the disk. - * @param {string[]} buffer buffer to push the serialization results into - */ - serialize(buffer) - { - buffer.push("[Subscription]"); - buffer.push("url=" + this.url); - if (this._title) - buffer.push("title=" + this._title); - if (this._fixedTitle) - buffer.push("fixedTitle=true"); - if (this._disabled) - buffer.push("disabled=true"); - }, - - serializeFilters(buffer) - { - for (let filter of this.filters) - buffer.push(filter.text.replace(/\[/g, "\\[")); - }, - - toString() - { - let buffer = []; - this.serialize(buffer); - return buffer.join("\n"); - } -}; - -/** - * Cache for known filter subscriptions, maps URL to subscription objects. - * @type {Object} - */ -Subscription.knownSubscriptions = Object.create(null); - -/** - * Returns a subscription from its URL, creates a new one if necessary. - * @param {string} url - * URL of the subscription - * @return {Subscription} - * subscription or null if the subscription couldn't be created - */ -Subscription.fromURL = function(url) -{ - if (url in Subscription.knownSubscriptions) - return Subscription.knownSubscriptions[url]; - - if (url[0] != "~") - return new DownloadableSubscription(url, null); - return new SpecialSubscription(url); -}; - -/** - * Deserializes a subscription - * - * @param {Object} obj - * map of serialized properties and their values - * @return {Subscription} - * subscription or null if the subscription couldn't be created - */ -Subscription.fromObject = function(obj) -{ - let result; - if (obj.url[0] != "~") - { - // URL is valid - this is a downloadable subscription - result = new DownloadableSubscription(obj.url, obj.title); - if ("downloadStatus" in obj) - result._downloadStatus = obj.downloadStatus; - if ("lastSuccess" in obj) - result.lastSuccess = parseInt(obj.lastSuccess, 10) || 0; - if ("lastCheck" in obj) - result._lastCheck = parseInt(obj.lastCheck, 10) || 0; - if ("expires" in obj) - result.expires = parseInt(obj.expires, 10) || 0; - if ("softExpiration" in obj) - result.softExpiration = parseInt(obj.softExpiration, 10) || 0; - if ("errors" in obj) - result._errors = parseInt(obj.errors, 10) || 0; - if ("version" in obj) - result.version = parseInt(obj.version, 10) || 0; - if ("requiredVersion" in obj) - result.requiredVersion = obj.requiredVersion; - if ("homepage" in obj) - result._homepage = obj.homepage; - if ("lastDownload" in obj) - result._lastDownload = parseInt(obj.lastDownload, 10) || 0; - if ("downloadCount" in obj) - result.downloadCount = parseInt(obj.downloadCount, 10) || 0; - } - else - { - result = new SpecialSubscription(obj.url, obj.title); - if ("defaults" in obj) - result.defaults = obj.defaults.split(" "); - } - if ("fixedTitle" in obj) - result._fixedTitle = (obj.fixedTitle == "true"); - if ("privateMode" in obj) - result.privateMode = (obj.privateMode == "true"); - if ("disabled" in obj) - result._disabled = (obj.disabled == "true"); - - return result; -}; - -/** - * Class for special filter subscriptions (user's filters) - * @param {string} url see Subscription() - * @param {string} [title] see Subscription() - * @constructor - * @augments Subscription - */ -function SpecialSubscription(url, title) -{ - Subscription.call(this, url, title); -} -exports.SpecialSubscription = SpecialSubscription; - -SpecialSubscription.prototype = extend(Subscription, { - /** - * Filter types that should be added to this subscription by default - * (entries should correspond to keys in SpecialSubscription.defaultsMap). - * @type {string[]} - */ - defaults: null, - - /** - * Tests whether a filter should be added to this group by default - * @param {Filter} filter filter to be tested - * @return {boolean} - */ - isDefaultFor(filter) - { - if (this.defaults && this.defaults.length) - { - for (let type of this.defaults) - { - if (filter instanceof SpecialSubscription.defaultsMap[type]) - return true; - if (!(filter instanceof ActiveFilter) && type == "blacklist") - return true; - } - } - - return false; - }, - - /** - * See Subscription.serialize() - * @inheritdoc - */ - serialize(buffer) - { - Subscription.prototype.serialize.call(this, buffer); - if (this.defaults && this.defaults.length) - { - buffer.push("defaults=" + - this.defaults.filter( - type => type in SpecialSubscription.defaultsMap - ).join(" ") - ); - } - if (this._lastDownload) - buffer.push("lastDownload=" + this._lastDownload); - } -}); - -SpecialSubscription.defaultsMap = Object.create(null, desc({ - whitelist: WhitelistFilter, - blocking: BlockingFilter, - elemhide: ElemHideBase -})); - -/** - * Creates a new user-defined filter group. - * @param {string} [title] title of the new filter group - * @return {SpecialSubscription} - */ -SpecialSubscription.create = function(title) -{ - let url; - do - { - url = "~user~" + Math.round(Math.random() * 1000000); - } while (url in Subscription.knownSubscriptions); - return new SpecialSubscription(url, title); -}; - -/** - * Creates a new user-defined filter group and adds the given filter to it. - * This group will act as the default group for this filter type. - * @param {Filter} filter - * @return {SpecialSubscription} - */ -SpecialSubscription.createForFilter = function(filter) -{ - let subscription = SpecialSubscription.create(); - subscription.filters.push(filter); - for (let type in SpecialSubscription.defaultsMap) - { - if (filter instanceof SpecialSubscription.defaultsMap[type]) - subscription.defaults = [type]; - } - if (!subscription.defaults) - subscription.defaults = ["blocking"]; - return subscription; -}; - -/** - * Abstract base class for regular filter subscriptions (both - * internally and externally updated) - * @param {string} url see Subscription() - * @param {string} [title] see Subscription() - * @constructor - * @augments Subscription - */ -function RegularSubscription(url, title) -{ - Subscription.call(this, url, title || url); -} -exports.RegularSubscription = RegularSubscription; - -RegularSubscription.prototype = extend(Subscription, { - _homepage: null, - _lastDownload: 0, - - /** - * Filter subscription homepage if known - * @type {string} - */ - get homepage() - { - return this._homepage; - }, - set homepage(value) - { - if (value != this._homepage) - { - let oldValue = this._homepage; - this._homepage = value; - FilterNotifier.triggerListeners("subscription.homepage", - this, value, oldValue); - } - return this._homepage; - }, - - /** - * Time of the last subscription download (in seconds since the - * beginning of the epoch) - * @type {number} - */ - get lastDownload() - { - return this._lastDownload; - }, - set lastDownload(value) - { - if (value != this._lastDownload) - { - let oldValue = this._lastDownload; - this._lastDownload = value; - FilterNotifier.triggerListeners("subscription.lastDownload", - this, value, oldValue); - } - return this._lastDownload; - }, - - /** - * See Subscription.serialize() - * @inheritdoc - */ - serialize(buffer) - { - Subscription.prototype.serialize.call(this, buffer); - if (this._homepage) - buffer.push("homepage=" + this._homepage); - if (this._lastDownload) - buffer.push("lastDownload=" + this._lastDownload); - } -}); - -/** - * Class for filter subscriptions updated externally (by other extension) - * @param {string} url see Subscription() - * @param {string} [title] see Subscription() - * @constructor - * @augments RegularSubscription - */ -function ExternalSubscription(url, title) -{ - RegularSubscription.call(this, url, title); -} -exports.ExternalSubscription = ExternalSubscription; - -ExternalSubscription.prototype = extend(RegularSubscription, { - /** - * See Subscription.serialize() - * @inheritdoc - */ - serialize(buffer) - { - throw new Error( - "Unexpected call, external subscriptions should not be serialized" - ); - } -}); - -/** - * Class for filter subscriptions updated externally (by other extension) - * @param {string} url see Subscription() - * @param {string} [title] see Subscription() - * @constructor - * @augments RegularSubscription - */ -function DownloadableSubscription(url, title) -{ - RegularSubscription.call(this, url, title); -} -exports.DownloadableSubscription = DownloadableSubscription; - -DownloadableSubscription.prototype = extend(RegularSubscription, { - _downloadStatus: null, - _lastCheck: 0, - _errors: 0, - - /** - * Status of the last download (ID of a string) - * @type {string} - */ - get downloadStatus() - { - return this._downloadStatus; - }, - set downloadStatus(value) - { - let oldValue = this._downloadStatus; - this._downloadStatus = value; - FilterNotifier.triggerListeners("subscription.downloadStatus", - this, value, oldValue); - return this._downloadStatus; - }, - - /** - * Time of the last successful download (in seconds since the beginning of the - * epoch). - */ - lastSuccess: 0, - - /** - * Time when the subscription was considered for an update last time - * (in seconds since the beginning of the epoch). This will be used - * to increase softExpiration if the user doesn't use Adblock Plus - * for some time. - * @type {number} - */ - get lastCheck() - { - return this._lastCheck; - }, - set lastCheck(value) - { - if (value != this._lastCheck) - { - let oldValue = this._lastCheck; - this._lastCheck = value; - FilterNotifier.triggerListeners("subscription.lastCheck", - this, value, oldValue); - } - return this._lastCheck; - }, - - /** - * Hard expiration time of the filter subscription (in seconds since - * the beginning of the epoch) - * @type {number} - */ - expires: 0, - - /** - * Soft expiration time of the filter subscription (in seconds since - * the beginning of the epoch) - * @type {number} - */ - softExpiration: 0, - - /** - * Number of download failures since last success - * @type {number} - */ - get errors() - { - return this._errors; - }, - set errors(value) - { - if (value != this._errors) - { - let oldValue = this._errors; - this._errors = value; - FilterNotifier.triggerListeners("subscription.errors", this, - value, oldValue); - } - return this._errors; - }, - - /** - * Version of the subscription data retrieved on last successful download - * @type {number} - */ - version: 0, - - /** - * Minimal Adblock Plus version required for this subscription - * @type {string} - */ - requiredVersion: null, - - /** - * Number indicating how often the object was downloaded. - * @type {number} - */ - downloadCount: 0, - - /** - * Should be true if the Privatemode: header is set to true in the subscription - * @type Boolean - */ - privateMode: false, - - /** - * See Subscription.serialize() - * @inheritdoc - */ - serialize(buffer) - { - RegularSubscription.prototype.serialize.call(this, buffer); - if (this.downloadStatus) - buffer.push("downloadStatus=" + this.downloadStatus); - if (this.lastSuccess) - buffer.push("lastSuccess=" + this.lastSuccess); - if (this.lastCheck) - buffer.push("lastCheck=" + this.lastCheck); - if (this.expires) - buffer.push("expires=" + this.expires); - if (this.softExpiration) - buffer.push("softExpiration=" + this.softExpiration); - if (this.errors) - buffer.push("errors=" + this.errors); - if (this.version) - buffer.push("version=" + this.version); - if (this.requiredVersion) - buffer.push("requiredVersion=" + this.requiredVersion); - if (this.downloadCount) - buffer.push("downloadCount=" + this.downloadCount); - if (this.privateMode) - buffer.push("privateMode=" + this.privateMode); - } -}); diff --git a/data/extensions/spyblock@gnu.org/lib/sync.js b/data/extensions/spyblock@gnu.org/lib/sync.js deleted file mode 100644 index 3f9d973..0000000 --- a/data/extensions/spyblock@gnu.org/lib/sync.js +++ /dev/null @@ -1,459 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 = Object.create(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 = Object.create(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(); diff --git a/data/extensions/spyblock@gnu.org/lib/synchronizer.js b/data/extensions/spyblock@gnu.org/lib/synchronizer.js deleted file mode 100644 index b8d14a2..0000000 --- a/data/extensions/spyblock@gnu.org/lib/synchronizer.js +++ /dev/null @@ -1,365 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -"use strict"; - -/** - * @fileOverview Manages synchronization of filter subscriptions. - */ - -const {Downloader, Downloadable, - MILLIS_IN_SECOND, MILLIS_IN_MINUTE, - MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader"); -const {Filter} = require("filterClasses"); -const {FilterStorage} = require("filterStorage"); -const {FilterNotifier} = require("filterNotifier"); -const {Prefs} = require("prefs"); -const {Subscription, DownloadableSubscription} = require("subscriptionClasses"); -const {Utils} = require("utils"); - -const INITIAL_DELAY = 1 * MILLIS_IN_MINUTE; -const CHECK_INTERVAL = 1 * MILLIS_IN_HOUR; -const DEFAULT_EXPIRATION_INTERVAL = 5 * MILLIS_IN_DAY; - -/** - * The object providing actual downloading functionality. - * @type {Downloader} - */ -let downloader = null; - -/** - * This object is responsible for downloading filter subscriptions whenever - * necessary. - * @class - */ -let Synchronizer = exports.Synchronizer = -{ - /** - * Called on module startup. - */ - init() - { - downloader = new Downloader(this._getDownloadables.bind(this), - INITIAL_DELAY, CHECK_INTERVAL); - onShutdown.add(() => - { - downloader.cancel(); - }); - - downloader.onExpirationChange = this._onExpirationChange.bind(this); - downloader.onDownloadStarted = this._onDownloadStarted.bind(this); - downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this); - downloader.onDownloadError = this._onDownloadError.bind(this); - }, - - /** - * Checks whether a subscription is currently being downloaded. - * @param {string} url URL of the subscription - * @return {boolean} - */ - isExecuting(url) - { - return downloader.isDownloading(url); - }, - - /** - * Starts the download of a subscription. - * @param {DownloadableSubscription} subscription - * Subscription to be downloaded - * @param {boolean} manual - * true for a manually started download (should not trigger fallback - * requests) - */ - execute(subscription, manual) - { - downloader.download(this._getDownloadable(subscription, manual)); - }, - - /** - * Yields Downloadable instances for all subscriptions that can be downloaded. - */ - *_getDownloadables() - { - if (!Prefs.subscriptions_autoupdate) - return; - - for (let subscription of FilterStorage.subscriptions) - { - if (subscription instanceof DownloadableSubscription) - yield this._getDownloadable(subscription, false); - } - }, - - /** - * Creates a Downloadable instance for a subscription. - * @param {Subscription} subscription - * @param {boolean} manual - * @return {Downloadable} - */ - _getDownloadable(subscription, manual) - { - let result = new Downloadable(subscription.url); - if (subscription.lastDownload != subscription.lastSuccess) - result.lastError = subscription.lastDownload * MILLIS_IN_SECOND; - result.lastCheck = subscription.lastCheck * MILLIS_IN_SECOND; - result.lastVersion = subscription.version; - result.softExpiration = subscription.softExpiration * MILLIS_IN_SECOND; - result.hardExpiration = subscription.expires * MILLIS_IN_SECOND; - result.manual = manual; - result.downloadCount = subscription.downloadCount; - result.privateMode = subscription.privateMode; - return result; - }, - - _onExpirationChange(downloadable) - { - let subscription = Subscription.fromURL(downloadable.url); - subscription.lastCheck = Math.round( - downloadable.lastCheck / MILLIS_IN_SECOND - ); - subscription.softExpiration = Math.round( - downloadable.softExpiration / MILLIS_IN_SECOND - ); - subscription.expires = Math.round( - downloadable.hardExpiration / MILLIS_IN_SECOND - ); - }, - - _onDownloadStarted(downloadable) - { - let subscription = Subscription.fromURL(downloadable.url); - FilterNotifier.triggerListeners("subscription.downloading", subscription); - }, - - _onDownloadSuccess(downloadable, responseText, errorCallback, - redirectCallback) - { - let lines = responseText.split(/[\r\n]+/); - let headerMatch = /\[Adblock(?:\s*Plus\s*([\d.]+)?)?\]/i.exec(lines[0]); - if (!headerMatch) - return errorCallback("synchronize_invalid_data"); - let minVersion = headerMatch[1]; - - // Don't remove parameter comments immediately but add them to a list first, - // they need to be considered in the checksum calculation. - let remove = []; - let params = { - redirect: null, - homepage: null, - title: null, - version: null, - privatemode: null, - expires: null - }; - for (let i = 0; i < lines.length; i++) - { - let match = /^\s*!\s*(\w+)\s*:\s*(.*)/.exec(lines[i]); - if (match) - { - let keyword = match[1].toLowerCase(); - let value = match[2]; - if (keyword in params) - { - params[keyword] = value; - remove.push(i); - } - else if (keyword == "checksum") - { - lines.splice(i--, 1); - let checksum = Utils.generateChecksum(lines); - if (checksum && checksum != value.replace(/=+$/, "")) - return errorCallback("synchronize_checksum_mismatch"); - } - } - } - - if (params.redirect) - return redirectCallback(params.redirect); - - // Handle redirects - let subscription = Subscription.fromURL(downloadable.redirectURL || - downloadable.url); - if (downloadable.redirectURL && - downloadable.redirectURL != downloadable.url) - { - let oldSubscription = Subscription.fromURL(downloadable.url); - subscription.title = oldSubscription.title; - subscription.disabled = oldSubscription.disabled; - subscription.lastCheck = oldSubscription.lastCheck; - - let listed = (oldSubscription.url in FilterStorage.knownSubscriptions); - if (listed) - FilterStorage.removeSubscription(oldSubscription); - - delete Subscription.knownSubscriptions[oldSubscription.url]; - - if (listed) - FilterStorage.addSubscription(subscription); - } - - // The download actually succeeded - subscription.lastSuccess = subscription.lastDownload = Math.round( - Date.now() / MILLIS_IN_SECOND - ); - subscription.downloadStatus = "synchronize_ok"; - subscription.downloadCount = downloadable.downloadCount; - subscription.errors = 0; - - // Remove lines containing parameters - for (let i = remove.length - 1; i >= 0; i--) - lines.splice(remove[i], 1); - - // Process parameters - if (params.homepage) - { - let url; - try - { - url = new URL(params.homepage); - } - catch (e) - { - url = null; - } - - if (url && (url.protocol == "http:" || url.protocol == "https:")) - subscription.homepage = url.href; - } - - if (params.privatemode) - { - subscription.privateMode = (params.privatemode == "true"); - } - - if (params.title) - { - subscription.title = params.title; - subscription.fixedTitle = true; - } - else - subscription.fixedTitle = false; - - subscription.version = (params.version ? parseInt(params.version, 10) : 0); - - let expirationInterval = DEFAULT_EXPIRATION_INTERVAL; - if (params.expires) - { - let match = /^(\d+)\s*(h)?/.exec(params.expires); - if (match) - { - let interval = parseInt(match[1], 10); - if (match[2]) - expirationInterval = interval * MILLIS_IN_HOUR; - else - expirationInterval = interval * MILLIS_IN_DAY; - } - } - - let [ - softExpiration, - hardExpiration - ] = downloader.processExpirationInterval(expirationInterval); - subscription.softExpiration = Math.round(softExpiration / MILLIS_IN_SECOND); - subscription.expires = Math.round(hardExpiration / MILLIS_IN_SECOND); - - if (minVersion) - subscription.requiredVersion = minVersion; - else - delete subscription.requiredVersion; - - // Process filters - lines.shift(); - let filters = []; - for (let line of lines) - { - line = Filter.normalize(line); - if (line) - filters.push(Filter.fromText(line)); - } - - FilterStorage.updateSubscriptionFilters(subscription, filters); - - return undefined; - }, - - _onDownloadError(downloadable, downloadURL, error, channelStatus, - responseStatus, redirectCallback) - { - let subscription = Subscription.fromURL(downloadable.url); - subscription.lastDownload = Math.round(Date.now() / MILLIS_IN_SECOND); - subscription.downloadStatus = error; - - // Request fallback URL if necessary - for automatic updates only - if (!downloadable.manual) - { - subscription.errors++; - - if (redirectCallback && - subscription.errors >= Prefs.subscriptions_fallbackerrors && - /^https?:\/\//i.test(subscription.url)) - { - subscription.errors = 0; - - let fallbackURL = Prefs.subscriptions_fallbackurl; - const {addonVersion} = require("info"); - fallbackURL = fallbackURL.replace(/%VERSION%/g, - encodeURIComponent(addonVersion)); - fallbackURL = fallbackURL.replace(/%SUBSCRIPTION%/g, - encodeURIComponent(subscription.url)); - fallbackURL = fallbackURL.replace(/%URL%/g, - encodeURIComponent(downloadURL)); - fallbackURL = fallbackURL.replace(/%ERROR%/g, - encodeURIComponent(error)); - fallbackURL = fallbackURL.replace(/%CHANNELSTATUS%/g, - encodeURIComponent(channelStatus)); - fallbackURL = fallbackURL.replace(/%RESPONSESTATUS%/g, - encodeURIComponent(responseStatus)); - - let request = new XMLHttpRequest(); - request.mozBackgroundRequest = true; - request.open("GET", fallbackURL); - request.overrideMimeType("text/plain"); - request.channel.loadFlags = request.channel.loadFlags | - request.channel.INHIBIT_CACHING | - request.channel.VALIDATE_ALWAYS; - request.addEventListener("load", ev => - { - if (onShutdown.done) - return; - - if (!(subscription.url in FilterStorage.knownSubscriptions)) - return; - - let match = /^(\d+)(?:\s+(\S+))?$/.exec(request.responseText); - if (match && match[1] == "301" && // Moved permanently - match[2] && /^https?:\/\//i.test(match[2])) - { - redirectCallback(match[2]); - } - else if (match && match[1] == "410") // Gone - { - let data = "[Adblock]\n" + - subscription.filters.map(f => f.text).join("\n"); - redirectCallback("data:text/plain," + encodeURIComponent(data)); - } - }, false); - request.send(null); - } - } - } -}; -Synchronizer.init(); diff --git a/data/extensions/spyblock@gnu.org/lib/ui.js b/data/extensions/spyblock@gnu.org/lib/ui.js deleted file mode 100644 index 1941a97..0000000 --- a/data/extensions/spyblock@gnu.org/lib/ui.js +++ /dev/null @@ -1,1906 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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/>. - */ - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -let {Utils} = require("utils"); -let {port} = require("messaging"); -let {Prefs} = require("prefs"); -let {Policy} = require("contentPolicy"); -let {FilterStorage} = require("filterStorage"); -let {FilterNotifier} = require("filterNotifier"); -let {RequestNotifier} = require("requestNotifier"); -let {Filter} = require("filterClasses"); -let {Subscription, SpecialSubscription, DownloadableSubscription} = require("subscriptionClasses"); -let {Synchronizer} = require("synchronizer"); -let {KeySelector} = require("keySelector"); -let {Notification} = require("notification"); -let {initAntiAdblockNotification} = require("antiadblockInit"); - -let CustomizableUI = null; - -/** - * Filter corresponding with "disable on site" menu item (set in fillIconMent()). - * @type Filter - */ -let siteWhitelist = null; -/** - * Filter corresponding with "disable on site" menu item (set in fillIconMenu()). - * @type Filter - */ -let pageWhitelist = null; - -/** - * Window containing the detached list of blockable items. - * @type Window - */ -let detachedBottombar = null; - -/** - * Object initializing add-on options, observes add-on manager notifications - * about add-on options being opened. - * @type nsIObserver - */ -let optionsObserver = -{ - init: function() - { - Services.obs.addObserver(this, "addon-options-displayed", true); - onShutdown.add(function() - { - Services.obs.removeObserver(this, "addon-options-displayed"); - }.bind(this)); - }, - - /** - * Initializes options in add-on manager when they show up. - */ - initOptionsDoc: function(/**Document*/ doc) - { - function hideElement(id, hide) - { - let element = doc.getElementById(id); - if (element) - element.collapsed = hide; - } - function setChecked(id, checked) - { - let element = doc.getElementById(id); - if (element) - element.value = checked; - } - function addCommandHandler(id, handler) - { - let element = doc.getElementById(id); - if (element) - element.addEventListener("command", handler, false); - } - - Utils.splitAllLabels(doc); - - addCommandHandler("adblockplus-filters", UI.openFiltersDialog.bind(UI)); - - let {Sync} = require("sync"); - let syncEngine = Sync.getEngine(); - hideElement("adblockplus-sync", !syncEngine); - - let {defaultToolbarPosition, statusbarPosition} = require("appSupport"); - let hasToolbar = defaultToolbarPosition; - let hasStatusBar = statusbarPosition; - - hideElement("adblockplus-showintoolbar", !hasToolbar); - hideElement("adblockplus-showinstatusbar", !hasStatusBar); - - let checkbox = doc.querySelector("setting[type=bool]"); - if (checkbox) - initCheckboxes(); - - function initCheckboxes() - { - if (!("value" in checkbox)) - { - // XBL bindings didn't apply yet (bug 708397), try later - Utils.runAsync(initCheckboxes); - return; - } - - setChecked("adblockplus-savestats", Prefs.savestats); - addCommandHandler("adblockplus-savestats", function() - { - UI.toggleSaveStats(doc.defaultView); - this.value = Prefs.savestats; - }); - - hideElement("adblockplus-shownotifications", !Prefs.notifications_showui); - setChecked("adblockplus-shownotifications", Prefs.notifications_ignoredcategories.indexOf("*") == -1); - addCommandHandler("adblockplus-shownotifications", function() - { - Notification.toggleIgnoreCategory("*"); - this.value = (Prefs.notifications_ignoredcategories.indexOf("*") == -1); - }); - - let hasAcceptableAds = FilterStorage.subscriptions.some((subscription) => subscription instanceof DownloadableSubscription && - subscription.url == Prefs.subscriptions_exceptionsurl); - setChecked("adblockplus-acceptableAds", hasAcceptableAds); - addCommandHandler("adblockplus-acceptableAds", function() - { - this.value = UI.toggleAcceptableAds(); - }); - - setChecked("adblockplus-sync", syncEngine && syncEngine.enabled); - addCommandHandler("adblockplus-sync", function() - { - this.value = UI.toggleSync(); - }); - - setChecked("adblockplus-showintoolbar", UI.isToolbarIconVisible()); - addCommandHandler("adblockplus-showintoolbar", function() - { - UI.toggleToolbarIcon(); - this.value = UI.isToolbarIconVisible(); - }); - - let list = doc.getElementById("adblockplus-subscription-list"); - if (list) - { - // Load subscriptions data - let request = new XMLHttpRequest(); - request.mozBackgroundRequest = true; - request.open("GET", "chrome://adblockplus/content/ui/subscriptions.xml"); - request.addEventListener("load", function() - { - if (onShutdown.done) - return; - - let currentSubscription = FilterStorage.subscriptions.filter((subscription) => subscription instanceof DownloadableSubscription && - subscription.url != Prefs.subscriptions_exceptionsurl && - subscription.url != Prefs.subscriptions_antiadblockurl); - currentSubscription = (currentSubscription.length ? currentSubscription[0] : null); - - let subscriptions =request.responseXML.getElementsByTagName("subscription"); - for (let i = 0; i < subscriptions.length; i++) - { - let item = subscriptions[i]; - let url = item.getAttribute("url"); - if (!url) - continue; - - list.appendItem(item.getAttribute("title"), url, null); - if (currentSubscription && url == currentSubscription.url) - list.selectedIndex = list.itemCount - 1; - - if (currentSubscription && list.selectedIndex < 0) - { - list.appendItem(currentSubscription.title, currentSubscription.url, null); - list.selectedIndex = list.itemCount - 1; - } - } - - var listener = function() - { - if (list.value) - UI.setSubscription(list.value, list.label); - } - list.addEventListener("command", listener, false); - - // xul:menulist in Fennec is broken and doesn't trigger any events - // on selection. Have to detect selectIndex changes instead. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=891736 - list.watch("selectedIndex", function(prop, oldval, newval) - { - Utils.runAsync(listener); - return newval; - }); - }, false); - request.send(); - } - } - }, - - observe: function(subject, topic, data) - { - let {addonID} = require("info") - if (data != addonID) - return; - - this.initOptionsDoc(subject.QueryInterface(Ci.nsIDOMDocument)); - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]) -}; -optionsObserver.init(); - -/** - * Session restore observer instance, stored to prevent it from being garbage - * collected. - * @type SessionRestoreObserver - */ -let sessionRestoreObserver = null; - -/** - * Observer waiting for the browsing session to be restored on startup. - */ -function SessionRestoreObserver(/**function*/ callback) -{ - sessionRestoreObserver = this; - - this.callback = callback; - Services.obs.addObserver(this, "sessionstore-windows-restored", true); - - // Just in case, don't wait longer than 5 seconds - this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this.timer.init(this, 5000, Ci.nsITimer.TYPE_ONE_SHOT); -} -SessionRestoreObserver.prototype = -{ - callback: null, - timer: null, - observe: function(subject, topic, data) - { - Services.obs.removeObserver(this, "sessionstore-windows-restored"); - sessionRestoreObserver = null; - - this.timer.cancel(); - this.timer = null; - - if (!onShutdown.done) - this.callback(); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]) -} - -/** - * Timer used to delay notification handling. - * @type nsITimer - */ -let notificationTimer = null; - -let UI = exports.UI = -{ - /** - * Gets called on startup, initializes UI integration. - */ - init: function() - { - // We have to wait for multiple events before running start-up actions - let prerequisites = []; - - // Start loading overlay - prerequisites.push(new Promise((resolve, reject) => - { - let request = new XMLHttpRequest(); - request.mozBackgroundRequest = true; - request.open("GET", "chrome://adblockplus/content/ui/overlay.xul"); - request.channel.owner = Utils.systemPrincipal; - request.addEventListener("load", event => - { - if (onShutdown.done) - return; - - this.processOverlay(request.responseXML.documentElement); - - // Don't wait for the rest of the startup sequence, add icon already - this.addToolbarButton(); - - resolve(); - }, false); - - request.addEventListener("error", event => - { - reject(new Error("Unexpected: Failed to load overlay.xul")); - }); - - request.send(null); - })); - - // Wait for filters to load - if (!FilterStorage.initialized) - prerequisites.push(FilterNotifier.once("load")); - - // Wait for session to be restored - prerequisites.push(new Promise((resolve, reject) => - { - let window = this.currentWindow; - if (!window && "nsISessionStore" in Ci) - { - // No application windows yet, the application must be starting up. Wait - // for session to be restored before initializing our UI. - new SessionRestoreObserver(resolve); - } - else - resolve(); - })); - - Promise.all(prerequisites).then(() => this.initDone()) - .catch(e => Cu.reportError(e)); - }, - - /** - * Provesses overlay document data and initializes overlay property. - */ - processOverlay: function(/**Element*/ root) - { - Utils.splitAllLabels(root); - - let specialElements = {"abp-status-popup": true, "abp-status": true, "abp-toolbarbutton": true, "abp-menuitem": true, "abp-bottombar-container": true}; - - this.overlay = {all: []}; - - // Remove whitespace text nodes - let walker = root.ownerDocument.createTreeWalker( - root, Ci.nsIDOMNodeFilter.SHOW_TEXT, - (node) => !/\S/.test(node.nodeValue), false - ); - let whitespaceNodes = []; - while (walker.nextNode()) - whitespaceNodes.push(walker.currentNode); - - for (let i = 0; i < whitespaceNodes.length; i++) - whitespaceNodes[i].parentNode.removeChild(whitespaceNodes[i]); - - // Put overlay elements into appropriate fields - while (root.firstElementChild) - { - let child = root.firstElementChild; - if (child.getAttribute("id") in specialElements) - this.overlay[child.getAttribute("id")] = child; - else - this.overlay.all.push(child); - root.removeChild(child); - } - - // Read overlay attributes - this.overlay.attributes = {}; - for (let i = 0; i < root.attributes.length; i++) - this.overlay.attributes[root.attributes[i].name] = root.attributes[i].value; - - // Copy context menu into the toolbar icon and Tools menu item - function fixId(element, newId) - { - if (element.hasAttribute("id")) - element.setAttribute("id", element.getAttribute("id").replace("abp-status", newId)); - - for (let i = 0, len = element.children.length; i < len; i++) - fixId(element.children[i], newId); - - return element; - } - - if ("abp-status-popup" in this.overlay) - { - let menuSource = this.overlay["abp-status-popup"]; - delete this.overlay["abp-status-popup"]; - - if (this.overlay.all.length) - this.overlay.all[0].appendChild(menuSource); - if ("abp-toolbarbutton" in this.overlay) - this.overlay["abp-toolbarbutton"].appendChild(fixId(menuSource.cloneNode(true), "abp-toolbar")); - if ("abp-menuitem" in this.overlay) - this.overlay["abp-menuitem"].appendChild(fixId(menuSource.cloneNode(true), "abp-menuitem")); - } - }, - - /** - * Gets called once the initialization is finished and Adblock Plus elements - * can be added to the UI. - */ - initDone: function() - { - // The icon might be added already, make sure its state is correct - this.updateState(); - - // Listen for pref and filters changes - Prefs.addListener(name => - { - if (name == "enabled" || name == "defaulttoolbaraction" || name == "defaultstatusbaraction") - this.updateState(); - else if (name == "showinstatusbar") - { - for (let window of this.applicationWindows) - this.updateStatusbarIcon(window); - } - }); - - for (let eventName of [ - "filter.added", "filter.removed", "filter.disabled", - "subscription.added", "subscription.removed", "subscription.disabled", - "subscription.updated", "load" - ]) - { - FilterNotifier.on(eventName, () => this.updateState()); - } - - Notification.addShowListener(notification => - { - let window = this.currentWindow; - if (!window) - return; - - let button = window.document.getElementById("abp-toolbarbutton") - || window.document.getElementById("abp-status"); - if (!button) - return; - - this._showNotification(window, button, notification); - }); - - // Add "anti-adblock messages" notification - initAntiAdblockNotification(); - - // Initialize subscribe link handling - port.on("subscribeLinkClick", data => this.subscribeLinkClicked(data)); - - // Execute first-run actions if a window is open already, otherwise it - // will happen in applyToWindow() when a window is opened. - this.firstRunActions(this.currentWindow); - }, - - addToolbarButton: function() - { - let {WindowObserver} = require("windowObserver"); - new WindowObserver(this); - - let {defaultToolbarPosition} = require("appSupport"); - if ("abp-toolbarbutton" in this.overlay && defaultToolbarPosition) - { - try - { - ({CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm", null)); - } - catch (e) - { - // No built-in CustomizableUI API, use our own implementation. - ({CustomizableUI} = require("customizableUI")); - } - - CustomizableUI.createWidget({ - id: "abp-toolbarbutton", - type: "custom", - positionAttribute: "abp-iconposition", // For emulation only - defaultArea: defaultToolbarPosition.parent, - defaultBefore: defaultToolbarPosition.before, // For emulation only - defaultAfter: defaultToolbarPosition.after, // For emulation only - removable: true, - onBuild: function(document) - { - let node = document.importNode(this.overlay["abp-toolbarbutton"], true); - node.addEventListener("click", this.onIconClick, false); - node.addEventListener("command", this.onIconClick, false); - this.updateIconState(document.defaultView, node); - return node; - }.bind(this), - onAdded: function(node) - { - // For emulation only, this callback isn't part of the official - // CustomizableUI API. - this.updateIconState(node.ownerDocument.defaultView, node); - }.bind(this), - }); - onShutdown.add(CustomizableUI.destroyWidget.bind(CustomizableUI, "abp-toolbarbutton")); - } - }, - - firstRunActions: function(window) - { - if (this.firstRunDone || !window || !FilterStorage.initialized) - return; - - this.firstRunDone = true; - - let {addonVersion} = require("info"); - let prevVersion = Prefs.currentVersion; - if (prevVersion != addonVersion) - { - Prefs.currentVersion = addonVersion; - this.addSubscription(window, prevVersion); - - // The "Hide placeholders" option has been removed from the UI in 2.6.6.3881 - // So we reset the option for users updating from older versions. - if (prevVersion && Services.vc.compare(prevVersion, "2.6.6.3881") < 0) - Prefs.fastcollapse = false; - } - }, - - /** - * Will be set to true after the check whether first-run actions should run - * has been performed. - * @type Boolean - */ - firstRunDone: false, - - /** - * Initializes Adblock Plus UI in a window. - */ - applyToWindow: function(/**Window*/ window, /**Boolean*/ noDelay) - { - let {delayInitialization, isKnownWindow, getBrowser, addBrowserLocationListener} = require("appSupport"); - if (window.document.documentElement.id == "CustomizeToolbarWindow" || isKnownWindow(window)) - { - // Add style processing instruction - let style = window.document.createProcessingInstruction("xml-stylesheet", 'class="adblockplus-node" href="chrome://adblockplus/skin/overlay.css" type="text/css"'); - window.document.insertBefore(style, window.document.firstChild); - } - - if (!isKnownWindow(window)) - return; - - // Thunderbird windows will not be initialized at this point, execute - // delayed - if (!noDelay && delayInitialization) - { - Utils.runAsync(this.applyToWindow.bind(this, window, true)); - return; - } - - // Add general items to the document - for (let i = 0; i < this.overlay.all.length; i++) - window.document.documentElement.appendChild(this.overlay.all[i].cloneNode(true)); - - // Add status bar icon - this.updateStatusbarIcon(window); - - // Add tools menu item - if ("abp-menuitem" in this.overlay) - { - let {toolsMenu} = require("appSupport"); - let [parent, before] = this.resolveInsertionPoint(window, toolsMenu); - if (parent) - parent.insertBefore(this.overlay["abp-menuitem"].cloneNode(true), before); - } - - // Attach event handlers - for (let i = 0; i < eventHandlers.length; i++) - { - let [id, event, handler] = eventHandlers[i]; - let element = window.document.getElementById(id); - if (element) - element.addEventListener(event, handler.bind(null, window), false); - } - window.addEventListener("popupshowing", this.onPopupShowing, false); - window.addEventListener("keypress", this.onKeyPress, false); - - addBrowserLocationListener(window, function() - { - this.updateIconState(window, window.document.getElementById("abp-status")); - this.updateIconState(window, window.document.getElementById("abp-toolbarbutton")); - - Notification.showNext(this.getCurrentLocation(window).spec); - }.bind(this)); - - let notificationPanel = window.document.getElementById("abp-notification"); - notificationPanel.addEventListener("command", function(event) - { - switch (event.target.id) - { - case "abp-notification-close": - notificationPanel.classList.add("abp-closing"); - break; - case "abp-notification-optout": - Notification.toggleIgnoreCategory("*", true); - /* FALL THROUGH */ - case "abp-notification-hide": - notificationPanel.hidePopup(); - break; - } - }, false); - - // First-run actions? - this.firstRunActions(window); - - // Some people actually switch off browser.frames.enabled and are surprised - // that things stop working... - window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .allowSubframes = true; - }, - - /** - * Removes Adblock Plus UI from a window. - */ - removeFromWindow: function(/**Window*/ window) - { - let {isKnownWindow, removeBrowserLocationListeners} = require("appSupport"); - if (window.document.documentElement.id == "CustomizeToolbarWindow" || isKnownWindow(window)) - { - // Remove style processing instruction - for (let child = window.document.firstChild; child; child = child.nextSibling) - if (child.nodeType == child.PROCESSING_INSTRUCTION_NODE && child.data.indexOf("adblockplus-node") >= 0) - child.parentNode.removeChild(child); - } - - if (!isKnownWindow(window)) - return; - - for (let id in this.overlay) - { - if (id == "all") - { - let list = this.overlay[id]; - for (let i = 0; i < list.length; i++) - { - let clone = window.document.getElementById(list[i].getAttribute("id")); - if (clone) - clone.parentNode.removeChild(clone); - } - } - else - { - let clone = window.document.getElementById(id); - if (clone) - clone.parentNode.removeChild(clone); - } - } - - window.removeEventListener("popupshowing", this.onPopupShowing, false); - window.removeEventListener("keypress", this.onKeyPress, false); - removeBrowserLocationListeners(window); - }, - - /** - * The overlay information to be used when adding elements to the UI. - * @type Object - */ - overlay: null, - - /** - * Iterator for application windows that Adblock Plus should apply to. - * @type Iterator - */ - get applicationWindows() - { - let {isKnownWindow} = require("appSupport"); - - let enumerator = Services.wm.getZOrderDOMWindowEnumerator(null, true); - if (!enumerator.hasMoreElements()) - { - // On Linux the list returned will be empty, see bug 156333. Fall back to random order. - enumerator = Services.wm.getEnumerator(null); - } - - let generate = function*() - { - while (enumerator.hasMoreElements()) - { - let window = enumerator.getNext().QueryInterface(Ci.nsIDOMWindow); - if (isKnownWindow(window)) - yield window; - } - }; - - return generate(); - }, - - /** - * Returns the top-most application window or null if none exists. - * @type Window - */ - get currentWindow() - { - for (let window of this.applicationWindows) - return window; - return null; - }, - - /** - * Opens a URL in the browser window. If browser window isn't passed as parameter, - * this function attempts to find a browser window. If an event is passed in - * it should be passed in to the browser if possible (will e.g. open a tab in - * background depending on modifiers keys). - */ - loadInBrowser: function(/**String*/ url, /**Window*/ currentWindow, /**Event*/ event) - { - if (!currentWindow) - currentWindow = this.currentWindow; - - let {addTab} = require("appSupport"); - if (currentWindow && addTab) - addTab(currentWindow, url, event); - else - { - let protocolService = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService); - protocolService.loadURI(Services.io.newURI(url, null, null), null); - } - }, - - /** - * Opens a pre-defined documentation link in the browser window. This will - * send the UI language to adblockplus.org so that the correct language - * version of the page can be selected. - */ - loadDocLink: function(/**String*/ linkID, /**Window*/ window) - { - let link = Utils.getDocLink(linkID); - this.loadInBrowser(link, window); - }, - - - /** - * Brings up the filter composer dialog to block an item. The optional nodesID - * parameter must be a unique ID returned by - * RequestNotifier.storeNodesForEntry() or similar. - */ - blockItem: function(/**Window*/ window, /**string*/ nodesID, /**RequestEntry*/ item) - { - if (!item) - return; - - window.openDialog("chrome://adblockplus/content/ui/composer.xul", "_blank", - "chrome,centerscreen,resizable,dialog=no,dependent", nodesID, item); - }, - - /** - * Opens filter preferences dialog or focuses an already open dialog. - * @param {Filter} [filter] filter to be selected - */ - openFiltersDialog: function(filter) - { - let existing = Services.wm.getMostRecentWindow("abp:filters"); - if (existing) - { - try - { - existing.focus(); - } catch (e) {} - if (filter) - existing.SubscriptionActions.selectFilter(filter); - } - else - { - Services.ww.openWindow(null, "chrome://adblockplus/content/ui/filters.xul", "_blank", "chrome,centerscreen,resizable,dialog=no", {wrappedJSObject: filter}); - } - }, - - /** - * Opens report wizard for the current page. - */ - openReportDialog: function(/**Window*/ window) - { - let wnd = Services.wm.getMostRecentWindow("abp:sendReport"); - if (wnd) - wnd.focus(); - else - { - let uri = this.getCurrentLocation(window); - if (uri) - { - let {getBrowser} = require("appSupport"); - let browser = getBrowser(window); - if ("selectedBrowser" in browser) - browser = browser.selectedBrowser; - window.openDialog("chrome://adblockplus/content/ui/sendReport.xul", "_blank", "chrome,centerscreen,resizable=no", browser.outerWindowID, uri, browser); - } - } - }, - - /** - * Opens our contribution page. - */ - openContributePage: function(/**Window*/ window) - { - this.loadDocLink("contribute", window); - }, - - /** - * Executed on first run, adds a filter subscription and notifies that user - * about that. - */ - addSubscription: function(/**Window*/ window, /**String*/ prevVersion) - { - // Add "acceptable ads" subscription for new users and user updating from old ABP versions. - // Don't add it for users of privacy subscriptions (use a hardcoded list for now). - let addAcceptable = (Services.vc.compare(prevVersion, "2.0") < 0); - let privacySubscriptions = { - "https://easylist-downloads.adblockplus.org/easyprivacy+easylist.txt": true, - "https://easylist-downloads.adblockplus.org/easyprivacy.txt": true, - "https://secure.fanboy.co.nz/fanboy-tracking.txt": true, - "https://fanboy-adblock-list.googlecode.com/hg/fanboy-adblocklist-stats.txt": true, - "https://bitbucket.org/fanboy/fanboyadblock/raw/tip/fanboy-adblocklist-stats.txt": true, - "https://hg01.codeplex.com/fanboyadblock/raw-file/tip/fanboy-adblocklist-stats.txt": true, - "https://adversity.googlecode.com/hg/Adversity-Tracking.txt": true - }; - if (FilterStorage.subscriptions.some((subscription) => subscription.url == Prefs.subscriptions_exceptionsurl || subscription.url in privacySubscriptions)) - addAcceptable = false; - - // Don't add subscription if the user has a subscription already - let addSubscription = true; - //if (FilterStorage.subscriptions.some((subscription) => subscription instanceof DownloadableSubscription && subscription.url != Prefs.subscriptions_exceptionsurl)) - addSubscription = false; - - // If this isn't the first run, only add subscription if the user has no custom filters - if (addSubscription && Services.vc.compare(prevVersion, "0.0") > 0) - { - if (FilterStorage.subscriptions.some((subscription) => subscription.url != Prefs.subscriptions_exceptionsurl && subscription.filters.length)) - addSubscription = false; - } - - // Add "acceptable ads" subscription - if (false) - { - let subscription = Subscription.fromURL(Prefs.subscriptions_exceptionsurl); - if (subscription) - { - subscription.title = "Allow non-intrusive advertising"; - FilterStorage.addSubscription(subscription); - if (subscription instanceof DownloadableSubscription && !subscription.lastDownload) - Synchronizer.execute(subscription); - } - else - addAcceptable = false; - } - - /* Add "anti-adblock messages" subscription for new users and users updating from old ABP versions - if (Services.vc.compare(prevVersion, "2.5") < 0) - { - let subscription = Subscription.fromURL(Prefs.subscriptions_antiadblockurl); - if (subscription && !(subscription.url in FilterStorage.knownSubscriptions)) - { - subscription.disabled = true; - FilterStorage.addSubscription(subscription); - if (subscription instanceof DownloadableSubscription && !subscription.lastDownload) - Synchronizer.execute(subscription); - } - }*/ - - // Extra subsriptions - let subscription = Subscription.fromURL("http://gnuzilla.gnu.org/filters/blacklist.txt"); - subscription.disabled = false; - FilterStorage.addSubscription(subscription); - Synchronizer.execute(subscription); - - subscription = Subscription.fromURL("http://gnuzilla.gnu.org/filters/third-party.txt"); - subscription.disabled = false; - FilterStorage.addSubscription(subscription); - Synchronizer.execute(subscription); - - if (!addSubscription && !addAcceptable) - return; - - function notifyUser() - {return; - - let {addTab} = require("appSupport"); - if (addTab) - { - addTab(window, "chrome://adblockplus/content/ui/firstRun.html"); - } - else - { - let dialogSource = '\ - <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>\ - <dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="document.title=content.document.title" buttons="accept" width="500" height="600">\ - <iframe type="content-primary" flex="1" src="chrome://adblockplus/content/ui/firstRun.html"/>\ - </dialog>'; - Services.ww.openWindow(window, - "data:application/vnd.mozilla.xul+xml," + encodeURIComponent(dialogSource), - "_blank", "chrome,centerscreen,resizable,dialog=no", null); - } - } - - if (addSubscription) - { - // Load subscriptions data - let request = new XMLHttpRequest(); - request.mozBackgroundRequest = true; - request.open("GET", "chrome://adblockplus/content/ui/subscriptions.xml"); - request.addEventListener("load", function() - { - if (onShutdown.done) - return; - - let node = Utils.chooseFilterSubscription(request.responseXML.getElementsByTagName("subscription")); - let subscription = (node ? Subscription.fromURL(node.getAttribute("url")) : null); - if (subscription) - { - FilterStorage.addSubscription(subscription); - subscription.disabled = false; - subscription.title = node.getAttribute("title"); - subscription.homepage = node.getAttribute("homepage"); - if (subscription instanceof DownloadableSubscription && !subscription.lastDownload) - Synchronizer.execute(subscription); - - notifyUser(); - } - }, false); - request.send(); - } - else - notifyUser(); - }, - - /** - * Called whenever child/subscribeLinks module intercepts clicks on abp: links - * as well as links to subscribe.adblockplus.org. - */ - subscribeLinkClicked: function({title, url, - mainSubscriptionTitle, mainSubscriptionURL}) - { - if (!url) - return; - - // Default title to the URL - if (!title) - title = url; - - // Main subscription needs both title and URL - if (mainSubscriptionTitle && !mainSubscriptionURL) - mainSubscriptionTitle = null; - if (mainSubscriptionURL && !mainSubscriptionTitle) - mainSubscriptionURL = null; - - // Trim spaces in title and URL - title = title.trim(); - url = url.trim(); - if (mainSubscriptionURL) - { - mainSubscriptionTitle = mainSubscriptionTitle.trim(); - mainSubscriptionURL = mainSubscriptionURL.trim(); - } - - // Verify that the URL is valid - url = Utils.makeURI(url); - if (!url || (url.scheme != "http" && url.scheme != "https" && url.scheme != "ftp")) - return; - url = url.spec; - - if (mainSubscriptionURL) - { - mainSubscriptionURL = Utils.makeURI(mainSubscriptionURL); - if (!mainSubscriptionURL || (mainSubscriptionURL.scheme != "http" && mainSubscriptionURL.scheme != "https" && mainSubscriptionURL.scheme != "ftp")) - mainSubscriptionURL = mainSubscriptionTitle = null; - else - mainSubscriptionURL = mainSubscriptionURL.spec; - } - - this.openSubscriptionDialog(this.currentWindow, url, title, mainSubscriptionURL, mainSubscriptionTitle); - }, - - /** - * Opens a dialog letting the user confirm/adjust a filter subscription to - * be added. - */ - openSubscriptionDialog: function(/**Window*/ window, /**String*/ url, /**String*/ title, /**String*/ mainURL, /**String*/ mainTitle) - { - let subscription = {url: url, title: title, disabled: false, external: false, - mainSubscriptionTitle: mainTitle, mainSubscriptionURL: mainURL}; - window.openDialog("chrome://adblockplus/content/ui/subscriptionSelection.xul", "_blank", - "chrome,centerscreen,resizable,dialog=no", subscription, null); - }, - - /** - * Retrieves the current location of the browser. - */ - getCurrentLocation: function(/**Window*/ window) /**nsIURI*/ - { - let {getCurrentLocation} = require("appSupport"); - let result = getCurrentLocation(window); - return (result ? Utils.unwrapURL(result) : null); - }, - - /** - * Looks up an element with given ID in the window. If a list of IDs is given - * will try all of them until an element exists. - */ - findElement: function(/**Window*/ window, /**String|String[]*/ id) /**Element*/ - { - if (id instanceof Array) - { - for (let candidate of id) - { - let result = window.document.getElementById(candidate); - if (result) - return result; - } - return null; - } - else - return window.document.getElementById(id); - }, - - /** - * Resolves an insertion point as specified in appSupport module. Returns - * two elements: the parent element and the element to insert before. - */ - resolveInsertionPoint: function(/**Window*/ window, /**Object*/ insertionPoint) /**Element[]*/ - { - let parent = null; - let before = null; - if (insertionPoint) - { - if ("parent" in insertionPoint) - parent = this.findElement(window, insertionPoint.parent); - - if (parent && "before" in insertionPoint) - before = this.findElement(window, insertionPoint.before); - - if (parent && !before && "after" in insertionPoint) - { - let after = this.findElement(window, insertionPoint.after); - if (after) - before = after.nextElementSibling; - } - - if (before && before.parentNode != parent) - before = null; - } - - return [parent, before]; - }, - - /** - * Toggles visibility state of the toolbar icon. - */ - toggleToolbarIcon: function() - { - if (!CustomizableUI) - return; - if (this.isToolbarIconVisible()) - CustomizableUI.removeWidgetFromArea("abp-toolbarbutton"); - else - { - let {defaultToolbarPosition} = require("appSupport"); - CustomizableUI.addWidgetToArea("abp-toolbarbutton", defaultToolbarPosition.parent); - } - }, - - /** - * Updates Adblock Plus icon state for all windows. - */ - updateState: function() - { - for (let window of this.applicationWindows) - { - this.updateIconState(window, window.document.getElementById("abp-status")); - this.updateIconState(window, window.document.getElementById("abp-toolbarbutton")); - } - }, - - /** - * Updates Adblock Plus icon state for a single application window. - */ - updateIconState: function(/**Window*/ window, /**Element*/ icon) - { - if (!icon) - return; - - let state = (Prefs.enabled ? "active" : "disabled"); - if (state == "active") - { - let location = this.getCurrentLocation(window); - if (location && Policy.isWhitelisted(location.spec)) - state = "whitelisted"; - } - - let popupId = "abp-status-popup"; - if (icon.localName == "statusbarpanel") - { - if (Prefs.defaultstatusbaraction == 0) - { - icon.setAttribute("popup", popupId); - icon.removeAttribute("context"); - } - else - { - icon.removeAttribute("popup"); - icon.setAttribute("context", popupId); - } - } - else - { - if (Prefs.defaulttoolbaraction == 0) - { - icon.setAttribute("type", "menu"); - icon.removeAttribute("context"); - } - else - { - icon.setAttribute("type", "menu-button"); - icon.setAttribute("context", popupId); - } - } - - icon.setAttribute("abpstate", state); - }, - - /** - * Shows or hides status bar icons in all windows, according to pref. - */ - updateStatusbarIcon: function(/**Window*/ window) - { - if (!("abp-status" in this.overlay)) - return; - - let {statusbarPosition} = require("appSupport"); - if (!statusbarPosition) - return; - - let icon = window.document.getElementById("abp-status"); - if (Prefs.showinstatusbar && !icon) - { - let [parent, before] = this.resolveInsertionPoint(window, statusbarPosition); - if (!parent) - return; - - parent.insertBefore(this.overlay["abp-status"].cloneNode(true), before); - - icon = window.document.getElementById("abp-status"); - this.updateIconState(window, icon); - icon.addEventListener("click", this.onIconClick, false); - } - else if (!Prefs.showinstatusbar && icon) - icon.parentNode.removeChild(icon); - }, - - /** - * Toggles the value of a boolean preference. - */ - togglePref: function(/**String*/ pref) - { - Prefs[pref] = !Prefs[pref]; - }, - - /** - * If the given filter is already in user's list, removes it from the list. Otherwise adds it. - */ - toggleFilter: function(/**Filter*/ filter) - { - if (filter.subscriptions.length) - { - if (filter.disabled || filter.subscriptions.some((subscription) => !(subscription instanceof SpecialSubscription))) - filter.disabled = !filter.disabled; - else - FilterStorage.removeFilter(filter); - } - else - { - filter.disabled = false; - FilterStorage.addFilter(filter); - } - }, - - - /** - * Toggles "Count filter hits" option. - */ - toggleSaveStats: function(window) - { - if (Prefs.savestats) - { - if (!Utils.confirm(window, Utils.getString("clearStats_warning"))) - return; - - FilterStorage.resetHitCounts(); - Prefs.savestats = false; - } - else - Prefs.savestats = true; - }, - - /** - * Sets the current filter subscription in a single-subscription scenario, - * all other subscriptions will be removed. - */ - setSubscription: function(url, title) - { - let subscription = Subscription.fromURL(url); - let currentSubscriptions = FilterStorage.subscriptions.filter( - ((subscription) => subscription instanceof DownloadableSubscription && subscription.url != Prefs.subscriptions_exceptionsurl) - ); - if (!subscription || currentSubscriptions.indexOf(subscription) >= 0) - return; - - for (let i = 0; i < currentSubscriptions.length; i++) - FilterStorage.removeSubscription(currentSubscriptions[i]); - - subscription.title = title; - FilterStorage.addSubscription(subscription); - if (subscription instanceof DownloadableSubscription && !subscription.lastDownload) - Synchronizer.execute(subscription); - }, - - /** - * Adds or removes "non-intrisive ads" filter list. - * @return {Boolean} true if the filter list has been added - **/ - toggleAcceptableAds: function() - { - let subscription = Subscription.fromURL(Prefs.subscriptions_exceptionsurl); - if (!subscription) - return false; - - subscription.disabled = false; - subscription.title = "Allow non-intrusive advertising"; - if (subscription.url in FilterStorage.knownSubscriptions) - FilterStorage.removeSubscription(subscription); - else - { - FilterStorage.addSubscription(subscription); - if (subscription instanceof DownloadableSubscription && !subscription.lastDownload) - Synchronizer.execute(subscription); - } - - return (subscription.url in FilterStorage.knownSubscriptions); - }, - - /** - * Toggles the pref for the Adblock Plus sync engine. - * @return {Boolean} new state of the sync engine - */ - toggleSync: function() - { - let {Sync} = require("sync"); - let syncEngine = Sync.getEngine(); - if (syncEngine) - { - syncEngine.enabled = !syncEngine.enabled; - return syncEngine.enabled; - } - else - return false; - }, - - /** - * Tests whether blockable items list is currently open. - */ - isBottombarOpen: function(/**Window*/ window) /**Boolean*/ - { - if (detachedBottombar && !detachedBottombar.closed) - return true; - - return !!window.document.getElementById("abp-bottombar"); - }, - - /** - * Called when some pop-up in the application window shows up, initializes - * pop-ups related to Adblock Plus. - */ - onPopupShowing: function(/**Event*/ event) - { - if (event.defaultPrevented) - return; - - let popup = event.originalTarget; - - let {contentContextMenu} = require("appSupport"); - if ((typeof contentContextMenu == "string" && popup.id == contentContextMenu) || - (contentContextMenu instanceof Array && contentContextMenu.indexOf(popup.id) >= 0)) - { - this.fillContentContextMenu(popup); - } - else if (popup.id == "abp-tooltip") - this.fillIconTooltip(event, popup.ownerDocument.defaultView); - else - { - let match = /^(abp-(?:toolbar|status|menuitem)-)popup$/.exec(popup.id); - if (match) - this.fillIconMenu(event, popup.ownerDocument.defaultView, match[1]); - } - }, - - /** - * Handles click on toolbar and status bar icons. - */ - onIconClick: function(/**Event*/ event) - { - if (event.eventPhase != event.AT_TARGET) - return; - - let isToolbar = (event.target.localName != "statusbarpanel"); - let action = 0; - if ((isToolbar && event.type == "command") || (!isToolbar && event.button == 0)) - action = (isToolbar ? Prefs.defaulttoolbaraction : Prefs.defaultstatusbaraction); - else if (event.button == 1) - action = 3; - - let window = event.target.ownerDocument.defaultView; - if (action == 1) - this.toggleBottombar(window); - else if (action == 2) - this.openFiltersDialog(); - else if (action == 3) - { - // If there is a whitelisting rule for current page - remove it (reenable). - // Otherwise flip "enabled" pref. - if (!this.removeWhitelist(window)) - this.togglePref("enabled"); - } - }, - - /** - * Removes/disables the exception rule applying for the current page. - */ - removeWhitelist: function(/**Window*/ window) - { - let location = this.getCurrentLocation(window); - let filter = null; - if (location) - filter = Policy.isWhitelisted(location.spec); - if (filter && filter.subscriptions.length && !filter.disabled) - { - UI.toggleFilter(filter); - return true; - } - return false; - }, - - /** - * Updates state of the icon tooltip. - */ - fillIconTooltip: function(/**Event*/ event, /**Window*/ window) - { - let E = (id) => window.document.getElementById(id); - - let node = window.document.tooltipNode; - if (!node || !node.hasAttribute("tooltip")) - { - event.preventDefault(); - return; - } - - // Prevent tooltip from overlapping menu - for (let id of ["abp-toolbar-popup", "abp-status-popup"]) - { - let element = E(id); - if (element && element.state == "open") - { - event.preventDefault(); - return; - } - } - - let type = (node.id == "abp-toolbarbutton" ? "toolbar" : "statusbar"); - let action = parseInt(Prefs["default" + type + "action"]); - if (isNaN(action)) - action = -1; - - let actionDescr = E("abp-tooltip-action"); - actionDescr.hidden = (action < 0 || action > 3); - if (!actionDescr.hidden) - actionDescr.setAttribute("value", Utils.getString("action" + action + "_tooltip")); - - let statusDescr = E("abp-tooltip-status"); - let state = node.getAttribute("abpstate"); - let statusStr = Utils.getString(state + "_tooltip"); - if (state == "active") - { - let [activeSubscriptions, activeFilters] = FilterStorage.subscriptions.reduce(function([subscriptions, filters], current) - { - if (current instanceof SpecialSubscription) - return [subscriptions, filters + current.filters.filter((filter) => !filter.disabled).length]; - else if (!current.disabled && !(Prefs.subscriptions_exceptionscheckbox && current.url == Prefs.subscriptions_exceptionsurl)) - return [subscriptions + 1, filters]; - else - return [subscriptions, filters] - }, [0, 0]); - - statusStr = statusStr.replace(/\?1\?/, activeSubscriptions).replace(/\?2\?/, activeFilters); - } - statusDescr.setAttribute("value", statusStr); - - E("abp-tooltip-blocked-label").hidden = true; - E("abp-tooltip-blocked").hidden = true; - E("abp-tooltip-filters-label").hidden = true; - E("abp-tooltip-filters").hidden = true; - E("abp-tooltip-more-filters").hidden = true; - - if (state == "active") - { - let {getBrowser} = require("appSupport"); - let browser = getBrowser(window); - if ("selectedBrowser" in browser) - browser = browser.selectedBrowser; - let outerWindowID = browser.outerWindowID; - RequestNotifier.getWindowStatistics(outerWindowID, (stats) => - { - E("abp-tooltip-blocked-label").hidden = false; - E("abp-tooltip-blocked").hidden = false; - - let blockedStr = Utils.getString("blocked_count_tooltip"); - blockedStr = blockedStr.replace(/\?1\?/, stats ? stats.blocked : 0).replace(/\?2\?/, stats ? stats.items : 0); - - if (stats && stats.whitelisted + stats.hidden) - { - blockedStr += " " + Utils.getString("blocked_count_addendum"); - blockedStr = blockedStr.replace(/\?1\?/, stats.whitelisted).replace(/\?2\?/, stats.hidden); - } - - E("abp-tooltip-blocked").setAttribute("value", blockedStr); - - let activeFilters = []; - if (stats) - { - let filterSort = function(a, b) - { - return stats.filters[b] - stats.filters[a]; - }; - for (let filter in stats.filters) - activeFilters.push(filter); - activeFilters = activeFilters.sort(filterSort); - } - - if (activeFilters.length > 0) - { - let filtersContainer = E("abp-tooltip-filters"); - while (filtersContainer.firstChild) - filtersContainer.removeChild(filtersContainer.firstChild); - - for (let i = 0; i < activeFilters.length && i < 3; i++) - { - let descr = filtersContainer.ownerDocument.createElement("description"); - descr.setAttribute("value", activeFilters[i] + " (" + stats.filters[activeFilters[i]] + ")"); - filtersContainer.appendChild(descr); - } - } - - E("abp-tooltip-filters-label").hidden = (activeFilters.length == 0); - E("abp-tooltip-filters").hidden = (activeFilters.length == 0); - E("abp-tooltip-more-filters").hidden = (activeFilters.length <= 3); - }); - } - }, - - /** - * Updates state of the icon context menu. - */ - fillIconMenu: function(/**Event*/ event, /**Window*/ window, /**String*/ prefix) - { - function hideElement(id, hide) - { - let element = window.document.getElementById(id); - if (element) - element.hidden = hide; - } - function setChecked(id, checked) - { - let element = window.document.getElementById(id); - if (element) - element.setAttribute("checked", checked); - } - function setDisabled(id, disabled) - { - let element = window.document.getElementById(id); - if (element) - element.setAttribute("disabled", disabled); - } - function setDefault(id, isDefault) - { - let element = window.document.getElementById(id); - if (element) - element.setAttribute("default", isDefault); - } - function generateLabel(id, param) - { - let element = window.document.getElementById(id); - if (element) - element.setAttribute("label", element.getAttribute("labeltempl").replace(/\?1\?/, param)); - } - - let bottombarOpen = this.isBottombarOpen(window); - hideElement(prefix + "openbottombar", bottombarOpen); - hideElement(prefix + "closebottombar", !bottombarOpen); - - hideElement(prefix + "whitelistsite", true); - hideElement(prefix + "whitelistpage", true); - - let location = this.getCurrentLocation(window); - if (location && Policy.isBlockableScheme(location)) - { - let host = null; - try - { - host = location.host.replace(/^www\./, ""); - } catch (e) {} - - if (host) - { - let ending = "|"; - location = location.clone(); - if (location instanceof Ci.nsIURL) - location.ref = ""; - if (location instanceof Ci.nsIURL && location.query) - { - location.query = ""; - ending = "?"; - } - - siteWhitelist = Filter.fromText("@@||" + host + "^$document"); - setChecked(prefix + "whitelistsite", siteWhitelist.subscriptions.length && !siteWhitelist.disabled); - generateLabel(prefix + "whitelistsite", host); - hideElement(prefix + "whitelistsite", false); - - pageWhitelist = Filter.fromText("@@|" + location.spec + ending + "$document"); - setChecked(prefix + "whitelistpage", pageWhitelist.subscriptions.length && !pageWhitelist.disabled); - hideElement(prefix + "whitelistpage", false); - } - else - { - siteWhitelist = Filter.fromText("@@|" + location.spec + "|"); - setChecked(prefix + "whitelistsite", siteWhitelist.subscriptions.length && !siteWhitelist.disabled); - generateLabel(prefix + "whitelistsite", location.spec.replace(/^mailto:/, "")); - hideElement(prefix + "whitelistsite", false); - } - } - - setDisabled("abp-command-sendReport", !location || !Policy.isBlockableScheme(location) || location.scheme == "mailto"); - - setChecked(prefix + "disabled", !Prefs.enabled); - setChecked(prefix + "frameobjects", Prefs.frameobjects); - setChecked(prefix + "savestats", Prefs.savestats); - - let {defaultToolbarPosition, statusbarPosition} = require("appSupport"); - let hasToolbar = defaultToolbarPosition; - let hasStatusBar = statusbarPosition; - hideElement(prefix + "showintoolbar", !hasToolbar || prefix == "abp-toolbar-"); - hideElement(prefix + "showinstatusbar", !hasStatusBar); - hideElement(prefix + "shownotifications", !Prefs.notifications_showui); - hideElement(prefix + "iconSettingsSeparator", (prefix == "abp-toolbar-" || !hasToolbar) && !hasStatusBar); - - setChecked(prefix + "showintoolbar", this.isToolbarIconVisible()); - setChecked(prefix + "showinstatusbar", Prefs.showinstatusbar); - setChecked(prefix + "shownotifications", Prefs.notifications_ignoredcategories.indexOf("*") == -1); - - let {Sync} = require("sync"); - let syncEngine = Sync.getEngine(); - hideElement(prefix + "sync", !syncEngine); - setChecked(prefix + "sync", syncEngine && syncEngine.enabled); - - let defAction = (!window.document.popupNode || window.document.popupNode.id == "abp-toolbarbutton" ? - Prefs.defaulttoolbaraction : - Prefs.defaultstatusbaraction); - setDefault(prefix + "openbottombar", defAction == 1); - setDefault(prefix + "closebottombar", defAction == 1); - setDefault(prefix + "filters", defAction == 2); - setDefault(prefix + "disabled", defAction == 3); - - let popup = window.document.getElementById(prefix + "popup"); - let items = (popup ? popup.querySelectorAll('menuitem[key]') : []); - for (let i = 0; i < items.length; i++) - { - let item = items[i]; - let match = /^abp-key-/.exec(item.getAttribute("key")); - if (!match) - continue; - - let name = match.input.substr(match.index + match[0].length); - if (!this.hotkeys) - this.configureKeys(window); - if (name in this.hotkeys) - { - let text = KeySelector.getTextForKey(this.hotkeys[name]); - if (text) - item.setAttribute("acceltext", text); - else - item.removeAttribute("acceltext"); - } - } - - hideElement(prefix + "contributebutton", Prefs.hideContributeButton); - }, - - /** - * Adds Adblock Plus menu items to the content area context menu when it shows - * up. - */ - fillContentContextMenu: function(/**Element*/ popup) - { - let window = popup.ownerDocument.defaultView; - let data = window.gContextMenuContentData; - if (!data) - { - // This is SeaMonkey Mail or Thunderbird, they won't get context menu data - // for us. Send the notification ourselves. - data = { - event: {target: popup.triggerNode}, - addonInfo: {}, - get wrappedJSObject() {return this;} - }; - Services.obs.notifyObservers(data, "AdblockPlus:content-contextmenu", null); - } - - if (typeof data.addonInfo != "object" || typeof data.addonInfo.adblockplus != "object") - return; - - let items = data.addonInfo.adblockplus; - let clicked = null; - let menuItems = []; - - function menuItemTriggered(id, nodeData) - { - clicked = id; - this.blockItem(window, id, nodeData); - } - - for (let [id, nodeData] of items) - { - let type = nodeData.type.toLowerCase(); - let label = this.overlay.attributes[type + "contextlabel"]; - if (!label) - return; - - let item = popup.ownerDocument.createElement("menuitem"); - item.setAttribute("label", label); - item.setAttribute("class", "abp-contextmenuitem"); - item.addEventListener("command", menuItemTriggered.bind(this, id, nodeData), false); - popup.appendChild(item); - - menuItems.push(item); - } - - // Add "Remove exception" menu item if necessary - let location = this.getCurrentLocation(window); - let filter = (location ? Policy.isWhitelisted(location.spec) : null); - if (filter && filter.subscriptions.length && !filter.disabled) - { - let label = this.overlay.attributes.whitelistcontextlabel; - if (!label) - return; - - let item = popup.ownerDocument.createElement("menuitem"); - item.setAttribute("label", label); - item.setAttribute("class", "abp-contextmenuitem"); - item.addEventListener("command", this.toggleFilter.bind(this, filter), false); - popup.appendChild(item); - - menuItems.push(item); - } - - // Make sure to clean up everything once the context menu is closed - let cleanUp = function(event) - { - if (event.eventPhase != event.AT_TARGET) - return; - - popup.removeEventListener("popuphidden", cleanUp, false); - for (let menuItem of menuItems) - if (menuItem.parentNode) - menuItem.parentNode.removeChild(menuItem); - - for (let [id, nodeData] of items) - if (id && id != clicked) - Policy.deleteNodes(id); - }.bind(this); - popup.addEventListener("popuphidden", cleanUp, false); - }, - - /** - * Called when the user presses a key in the application window, reacts to our - * shortcut keys. - */ - onKeyPress: function(/**Event*/ event) - { - if (!this.hotkeys) - this.configureKeys(event.currentTarget); - - for (let key in this.hotkeys) - { - if (KeySelector.matchesKey(event, this.hotkeys[key])) - { - event.preventDefault(); - let command = event.currentTarget.document.getElementById("abp-command-" + key); - if (command) - command.doCommand(); - } - } - }, - - /** - * Checks whether the toolbar icon is currently displayed. - */ - isToolbarIconVisible: function() /**Boolean*/ - { - if (!CustomizableUI) - return false; - let placement = CustomizableUI.getPlacementOfWidget("abp-toolbarbutton"); - return !!placement; - }, - - /** - * Stores the selected hotkeys, initialized when the user presses a key. - */ - hotkeys: null, - - /** - * Chooses shortcut keys that are available in the window according to - * preferences. - */ - configureKeys: function(/**Window*/ window) - { - let selector = new KeySelector(window); - - this.hotkeys = {}; - for (let name in Prefs) - { - let match = /_key$/.exec(name); - if (match && typeof Prefs[name] == "string") - { - let keyName = match.input.substr(0, match.index); - this.hotkeys[keyName] = selector.selectKey(Prefs[name]); - } - } - }, - - /** - * Toggles open/closed state of the blockable items list. - */ - toggleBottombar: function(/**Window*/ window) - { - if (detachedBottombar && !detachedBottombar.closed) - { - detachedBottombar.close(); - detachedBottombar = null; - } - else - { - let {addBottomBar, removeBottomBar, getBrowser} = require("appSupport"); - let mustDetach = !addBottomBar || !removeBottomBar || !("abp-bottombar-container" in this.overlay); - let detach = mustDetach || Prefs.detachsidebar; - if (!detach && window.document.getElementById("abp-bottombar")) - { - removeBottomBar(window); - - let browser = (getBrowser ? getBrowser(window) : null); - if (browser && "selectedBrowser" in browser) - browser = browser.selectedBrowser; - if (browser) - browser.focus(); - } - else if (!detach) - { - addBottomBar(window, this.overlay["abp-bottombar-container"]); - let element = window.document.getElementById("abp-bottombar"); - if (element) - { - element.setAttribute("width", Prefs.blockableItemsSize.width); - element.setAttribute("height", Prefs.blockableItemsSize.height); - - let splitter = window.document.getElementById("abp-bottombar-splitter"); - if (splitter) - { - splitter.addEventListener("command", function() - { - Prefs.blockableItemsSize = {width: element.width, height: element.height}; - }, false); - } - } - } - else - detachedBottombar = window.openDialog("chrome://adblockplus/content/ui/sidebarDetached.xul", "_blank", "chrome,resizable,dependent,dialog=no", mustDetach); - } - }, - - /** - * Hide contribute button and persist this choice. - */ - hideContributeButton: function(/**Window*/ window) - { - Prefs.hideContributeButton = true; - - for (let id of ["abp-status-contributebutton", "abp-toolbar-contributebutton", "abp-menuitem-contributebutton"]) - { - let button = window.document.getElementById(id); - if (button) - button.hidden = true; - } - }, - - _showNotification: function(window, button, notification) - { - let panel = window.document.getElementById("abp-notification"); - if (panel.state !== "closed") - return; - - function insertMessage(element, text, links) - { - let match = /^(.*?)<(a|strong)>(.*?)<\/\2>(.*)$/.exec(text); - if (!match) - { - element.appendChild(window.document.createTextNode(text)); - return; - } - - let [_, before, tagName, value, after] = match; - - insertMessage(element, before, links); - - let newElement = window.document.createElementNS("http://www.w3.org/1999/xhtml", tagName); - if (tagName === "a" && links && links.length) - newElement.setAttribute("href", links.shift()); - insertMessage(newElement, value, links); - element.appendChild(newElement); - - insertMessage(element, after, links); - } - - let texts = Notification.getLocalizedTexts(notification); - let titleElement = window.document.getElementById("abp-notification-title"); - titleElement.textContent = texts.title; - let messageElement = window.document.getElementById("abp-notification-message"); - messageElement.innerHTML = ""; - let docLinks = []; - if (notification.links) - for (let link of notification.links) - docLinks.push(Utils.getDocLink(link)); - - insertMessage(messageElement, texts.message, docLinks); - - messageElement.addEventListener("click", function(event) - { - let link = event.target; - while (link && link !== messageElement && link.localName !== "a") - link = link.parentNode; - if (!link || link.localName !== "a") - return; - event.preventDefault(); - event.stopPropagation(); - this.loadInBrowser(link.href, window); - }.bind(this)); - - if (notification.type === "question") - { - function buttonHandler(approved, event) - { - event.preventDefault(); - event.stopPropagation(); - panel.hidePopup(); - Notification.triggerQuestionListeners(notification.id, approved) - Notification.markAsShown(notification.id); - } - window.document.getElementById("abp-notification-yes").onclick = buttonHandler.bind(null, true); - window.document.getElementById("abp-notification-no").onclick = buttonHandler.bind(null, false); - } - else - Notification.markAsShown(notification.id); - - panel.setAttribute("class", "abp-" + notification.type); - panel.setAttribute("noautohide", true); - panel.openPopup(button, "bottomcenter topcenter", 0, 0, false, false, null); - } -}; -UI.onPopupShowing = UI.onPopupShowing.bind(UI); -UI.onKeyPress = UI.onKeyPress.bind(UI); -UI.onIconClick = UI.onIconClick.bind(UI); -UI.init(); - -/** - * List of event handers to be registered for each window. For each event - * handler the element ID, event and the actual event handler are listed. - * @type Array - */ -let eventHandlers = [ - ["abp-command-sendReport", "command", UI.openReportDialog.bind(UI)], - ["abp-command-filters", "command", UI.openFiltersDialog.bind(UI)], - ["abp-command-sidebar", "command", UI.toggleBottombar.bind(UI)], - ["abp-command-togglesitewhitelist", "command", function() { UI.toggleFilter(siteWhitelist); }], - ["abp-command-togglepagewhitelist", "command", function() { UI.toggleFilter(pageWhitelist); }], - ["abp-command-toggleobjtabs", "command", UI.togglePref.bind(UI, "frameobjects")], - ["abp-command-togglesavestats", "command", UI.toggleSaveStats.bind(UI)], - ["abp-command-togglesync", "command", UI.toggleSync.bind(UI)], - ["abp-command-toggleshowintoolbar", "command", UI.toggleToolbarIcon.bind(UI)], - ["abp-command-toggleshowinstatusbar", "command", UI.togglePref.bind(UI, "showinstatusbar")], - ["abp-command-enable", "command", UI.togglePref.bind(UI, "enabled")], - ["abp-command-contribute", "command", UI.openContributePage.bind(UI)], - ["abp-command-contribute-hide", "command", UI.hideContributeButton.bind(UI)], - ["abp-command-toggleshownotifications", "command", Notification.toggleIgnoreCategory.bind(Notification, "*", null)] -]; - -onShutdown.add(function() -{ - for (let window of UI.applicationWindows) - if (UI.isBottombarOpen(window)) - UI.toggleBottombar(window); -}); diff --git a/data/extensions/spyblock@gnu.org/lib/utils.js b/data/extensions/spyblock@gnu.org/lib/utils.js deleted file mode 100644 index 7b07041..0000000 --- a/data/extensions/spyblock@gnu.org/lib/utils.js +++ /dev/null @@ -1,751 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 Module containing a bunch of utility functions. - */ - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -let sidebarParams = null; - -/** - * Provides a bunch of utility functions. - * @class - */ -let Utils = exports.Utils = -{ - /** - * Returns the add-on ID used by Adblock Plus - */ - get addonID() - { - let {addonID} = require("info"); - return addonID; - }, - - /** - * Returns the installed Adblock Plus version - */ - get addonVersion() - { - let {addonVersion} = require("info"); - return addonVersion; - }, - - /** - * Returns whether we are running in Fennec, for Fennec-specific hacks - * @type Boolean - */ - get isFennec() - { - let {application} = require("info"); - let result = (application == "fennec" || application == "fennec2" || application == "icecatmobile"); - Utils.__defineGetter__("isFennec", () => result); - return result; - }, - - /** - * Returns the user interface locale selected for adblockplus chrome package. - */ - get appLocale() - { - let locale = "en-US"; - try - { - locale = Utils.chromeRegistry.getSelectedLocale("adblockplus"); - } - catch (e) - { - Cu.reportError(e); - } - Object.defineProperty(this, "appLocale", {value: locale}); - return locale; - }, - - /** - * Returns version of the Gecko platform - */ - get platformVersion() - { - let platformVersion = Services.appinfo.platformVersion; - Object.defineProperty(this, "platformVersion", {value: platformVersion}); - return platformVersion; - }, - - /** - * Retrieves a string from global.properties string bundle, will throw if string isn't found. - * - * @param {String} name string name - * @return {String} - */ - getString: function(name) - { - // Randomize URI to work around bug 719376 - let stringBundle = Services.strings.createBundle("chrome://adblockplus/locale/global.properties?" + Math.random()); - Utils.getString = function(name) - { - return stringBundle.GetStringFromName(name); - } - return Utils.getString(name); - }, - - /** - * Shows an alert message like window.alert() but with a custom title. - * - * @param {Window} parentWindow parent window of the dialog (can be null) - * @param {String} message message to be displayed - * @param {String} [title] dialog title, default title will be used if omitted - */ - alert: function(parentWindow, message, title) - { - if (!title) - title = Utils.getString("default_dialog_title"); - Utils.promptService.alert(parentWindow, title, message); - }, - - /** - * Asks the user for a confirmation like window.confirm() but with a custom title. - * - * @param {Window} parentWindow parent window of the dialog (can be null) - * @param {String} message message to be displayed - * @param {String} [title] dialog title, default title will be used if omitted - * @return {Bool} - */ - confirm: function(parentWindow, message, title) - { - if (!title) - title = Utils.getString("default_dialog_title"); - return Utils.promptService.confirm(parentWindow, title, message); - }, - - /** - * Retrieves the window for a document node. - * @return {Window} will be null if the node isn't associated with a window - */ - getWindow: function(/**Node*/ node) - { - if ("ownerDocument" in node && node.ownerDocument) - node = node.ownerDocument; - - if ("defaultView" in node) - return node.defaultView; - - return null; - }, - - /** - * Retrieves the top-level chrome window for a content window. - */ - getChromeWindow: function(/**Window*/ window) /**Window*/ - { - return window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - }, - - /** - * If the window doesn't have its own security context (e.g. about:blank or - * data: URL) walks up the parent chain until a window is found that has a - * security context. - */ - getOriginWindow: function(/**Window*/ wnd) /**Window*/ - { - while (wnd != wnd.parent) - { - let uri = Utils.makeURI(wnd.location.href); - if (uri.spec != "about:blank" && uri.spec != "moz-safe-about:blank" && - !Utils.netUtils.URIChainHasFlags(uri, Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT)) - { - break; - } - wnd = wnd.parent; - } - return wnd; - }, - - /** - * If a protocol using nested URIs like jar: is used - retrieves innermost - * nested URI. - */ - unwrapURL: function(/**nsIURI or String*/ url) /**nsIURI*/ - { - if (!(url instanceof Ci.nsIURI)) - url = Utils.makeURI(url); - - if (url instanceof Ci.nsINestedURI) - return url.innermostURI; - else - return url; - }, - - /** - * Translates a string URI into its nsIURI representation, will return null for - * invalid URIs. - */ - makeURI: function(/**String*/ url) /**nsIURI*/ - { - try - { - return Utils.ioService.newURI(url, null, null); - } - catch (e) { - return null; - } - }, - - /** - * Posts an action to the event queue of the current thread to run it - * asynchronously. - * @param {function} callback - */ - runAsync: function(callback) - { - Services.tm.currentThread.dispatch(callback, Ci.nsIEventTarget.DISPATCH_NORMAL); - }, - - /** - * Generates filter subscription checksum. - * - * @param {string[]} lines filter subscription lines (with checksum line removed) - * @return {String} checksum or null - */ - generateChecksum: function(lines) - { - let stream = null; - try - { - // Checksum is an MD5 checksum (base64-encoded without the trailing "=") of - // all lines in UTF-8 without the checksum line, joined with "\n". - - let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - stream = converter.convertToInputStream(lines.join("\n")); - - let hashEngine = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); - hashEngine.init(hashEngine.MD5); - hashEngine.updateFromStream(stream, stream.available()); - return hashEngine.finish(true).replace(/=+$/, ""); - } - catch (e) - { - return null; - } - finally - { - if (stream) - stream.close(); - } - }, - - /** - * Formats a unix time according to user's locale. - * @param {Integer} time unix time in milliseconds - * @return {String} formatted date and time - */ - formatTime: function(time) - { - try - { - let date = new Date(time); - return Utils.dateFormatter.FormatDateTime("", Ci.nsIScriptableDateFormat.dateFormatShort, - Ci.nsIScriptableDateFormat.timeFormatNoSeconds, - date.getFullYear(), date.getMonth() + 1, date.getDate(), - date.getHours(), date.getMinutes(), date.getSeconds()); - } - catch(e) - { - // Make sure to return even on errors - Cu.reportError(e); - return ""; - } - }, - - /** - * Checks whether any of the prefixes listed match the application locale, - * returns matching prefix if any. - */ - checkLocalePrefixMatch: function(/**String*/ prefixes) /**String*/ - { - if (!prefixes) - return null; - - let appLocale = Utils.appLocale; - for (let prefix of prefixes.split(/,/)) - if (new RegExp("^" + prefix + "\\b").test(appLocale)) - return prefix; - - return null; - }, - - /** - * Chooses the best filter subscription for user's language. - */ - chooseFilterSubscription: function(/**NodeList*/ subscriptions) /**Node*/ - { - let selectedItem = null; - let selectedPrefix = null; - let matchCount = 0; - for (let i = 0; i < subscriptions.length; i++) - { - let subscription = subscriptions[i]; - if (!selectedItem) - selectedItem = subscription; - - let prefix = Utils.checkLocalePrefixMatch(subscription.getAttribute("prefixes")); - if (prefix) - { - if (!selectedPrefix || selectedPrefix.length < prefix.length) - { - selectedItem = subscription; - selectedPrefix = prefix; - matchCount = 1; - } - else if (selectedPrefix && selectedPrefix.length == prefix.length) - { - matchCount++; - - // If multiple items have a matching prefix of the same length: - // Select one of the items randomly, probability should be the same - // for all items. So we replace the previous match here with - // probability 1/N (N being the number of matches). - if (Math.random() * matchCount < 1) - { - selectedItem = subscription; - selectedPrefix = prefix; - } - } - } - } - return selectedItem; - }, - - /** - * Saves sidebar state before detaching/reattaching - */ - setParams: function(params) - { - sidebarParams = params; - }, - - /** - * Retrieves and removes sidebar state after detaching/reattaching - */ - getParams: function() - { - let ret = sidebarParams; - sidebarParams = null; - return ret; - }, - - /** - * Verifies RSA signature. The public key and signature should be base64-encoded. - * @param {string} key - * @param {string} signature - * @param {string} data - * @return {boolean} - */ - verifySignature: function(key, signature, data) - { - if (!Utils.crypto) - return false; - - // Maybe we did the same check recently, look it up in the cache - if (!("_cache" in Utils.verifySignature)) - Utils.verifySignature._cache = new Cache(5); - let cache = Utils.verifySignature._cache; - let cacheKey = key + " " + signature + " " + data; - if (cacheKey in cache.data) - return cache.data[cacheKey]; - else - cache.add(cacheKey, false); - - let keyInfo, pubKey, context; - try - { - let keyItem = Utils.crypto.getSECItem(atob(key)); - keyInfo = Utils.crypto.SECKEY_DecodeDERSubjectPublicKeyInfo(keyItem.address()); - if (keyInfo.isNull()) - throw new Error("SECKEY_DecodeDERSubjectPublicKeyInfo failed"); - - pubKey = Utils.crypto.SECKEY_ExtractPublicKey(keyInfo); - if (pubKey.isNull()) - throw new Error("SECKEY_ExtractPublicKey failed"); - - let signatureItem = Utils.crypto.getSECItem(atob(signature)); - - context = Utils.crypto.VFY_CreateContext(pubKey, signatureItem.address(), Utils.crypto.SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE, null); - if (context.isNull()) - return false; // This could happen if the signature is invalid - - let error = Utils.crypto.VFY_Begin(context); - if (error < 0) - throw new Error("VFY_Begin failed"); - - error = Utils.crypto.VFY_Update(context, data, data.length); - if (error < 0) - throw new Error("VFY_Update failed"); - - error = Utils.crypto.VFY_End(context); - if (error < 0) - return false; - - cache.data[cacheKey] = true; - return true; - } - catch (e) - { - Cu.reportError(e); - return false; - } - finally - { - if (keyInfo && !keyInfo.isNull()) - Utils.crypto.SECKEY_DestroySubjectPublicKeyInfo(keyInfo); - if (pubKey && !pubKey.isNull()) - Utils.crypto.SECKEY_DestroyPublicKey(pubKey); - if (context && !context.isNull()) - Utils.crypto.VFY_DestroyContext(context, true); - } - }, - - /** - * Returns the documentation link from the preferences. - */ - getDocLink: function(/**String*/ linkID) - { - let {Prefs} = require("prefs"); - let docLink = Prefs.documentation_link; - return docLink.replace(/%LINK%/g, linkID).replace(/%LANG%/g, Utils.appLocale); - }, - - /** - * Splits up a combined label into the label and access key components. - * - * @return {Array} An array with two strings: label and access key - */ - splitLabel: function(/**String*/ label) - { - let match = /^(.*)\s*\(&(.)\)\s*(\u2026?)$/.exec(label); - if (match) - { - // Access key not part of the label - return [match[1] + match[3], match[2]]; - } - else - { - // Access key part of the label - let pos = label.indexOf("&"); - if (pos >= 0 && pos < label.length - 1) - return [label.substr(0, pos) + label.substr(pos + 1), label[pos + 1]]; - else - return [label, ""]; - } - }, - - /** - * Split all labels starting from a particular DOM node. - */ - splitAllLabels: function(/**DOMNode*/ root) - { - let attrMap = { - __proto__: null, - "label": "value", - "setting": "title" - }; - - let elements = root.querySelectorAll("*[label], label[value], setting[title]"); - for (let i = 0; i < elements.length; i++) - { - let element = elements[i]; - let attr = (element.localName in attrMap ? attrMap[element.localName] : "label"); - let origLabel = element.getAttribute(attr); - - let [label, accesskey] = this.splitLabel(origLabel); - if (label != origLabel) - element.setAttribute(attr, label); - if (accesskey != "") - element.setAttribute("accesskey", accesskey); - - // Labels forward changes of the accessKey property to their control, only - // set it for actual controls. - if (element.localName != "label") - element.accessKey = accesskey; - } - } -}; - -/** - * A cache with a fixed capacity, newer entries replace entries that have been - * stored first. - * @constructor - */ -function Cache(/**Integer*/ size) -{ - this._ringBuffer = new Array(size); - this.data = Object.create(null); -} -exports.Cache = Cache; - -Cache.prototype = -{ - /** - * Ring buffer storing hash keys, allows determining which keys need to be - * evicted. - * @type Array - */ - _ringBuffer: null, - - /** - * Index in the ring buffer to be written next. - * @type Integer - */ - _bufferIndex: 0, - - /** - * Cache data, maps values to the keys. Read-only access, for writing use - * add() method. - * @type Object - */ - data: null, - - /** - * Adds a key and the corresponding value to the cache. - */ - add: function(/**String*/ key, value) - { - if (!(key in this.data)) - { - // This is a new key - we need to add it to the ring buffer and evict - // another entry instead. - let oldKey = this._ringBuffer[this._bufferIndex]; - if (typeof oldKey != "undefined") - delete this.data[oldKey]; - this._ringBuffer[this._bufferIndex] = key; - - this._bufferIndex++; - if (this._bufferIndex >= this._ringBuffer.length) - this._bufferIndex = 0; - } - - this.data[key] = value; - }, - - /** - * Clears cache contents. - */ - clear: function() - { - this._ringBuffer = new Array(this._ringBuffer.length); - this.data = Object.create(null); - } -} - -// Getters for common services, this should be replaced by Services.jsm in future - -XPCOMUtils.defineLazyServiceGetter(Utils, "categoryManager", "@mozilla.org/categorymanager;1", "nsICategoryManager"); -XPCOMUtils.defineLazyServiceGetter(Utils, "ioService", "@mozilla.org/network/io-service;1", "nsIIOService"); -XPCOMUtils.defineLazyServiceGetter(Utils, "promptService", "@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"); -XPCOMUtils.defineLazyServiceGetter(Utils, "effectiveTLD", "@mozilla.org/network/effective-tld-service;1", "nsIEffectiveTLDService"); -XPCOMUtils.defineLazyServiceGetter(Utils, "netUtils", "@mozilla.org/network/util;1", "nsINetUtil"); -XPCOMUtils.defineLazyServiceGetter(Utils, "styleService", "@mozilla.org/content/style-sheet-service;1", "nsIStyleSheetService"); -XPCOMUtils.defineLazyServiceGetter(Utils, "prefService", "@mozilla.org/preferences-service;1", "nsIPrefService"); -XPCOMUtils.defineLazyServiceGetter(Utils, "versionComparator", "@mozilla.org/xpcom/version-comparator;1", "nsIVersionComparator"); -XPCOMUtils.defineLazyServiceGetter(Utils, "windowMediator", "@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator"); -XPCOMUtils.defineLazyServiceGetter(Utils, "windowWatcher", "@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher"); -XPCOMUtils.defineLazyServiceGetter(Utils, "chromeRegistry", "@mozilla.org/chrome/chrome-registry;1", "nsIXULChromeRegistry"); -XPCOMUtils.defineLazyServiceGetter(Utils, "systemPrincipal", "@mozilla.org/systemprincipal;1", "nsIPrincipal"); -XPCOMUtils.defineLazyServiceGetter(Utils, "dateFormatter", "@mozilla.org/intl/scriptabledateformat;1", "nsIScriptableDateFormat"); -XPCOMUtils.defineLazyServiceGetter(Utils, "httpProtocol", "@mozilla.org/network/protocol;1?name=http", "nsIHttpProtocolHandler"); -XPCOMUtils.defineLazyServiceGetter(Utils, "clipboard", "@mozilla.org/widget/clipboard;1", "nsIClipboard"); -XPCOMUtils.defineLazyServiceGetter(Utils, "clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); -XPCOMUtils.defineLazyGetter(Utils, "crypto", function() -{ - try - { - let ctypes = Cu.import("resource://gre/modules/ctypes.jsm", null).ctypes; - - let nsslib; - try - { - nsslib = ctypes.open(ctypes.libraryName("nss3")); - } - catch (e) - { - // It seems that on Mac OS X the full path name needs to be specified - let file; - // Gecko 35 added GreBinD key, see https://bugzilla.mozilla.org/show_bug.cgi?id=1077099 - if (Services.dirsvc.has("GreBinD")) - file = Services.dirsvc.get("GreBinD", Ci.nsILocalFile); - else - file = Services.dirsvc.get("GreD", Ci.nsILocalFile); - file.append(ctypes.libraryName("nss3")); - nsslib = ctypes.open(file.path); - } - - let result = {}; - - // seccomon.h - result.siUTF8String = 14; - - // secoidt.h - result.SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE = 15; - - // The following types are opaque to us - result.VFYContext = ctypes.void_t; - result.SECKEYPublicKey = ctypes.void_t; - result.CERTSubjectPublicKeyInfo = ctypes.void_t; - - /* - * seccomon.h - * struct SECItemStr { - * SECItemType type; - * unsigned char *data; - * unsigned int len; - * }; - */ - result.SECItem = ctypes.StructType("SECItem", [ - {type: ctypes.int}, - {data: ctypes.unsigned_char.ptr}, - {len: ctypes.int} - ]); - - /* - * cryptohi.h - * extern VFYContext *VFY_CreateContext(SECKEYPublicKey *key, SECItem *sig, - * SECOidTag sigAlg, void *wincx); - */ - result.VFY_CreateContext = nsslib.declare( - "VFY_CreateContext", - ctypes.default_abi, result.VFYContext.ptr, - result.SECKEYPublicKey.ptr, - result.SECItem.ptr, - ctypes.int, - ctypes.voidptr_t - ); - - /* - * cryptohi.h - * extern void VFY_DestroyContext(VFYContext *cx, PRBool freeit); - */ - result.VFY_DestroyContext = nsslib.declare( - "VFY_DestroyContext", - ctypes.default_abi, ctypes.void_t, - result.VFYContext.ptr, - ctypes.bool - ); - - /* - * cryptohi.h - * extern SECStatus VFY_Begin(VFYContext *cx); - */ - result.VFY_Begin = nsslib.declare("VFY_Begin", - ctypes.default_abi, ctypes.int, - result.VFYContext.ptr - ); - - /* - * cryptohi.h - * extern SECStatus VFY_Update(VFYContext *cx, const unsigned char *input, - * unsigned int inputLen); - */ - result.VFY_Update = nsslib.declare( - "VFY_Update", - ctypes.default_abi, ctypes.int, - result.VFYContext.ptr, - ctypes.unsigned_char.ptr, - ctypes.int - ); - - /* - * cryptohi.h - * extern SECStatus VFY_End(VFYContext *cx); - */ - result.VFY_End = nsslib.declare( - "VFY_End", - ctypes.default_abi, ctypes.int, - result.VFYContext.ptr - ); - - /* - * keyhi.h - * extern CERTSubjectPublicKeyInfo * - * SECKEY_DecodeDERSubjectPublicKeyInfo(SECItem *spkider); - */ - result.SECKEY_DecodeDERSubjectPublicKeyInfo = nsslib.declare( - "SECKEY_DecodeDERSubjectPublicKeyInfo", - ctypes.default_abi, result.CERTSubjectPublicKeyInfo.ptr, - result.SECItem.ptr - ); - - /* - * keyhi.h - * extern void SECKEY_DestroySubjectPublicKeyInfo(CERTSubjectPublicKeyInfo *spki); - */ - result.SECKEY_DestroySubjectPublicKeyInfo = nsslib.declare( - "SECKEY_DestroySubjectPublicKeyInfo", - ctypes.default_abi, ctypes.void_t, - result.CERTSubjectPublicKeyInfo.ptr - ); - - /* - * keyhi.h - * extern SECKEYPublicKey * - * SECKEY_ExtractPublicKey(CERTSubjectPublicKeyInfo *); - */ - result.SECKEY_ExtractPublicKey = nsslib.declare( - "SECKEY_ExtractPublicKey", - ctypes.default_abi, result.SECKEYPublicKey.ptr, - result.CERTSubjectPublicKeyInfo.ptr - ); - - /* - * keyhi.h - * extern void SECKEY_DestroyPublicKey(SECKEYPublicKey *key); - */ - result.SECKEY_DestroyPublicKey = nsslib.declare( - "SECKEY_DestroyPublicKey", - ctypes.default_abi, ctypes.void_t, - result.SECKEYPublicKey.ptr - ); - - // Convenience method - result.getSECItem = function(data) - { - var dataArray = new ctypes.ArrayType(ctypes.unsigned_char, data.length)(); - for (let i = 0; i < data.length; i++) - dataArray[i] = data.charCodeAt(i) % 256; - return new result.SECItem(result.siUTF8String, dataArray, dataArray.length); - }; - - return result; - } - catch (e) - { - Cu.reportError(e); - // Expected, ctypes isn't supported in Gecko 1.9.2 - return null; - } -}); - -if ("@mozilla.org/messenger/headerparser;1" in Cc) - XPCOMUtils.defineLazyServiceGetter(Utils, "headerParser", "@mozilla.org/messenger/headerparser;1", "nsIMsgHeaderParser"); -else - Utils.headerParser = null; diff --git a/data/extensions/spyblock@gnu.org/lib/whitelisting.js b/data/extensions/spyblock@gnu.org/lib/whitelisting.js deleted file mode 100644 index 1006d26..0000000 --- a/data/extensions/spyblock@gnu.org/lib/whitelisting.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of Adblock Plus <https://adblockplus.org/>, - * 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 - * 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 This is a dummy to provide a function needed by message - * responder. - */ - -"use strict"; - -let {Policy} = require("contentPolicy"); -let {RegExpFilter} = require("filterClasses"); - -// NOTE: The function interface is supposed to be compatible with -// checkWhitelisted in adblockpluschrome. That's why there is a typeMask -// parameter here. However, this parameter is only used to decide whether -// elemhide whitelisting should be considered, so only supported values for this -// parameter are RegExpFilter.typeMap.DOCUMENT and -// RegExpFilter.typeMap.DOCUMENT | RegExpFilter.typeMap.ELEMHIDE. -exports.checkWhitelisted = function(page, frames, typeMask) -{ - let match = - Policy.isFrameWhitelisted(frames, typeMask & RegExpFilter.typeMap.ELEMHIDE); - if (match) - { - let [frameIndex, matchType, docDomain, thirdParty, location, filter] = match; - if (matchType == "DOCUMENT" || matchType == "ELEMHIDE") - return filter; - } - - return null; -}; diff --git a/data/extensions/spyblock@gnu.org/lib/windowObserver.js b/data/extensions/spyblock@gnu.org/lib/windowObserver.js deleted file mode 100644 index 7d34f8a..0000000 --- a/data/extensions/spyblock@gnu.org/lib/windowObserver.js +++ /dev/null @@ -1,99 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -exports.WindowObserver = WindowObserver; - -/** - * This class will call listener's method applyToWindow() for all new chrome - * windows being opened. It will also call listener's method removeFromWindow() - * for all windows still open when the extension is shut down. - * @param {Object} listener - * @param {String} [when] when to execute applyToWindow(). "start" means immediately - * when the window opens, "ready" when its contents are available - * and "end" (default) means to wait until the "load" event. - * @constructor - */ -function WindowObserver(listener, when) -{ - this._listener = listener; - this._when = when; - - let windows = []; - let e = Services.wm.getZOrderDOMWindowEnumerator(null, true); - while (e.hasMoreElements()) - windows.push(e.getNext()); - - // Check if there are any windows that we missed - let eAll = Services.ww.getWindowEnumerator(); - while (eAll.hasMoreElements()) - { - let element = eAll.getNext(); - if (windows.indexOf(element) < 0) - windows.push(element); - } - - for (let i = 0; i < windows.length; i++) - { - let window = windows[i].QueryInterface(Ci.nsIDOMWindow); - if (when == "start" || window.document.readyState == "complete") - this._listener.applyToWindow(window); - else - this.observe(window, "chrome-document-global-created", null); - } - - Services.obs.addObserver(this, "chrome-document-global-created", true); - - this._shutdownHandler = function() - { - let e = Services.ww.getWindowEnumerator(); - while (e.hasMoreElements()) - this._listener.removeFromWindow(e.getNext().QueryInterface(Ci.nsIDOMWindow)); - - Services.obs.removeObserver(this, "chrome-document-global-created"); - }.bind(this); - onShutdown.add(this._shutdownHandler); -} -WindowObserver.prototype = -{ - _listener: null, - _when: null, - _shutdownHandler: null, - - shutdown: function() - { - if (!this._shutdownHandler) - return; - - onShutdown.remove(this._shutdownHandler); - this._shutdownHandler(); - this._shutdownHandler = null; - }, - - observe: function(subject, topic, data) - { - if (topic == "chrome-document-global-created") - { - let window = subject.QueryInterface(Ci.nsIDOMWindow); - if (this._when == "start") - { - this._listener.applyToWindow(window); - return; - } - - let event = (this._when == "ready" ? "DOMContentLoaded" : "load"); - let listener = function() - { - window.removeEventListener(event, listener, false); - if (this._shutdownHandler) - this._listener.applyToWindow(window); - }.bind(this); - window.addEventListener(event, listener, false); - } - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIObserver]) -}; |