summaryrefslogtreecommitdiff
path: root/data/extensions/spyblock@gnu.org/chrome/content/ui/sidebar.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/spyblock@gnu.org/chrome/content/ui/sidebar.js')
-rw-r--r--data/extensions/spyblock@gnu.org/chrome/content/ui/sidebar.js1337
1 files changed, 0 insertions, 1337 deletions
diff --git a/data/extensions/spyblock@gnu.org/chrome/content/ui/sidebar.js b/data/extensions/spyblock@gnu.org/chrome/content/ui/sidebar.js
deleted file mode 100644
index 688c248..0000000
--- a/data/extensions/spyblock@gnu.org/chrome/content/ui/sidebar.js
+++ /dev/null
@@ -1,1337 +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");
-
-// Main browser window
-var mainWin = parent;
-
-// Location of the content window that the list refers to
-var contentLocation = null;
-
-// The window handler currently in use
-var requestNotifier = null;
-
-var cacheStorage = null;
-
-// Matcher for disabled filters
-var disabledMatcher = new CombinedMatcher();
-
-// Cached string values
-var docDomainThirdParty = null;
-var docDomainFirstParty = null;
-
-// Localized type names
-var localizedTypes = new Map();
-
-function init() {
- docDomainThirdParty = document.documentElement.getAttribute("docDomainThirdParty");
- docDomainFirstParty = document.documentElement.getAttribute("docDomainFirstParty");
-
- var list = E("list");
- list.view = treeView;
-
- // Restore previous state
- var params = Utils.getParams();
- if (params && params.filter)
- {
- E("searchField").value = params.filter;
- treeView.setFilter(params.filter);
- }
- if (params && params.focus && E(params.focus))
- E(params.focus).focus();
- else
- E("searchField").focus();
-
- var selected = null;
- if (/sidebarDetached\.xul$/.test(parent.location.href))
- {
- mainWin = parent.opener;
- mainWin.addEventListener("unload", mainUnload, false);
- E("detachButton").hidden = true;
- E("reattachButton").hidden = false;
-
- let mustDetach = parent.arguments[0];
- if (mustDetach)
- E("reattachButton").setAttribute("disabled", "true");
- if ("sidebar" in UI.hotkeys)
- {
- let {KeySelector} = require("keySelector");
- parent.addEventListener("keypress", function(event)
- {
- if (KeySelector.matchesKey(event, UI.hotkeys.sidebar))
- doClose();
- }, false);
- }
-
- // Set default size/position unless already persisted
- let defaults = {screenX: 0, screenY: 0, width: 600, height: 300};
- if (params && params.position)
- defaults = params.position;
-
- let wnd = parent.document.documentElement;
- for (let attr in defaults)
- if (!wnd.hasAttribute(attr))
- wnd.setAttribute(attr, defaults[attr]);
- }
-
- let {addBrowserLocationListener} = require("appSupport");
- updateContentLocation();
-
- // Initialize matcher for disabled filters
- reloadDisabledFilters();
- FilterNotifier.on("subscription.added", reloadDisabledFilters);
- FilterNotifier.on("subscription.removed", reloadDisabledFilters);
- FilterNotifier.on("subscription.disabled", reloadDisabledFilters);
- FilterNotifier.on("subscription.updated", reloadDisabledFilters);
- FilterNotifier.on("filter.added", reloadDisabledFilters);
- FilterNotifier.on("filter.removed", reloadDisabledFilters);
- FilterNotifier.on("filter.disabled", reloadDisabledFilters);
- Prefs.addListener(onPrefChange);
-
- // Activate flasher
- list.addEventListener("select", onSelectionChange, false);
-
- // Initialize data
- handleLocationChange();
-
- // Install a progress listener to catch location changes
- if (addBrowserLocationListener)
- addBrowserLocationListener(mainWin, handleLocationChange, true);
-
- for (let type of Policy.contentTypes.values())
- localizedTypes.set(type, Utils.getString("type_label_" + type.toLowerCase()));
-}
-
-// To be called for a detached window when the main window has been closed
-function mainUnload() {
- parent.close();
-}
-
-function updateContentLocation()
-{
- let {getCurrentLocation} = require("appSupport");
- let location = getCurrentLocation(mainWin);
- if (location instanceof Ci.nsIURI)
- location = location.spec;
- contentLocation = location;
-}
-
-function getOuterWindowID()
-{
- let {getBrowser} = require("appSupport");
- let browser = getBrowser(mainWin);
- if ("selectedBrowser" in browser)
- browser = browser.selectedBrowser;
- return browser.outerWindowID;
-}
-
-function getFilter(item)
-{
- if ("filter" in item && item.filter)
- return Filter.fromText(item.filter);
- else
- return null;
-}
-
-// To be called on unload
-function cleanUp() {
- requestNotifier.shutdown();
- FilterNotifier.off("subscription.added", reloadDisabledFilters);
- FilterNotifier.off("subscription.removed", reloadDisabledFilters);
- FilterNotifier.off("subscription.disabled", reloadDisabledFilters);
- FilterNotifier.off("subscription.updated", reloadDisabledFilters);
- FilterNotifier.off("filter.added", reloadDisabledFilters);
- FilterNotifier.off("filter.removed", reloadDisabledFilters);
- FilterNotifier.off("filter.disabled", reloadDisabledFilters);
- Prefs.removeListener(onPrefChange);
- E("list").view = null;
-
- let {removeBrowserLocationListener} = require("appSupport");
- if (removeBrowserLocationListener)
- removeBrowserLocationListener(mainWin, handleLocationChange);
- mainWin.removeEventListener("unload", mainUnload, false);
-}
-
-/**
- * Tracks preference changes, calls reloadDisabledFilters whenever Adblock Plus
- * is enabled/disabled.
- */
-function onPrefChange(name)
-{
- if (name == "enabled")
- reloadDisabledFilters();
-}
-
-let reloadDisabledScheduled = false;
-
-/**
- * Updates matcher for disabled filters (global disabledMatcher variable),
- * called on each filter change. Execute delayed to prevent multiple subsequent
- * invocations.
- */
-function reloadDisabledFilters()
-{
- if (reloadDisabledScheduled)
- return;
-
- Utils.runAsync(reloadDisabledFiltersInternal);
- reloadDisabledScheduled = true;
-}
-
-function reloadDisabledFiltersInternal()
-{
- reloadDisabledScheduled = false;
- disabledMatcher.clear();
-
- if (Prefs.enabled)
- {
- for (let subscription of FilterStorage.subscriptions)
- {
- if (subscription.disabled)
- continue;
-
- for (let filter of subscription.filters)
- if (filter instanceof RegExpFilter && filter.disabled)
- disabledMatcher.add(filter);
- }
- }
-
- treeView.updateFilters();
-}
-
-// Called whenever list selection changes - triggers flasher
-function onSelectionChange() {
- var item = treeView.getSelectedItem();
- if (item)
- E("copy-command").removeAttribute("disabled");
- else
- E("copy-command").setAttribute("disabled", "true");
-
- if (item)
- {
- let key = item.location + " " + item.type + " " + item.docDomain;
- RequestNotifier.storeWindowData(getOuterWindowID(), key);
- treeView.itemToSelect = null;
- }
-
- if (requestNotifier)
- requestNotifier.flashNodes(item ? item.ids : null, Prefs.flash_scrolltoitem);
-}
-
-function handleLocationChange()
-{
- if (requestNotifier)
- requestNotifier.shutdown();
-
- updateContentLocation();
- treeView.clearData();
-
- let outerWindowID = getOuterWindowID();
- RequestNotifier.retrieveWindowData(outerWindowID, key =>
- {
- treeView.itemToSelect = key;
- });
- requestNotifier = new RequestNotifier(outerWindowID, (item, scanComplete) =>
- {
- if (item)
- treeView.addItem(item, scanComplete);
- });
- cacheStorage = null;
-}
-
-// Fills a box with text splitting it up into multiple lines if necessary
-function setMultilineContent(box, text, noRemove)
-{
- if (!noRemove)
- while (box.firstChild)
- box.removeChild(box.firstChild);
-
- let lines = text.match(/.{1,80}/g);
- if (lines.length > 7)
- {
- // Text is too long to display in full so we cut out the middle part
- lines = lines.slice(0,3).concat("\u2026", lines.slice(-3));
- }
-
- for (let line of lines)
- {
- let description = document.createElement("description");
- description.setAttribute("value", line);
- box.appendChild(description);
- }
-}
-
-// Fill in tooltip data before showing it
-function fillInTooltip(e) {
- // Prevent tooltip from overlapping menu
- if (E("context").state == "open")
- {
- e.preventDefault();
- return;
- }
-
- var item;
- if (treeView.data && !treeView.data.length)
- item = treeView.getDummyTooltip();
- else
- item = treeView.getItemAt(e.clientX, e.clientY);
-
- if (!item)
- {
- e.preventDefault();
- return;
- }
-
- let filter = getFilter(item);
- let subscriptions = (filter ? filter.subscriptions.filter(function(subscription) { return !subscription.disabled; }) : []);
-
- E("tooltipDummy").hidden = !("tooltip" in item);
- E("tooltipAddressRow").hidden = ("tooltip" in item);
- E("tooltipTypeRow").hidden = ("tooltip" in item);
- E("tooltipDocDomainRow").hidden = ("tooltip" in item || !item.docDomain);
- E("tooltipFilterRow").hidden = !filter;
- E("tooltipFilterSourceRow").hidden = !subscriptions.length;
-
- if ("tooltip" in item)
- E("tooltipDummy").setAttribute("value", item.tooltip);
- else
- {
- E("tooltipAddress").parentNode.hidden = (item.type == "ELEMHIDE");
- setMultilineContent(E("tooltipAddress"), item.location);
-
- var type = localizedTypes.get(item.type);
- if (filter && filter instanceof WhitelistFilter)
- type += " " + E("tooltipType").getAttribute("whitelisted");
- else if (filter && item.type != "ELEMHIDE")
- type += " " + E("tooltipType").getAttribute("filtered");
- E("tooltipType").setAttribute("value", type);
-
- E("tooltipDocDomain").setAttribute("value", item.docDomain + " " + (item.thirdParty ? docDomainThirdParty : docDomainFirstParty));
- }
-
- if (filter)
- {
- let filterField = E("tooltipFilter");
- setMultilineContent(filterField, filter.text);
- if (filter.disabled)
- {
- let disabledText = document.createElement("description");
- disabledText.className = "disabledTextLabel";
- disabledText.textContent = filterField.getAttribute("disabledText");
- filterField.appendChild(disabledText);
- }
-
- if (subscriptions.length)
- {
- let sourceElement = E("tooltipFilterSource");
- while (sourceElement.firstChild)
- sourceElement.removeChild(sourceElement.firstChild);
- for (let i = 0; i < subscriptions.length; i++)
- setMultilineContent(sourceElement, getSubscriptionTitle(subscriptions[i]), true);
- }
- }
-
- E("tooltipSizeRow").hidden = true;
- if (!("tooltip" in item))
- {
- getItemSize(item, (size) =>
- {
- if (size)
- {
- E("tooltipSizeRow").hidden = false;
- E("tooltipSize").setAttribute("value", size.join(" x "));
- }
- });
- }
-
- var showPreview = Prefs.previewimages && !("tooltip" in item);
- showPreview = showPreview && item.type == "IMAGE";
- showPreview = showPreview && (!filter || filter.disabled || filter instanceof WhitelistFilter);
- E("tooltipPreviewBox").hidden = true;
- if (showPreview)
- {
- if (!cacheStorage)
- {
- let {Services} = Cu.import("resource://gre/modules/Services.jsm", null);
- let {LoadContextInfo} = Cu.import("resource://gre/modules/LoadContextInfo.jsm", null);
- cacheStorage = Services.cache2.diskCacheStorage(LoadContextInfo.default, false);
- }
-
- let showTooltipPreview = function ()
- {
- E("tooltipPreview").setAttribute("src", item.location);
- E("tooltipPreviewBox").hidden = false;
- };
- try
- {
- cacheStorage.asyncOpenURI(Utils.makeURI(item.location), "", Ci.nsICacheStorage.OPEN_READONLY, {
- onCacheEntryCheck: function (entry, appCache)
- {
- return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
- },
- onCacheEntryAvailable: function (entry, isNew, appCache, status)
- {
- if (Components.isSuccessCode(status) && !isNew)
- showTooltipPreview();
- }
- });
- }
- catch (e)
- {
- Cu.reportError(e);
- }
- }
-}
-
-const visual = {
- OTHER: true,
- IMAGE: true,
- SUBDOCUMENT: true
-}
-
-/**
- * Updates context menu before it is shown.
- */
-function fillInContext(/**Event*/ e)
-{
- let item, allItems;
- if (treeView.data && !treeView.data.length)
- {
- item = treeView.getDummyTooltip();
- allItems = [item];
- }
- else
- {
- item = treeView.getItemAt(e.clientX, e.clientY);
- allItems = treeView.getAllSelectedItems();
- }
-
- if (!item || ("tooltip" in item && !("filter" in item)))
- return false;
-
- E("contextDisableFilter").hidden = true;
- E("contextEnableFilter").hidden = true;
- E("contextDisableOnSite").hidden = true;
- let filter = getFilter(item);
- if (filter)
- {
- let menuItem = E(filter.disabled ? "contextEnableFilter" : "contextDisableFilter");
- menuItem.setAttribute("label", menuItem.getAttribute("labeltempl").replace(/\?1\?/, filter.text));
- menuItem.hidden = false;
-
- if (filter instanceof ActiveFilter && !filter.disabled && filter.subscriptions.length && !filter.subscriptions.some(subscription => !(subscription instanceof SpecialSubscription)))
- {
- let domain = null;
- try {
- domain = Utils.effectiveTLD.getBaseDomainFromHost(item.docDomain);
- } catch (e) {}
-
- if (domain && !filter.isActiveOnlyOnDomain(domain))
- {
- menuItem = E("contextDisableOnSite");
- menuItem.setAttribute("label", menuItem.getAttribute("labeltempl").replace(/\?1\?/, domain));
- menuItem.hidden = false;
- }
- }
- }
-
- E("contextWhitelist").hidden = ("tooltip" in item || !filter || filter.disabled || filter instanceof WhitelistFilter || item.type == "ELEMHIDE");
- E("contextBlock").hidden = !E("contextWhitelist").hidden;
- E("contextBlock").setAttribute("disabled", filter && !filter.disabled);
- E("contextEditFilter").setAttribute("disabled", !filter);
- E("contextOpen").setAttribute("disabled", "tooltip" in item || item.type == "ELEMHIDE");
- E("contextFlash").setAttribute("disabled", "tooltip" in item || !(item.type in visual) || (filter && !filter.disabled && !(filter instanceof WhitelistFilter)));
- E("contextCopyFilter").setAttribute("disabled", !allItems.some(getFilter));
-
- return true;
-}
-
-/**
- * Processed mouse clicks on the item list.
- * @param {Event} event
- */
-function handleClick(event)
-{
- let item = treeView.getItemAt(event.clientX, event.clientY);
- if (event.button == 0 && treeView.getColumnAt(event.clientX, event.clientY) == "state")
- {
- let filter = getFilter(item);
- if (filter)
- enableFilter(filter, filter.disabled);
- event.preventDefault();
- }
- else if (event.button == 1)
- {
- openInTab(item, event);
- event.preventDefault();
- }
-}
-
-/**
- * Processes double-clicks on the item list.
- * @param {Event} event
- */
-function handleDblClick(event)
-{
- if (event.button != 0 || treeView.getColumnAt(event.clientX, event.clientY) == "state")
- return;
-
- doBlock();
-}
-
-/**
- * Opens the item in a new tab.
- */
-function openInTab(item, /**Event*/ event)
-{
- let items = (item ? [item] : treeView.getAllSelectedItems());
- for (let item of items)
- {
- if (item && item.type != "ELEMHIDE")
- UI.loadInBrowser(item.location, mainWin, event);
- }
-}
-
-function doBlock() {
- var item = treeView.getSelectedItem();
- if (!item || item.type == "ELEMHIDE")
- return;
-
- var filter = getFilter(item);
- if (filter && !filter.disabled && filter instanceof WhitelistFilter)
- return;
-
- if (requestNotifier)
- {
- requestNotifier.storeNodesForEntries(item.ids, (nodesID) =>
- {
- UI.blockItem(window, nodesID, item.orig);
- });
- }
-}
-
-function editFilter()
-{
- var item = treeView.getSelectedItem();
- if (treeView.data && !treeView.data.length)
- item = treeView.getDummyTooltip();
-
- let filter = getFilter(item);
- if (!filter)
- return;
-
- UI.openFiltersDialog(filter);
-}
-
-function enableFilter(filter, enable) {
- filter.disabled = !enable;
-
- treeView.boxObject.invalidate();
-}
-
-/**
- * Edits the filter to disable it on a particular domain.
- */
-function disableOnSite()
-{
- let item = treeView.getSelectedItem();
- let filter = getFilter(item);
- if (!(filter instanceof ActiveFilter) || filter.disabled || !filter.subscriptions.length || filter.subscriptions.some(subscription => !(subscription instanceof SpecialSubscription)))
- return;
-
- let domain;
- try {
- domain = Utils.effectiveTLD.getBaseDomainFromHost(item.docDomain).toUpperCase();
- }
- catch (e)
- {
- return;
- }
-
- // Generate text for new filter that excludes current domain
- let text = filter.text;
- if (filter instanceof RegExpFilter)
- {
- let match = Filter.optionsRegExp.exec(text);
- if (match)
- {
- let found = false;
- let options = match[1].toUpperCase().split(",");
- for (let i = 0; i < options.length; i++)
- {
- let match = /^DOMAIN=(.*)/.exec(options[i]);
- if (match)
- {
- let domains = match[1].split("|").filter(d => d != domain && d != "~" + domain && (d.length <= domain.length || d.lastIndexOf("." + domain) != d.length - domain.length - 1));
- domains.push("~" + domain);
- options[i] = "DOMAIN=" + domains.join("|");
- found = true;
- break;
- }
- }
- if (!found)
- options.push("DOMAIN=~" + domain);
-
- text = text.replace(Filter.optionsRegExp, "$" + options.join(",").toLowerCase());
- }
- else
- text += "$domain=~" + domain.toLowerCase();
- }
- else if (filter instanceof ElemHideBase)
- {
- let match = /^([^#]+)(#.*)/.exec(text);
- if (match)
- {
- let selector = match[2];
- let domains = match[1].toUpperCase().split(",").filter(d => d != domain && (d.length <= domain.length || d != "~" + domain && d.lastIndexOf("." + domain) != d.length - domain.length - 1));
- domains.push("~" + domain);
- text = domains.join(",").toLowerCase() + selector;
- }
- else
- text = "~" + domain.toLowerCase() + text;
- }
-
- if (text == filter.text)
- return; // Just in case, shouldn't happen
-
- // Insert new filter before the old one and remove the old one then
- let newFilter = Filter.fromText(text);
- if (newFilter.disabled && newFilter.subscriptions.length)
- newFilter.disabled = false;
- else if (!newFilter.subscriptions.length)
- {
- newFilter.disabled = false;
- let subscription = filter.subscriptions.filter(s => s instanceof SpecialSubscription)[0];
- if (subscription)
- FilterStorage.addFilter(newFilter, subscription, subscription.filters.indexOf(filter));
- }
- FilterStorage.removeFilter(filter);
-
- // Update display
- for (let i = 0; i < treeView.allData.length; i++)
- if (getFilter(treeView.allData[i]) == filter)
- treeView.allData[i].filter = null;
- treeView.boxObject.invalidate();
-}
-
-function copyToClipboard() {
- var items = treeView.getAllSelectedItems();
- if (!items.length)
- return;
-
- Utils.clipboardHelper.copyString(items.map(function(item) {return item.location}).join(IO.lineBreak));
-}
-
-function copyFilter() {
- var items = treeView.getAllSelectedItems().filter(getFilter);
- if (treeView.data && !treeView.data.length)
- items = [treeView.getDummyTooltip()];
-
- if (!items.length)
- return;
-
- Utils.clipboardHelper.copyString(items.map(function(item) {return item.filter}).join(IO.lineBreak));
-}
-
-function selectAll() {
- treeView.selectAll();
-}
-
-// Saves sidebar's state before detaching/reattaching
-function saveState() {
- var focused = document.commandDispatcher.focusedElement;
- while (focused && (!focused.id || !("focus" in focused)))
- focused = focused.parentNode;
-
- // Calculate default position for the detached window
- var boxObject = document.documentElement.boxObject;
- var position = {screenX: boxObject.screenX, screenY: boxObject.screenY, width: boxObject.width, height: boxObject.height};
-
- var params = {
- filter: treeView.filter,
- focus: (focused ? focused.id : null),
- position: position
- };
- Utils.setParams(params);
-}
-
-// closes the sidebar
-function doClose()
-{
- mainWin.document.getElementById("abp-command-sidebar").doCommand();
-}
-
-// detaches/reattaches the sidebar
-function detach(doDetach)
-{
- saveState();
-
- // Store variables locally, global variables will go away when we are closed
- let myPrefs = Prefs;
- let myMainWin = mainWin;
-
- // Close sidebar and open detached window
- myMainWin.document.getElementById("abp-command-sidebar").doCommand();
- myPrefs.detachsidebar = doDetach;
- myMainWin.document.getElementById("abp-command-sidebar").doCommand();
-}
-
-// Returns item's size if already known, otherwise undefined
-function getCachedItemSize(item)
-{
- if ("size" in item)
- return item.size;
-
- let filter = getFilter(item);
- if (filter && !filter.disabled && filter instanceof BlockingFilter)
- return null;
-
- return undefined;
-}
-
-// Retrieves item's size in the document if available
-function getItemSize(item, callback)
-{
- let size = getCachedItemSize(item);
- if (typeof size != "undefined" || !requestNotifier)
- {
- callback(size);
- return;
- }
-
- requestNotifier.retrieveNodeSize(item.ids, function(size)
- {
- if (size)
- item.size = size;
- callback(size);
- });
-}
-
-// Sort functions for the item list
-function sortByAddress(item1, item2) {
- if (item1.location < item2.location)
- return -1;
- else if (item1.location > item2.location)
- return 1;
- else
- return 0;
-}
-
-function sortByAddressDesc(item1, item2) {
- return -sortByAddress(item1, item2);
-}
-
-function compareType(item1, item2) {
- let type1 = localizedTypes.get(item1.type);
- let type2 = localizedTypes.get(item2.type);
- if (type1 < type2)
- return -1;
- else if (type1 > type2)
- return 1;
- else
- return 0;
-}
-
-function compareFilter(item1, item2) {
- var hasFilter1 = (item1.filter ? 1 : 0);
- var hasFilter2 = (item2.filter ? 1 : 0);
- if (hasFilter1 != hasFilter2)
- return hasFilter1 - hasFilter2;
- else if (hasFilter1 && item1.filter < item2.filter)
- return -1;
- else if (hasFilter1 && item1.filter > item2.filter)
- return 1;
- else
- return 0;
-}
-
-function compareState(item1, item2)
-{
- let filter1 = getFilter(item1);
- let filter2 = getFilter(item2);
- let state1 = (!filter1 ? 0 : (filter1.disabled ? 1 : (filter1 instanceof WhitelistFilter ? 2 : 3)));
- let state2 = (!filter2 ? 0 : (filter2.disabled ? 1 : (filter2 instanceof WhitelistFilter ? 2 : 3)));
- return state1 - state2;
-}
-
-function compareSize(item1, item2)
-{
- let size1 = getCachedItemSize(item1);
- let size2 = getCachedItemSize(item2);
-
- size1 = size1 ? size1[0] * size1[1] : 0;
- size2 = size2 ? size2[0] * size2[1] : 0;
- return size1 - size2;
-}
-
-function compareDocDomain(item1, item2)
-{
- if (item1.docDomain < item2.docDomain)
- return -1;
- else if (item1.docDomain > item2.docDomain)
- return 1;
- else if (item1.thirdParty && !item2.thirdParty)
- return -1;
- else if (!item1.thirdParty && item2.thirdParty)
- return 1;
- else
- return 0;
-}
-
-function compareFilterSource(item1, item2)
-{
- let filter1 = getFilter(item1);
- let filter2 = getFilter(item2);
- let subs1 = filter1 ? filter1.subscriptions.map(s => getSubscriptionTitle(s)).join(", ") : "";
- let subs2 = filter2 ? filter2.subscriptions.map(s => getSubscriptionTitle(s)).join(", ") : "";
- if (subs1 < subs2)
- return -1;
- else if (subs1 > subs2)
- return 1;
- else
- return 0;
-}
-
-function createSortWithFallback(cmpFunc, fallbackFunc, desc) {
- var factor = (desc ? -1 : 1);
- return function(item1, item2) {
- var ret = cmpFunc(item1, item2);
- if (ret == 0)
- return fallbackFunc(item1, item2);
- else
- return factor * ret;
- }
-}
-
-// Item list's tree view object
-var treeView = {
- //
- // nsISupports implementation
- //
-
- QueryInterface: function(uuid) {
- if (!uuid.equals(Ci.nsISupports) &&
- !uuid.equals(Ci.nsITreeView))
- {
- throw Cr.NS_ERROR_NO_INTERFACE;
- }
-
- return this;
- },
-
- //
- // nsITreeView implementation
- //
-
- selection: null,
-
- setTree: function(boxObject) {
- if (!boxObject)
- return;
- this.boxObject = boxObject;
- this.itemsDummy = boxObject.treeBody.getAttribute("noitemslabel");
- this.whitelistDummy = boxObject.treeBody.getAttribute("whitelistedlabel");
- var stringAtoms = ["col-address", "col-type", "col-filter", "col-state", "col-size", "col-docDomain", "col-filterSource", "state-regular", "state-filtered", "state-whitelisted", "state-hidden", "state-hiddenexception"];
- var boolAtoms = ["selected", "dummy", "filter-disabled"];
- var atomService = Cc["@mozilla.org/atom-service;1"].getService(Ci.nsIAtomService);
- this.atoms = {};
- for (let atom of stringAtoms)
- this.atoms[atom] = atomService.getAtom(atom);
- for (let atom of boolAtoms)
- {
- this.atoms[atom + "-true"] = atomService.getAtom(atom + "-true");
- this.atoms[atom + "-false"] = atomService.getAtom(atom + "-false");
- }
-
- this.itemsDummyTooltip = Utils.getString("no_blocking_suggestions");
- this.whitelistDummyTooltip = Utils.getString("whitelisted_page");
-
- // Check current sort direction
- var cols = document.getElementsByTagName("treecol");
- var sortDir = null;
- for (let i = 0; i < cols.length; i++) {
- var col = cols[i];
- var dir = col.getAttribute("sortDirection");
- if (dir && dir != "natural") {
- this.sortColumn = col;
- sortDir = dir;
- }
- }
- if (!this.sortColumn)
- {
- let defaultSort = E("list").getAttribute("defaultSort");
- let match = /^(\w+)\s+(ascending|descending)$/.exec(defaultSort);
- if (match)
- {
- this.sortColumn = E(match[1]);
- if (this.sortColumn)
- {
- sortDir = match[2];
- this.sortColumn.setAttribute("sortDirection", sortDir);
- }
- }
- }
-
- if (sortDir)
- {
- this.sortProc = this.sortProcs[this.sortColumn.id + (sortDir == "descending" ? "Desc" : "")];
- E("list").setAttribute("defaultSort", " ");
- }
-
- // Make sure to update the dummy row every two seconds
- setInterval(function(view) {
- if (!view.data || !view.data.length)
- view.boxObject.invalidateRow(0);
- }, 2000, this);
-
- // Prevent a reference through closures
- boxObject = null;
- },
- get rowCount() {
- return (this.data && this.data.length ? this.data.length : 1);
- },
- getCellText: function(row, col) {
- col = col.id;
- if (col != "type" && col != "address" && col != "filter" && col != "size" && col != "docDomain" && col != "filterSource")
- return "";
- if (this.data && this.data.length) {
- if (row >= this.data.length)
- return "";
- if (col == "type")
- return localizedTypes.get(this.data[row].type);
- else if (col == "filter")
- return (this.data[row].filter || "");
- else if (col == "size")
- {
- let size = getCachedItemSize(this.data[row]);
- if (typeof size == "undefined")
- {
- getItemSize(this.data[row], (size) =>
- {
- if (size)
- this.boxObject.invalidateRow(row)
- });
- }
- return (size ? size.join(" x ") : "");
- }
- else if (col == "docDomain")
- return this.data[row].docDomain + " " + (this.data[row].thirdParty ? docDomainThirdParty : docDomainFirstParty);
- else if (col == "filterSource")
- {
- let filter = getFilter(this.data[row])
- if (!filter)
- return "";
-
- return filter.subscriptions.filter(s => !s.disabled).map(s => getSubscriptionTitle(s)).join(", ");
- }
- else
- return this.data[row].location;
- }
- else {
- // Empty list, show dummy
- if (row > 0 || (col != "address" && col != "filter"))
- return "";
- if (col == "filter") {
- var filter = Policy.isWhitelisted(contentLocation);
- return filter ? filter.text : "";
- }
-
- return (Policy.isWhitelisted(contentLocation) ? this.whitelistDummy : this.itemsDummy);
- }
- },
-
- generateProperties: function(list, properties)
- {
- if (properties)
- {
- // Gecko 21 and below: we have an nsISupportsArray parameter, add atoms
- // to that.
- for (let i = 0; i < list.length; i++)
- if (list[i] in this.atoms)
- properties.AppendElement(this.atoms[list[i]]);
- return null;
- }
- else
- {
- // Gecko 22+: no parameter, just return a string
- return list.join(" ");
- }
- },
-
- getColumnProperties: function(col, properties)
- {
- return this.generateProperties(["col-" + col.id], properties);
- },
-
- getRowProperties: function(row, properties)
- {
- if (row >= this.rowCount)
- return "";
-
- let list = [];
- list.push("selected-" + this.selection.isSelected(row));
-
- let state;
- if (this.data && this.data.length) {
- list.push("dummy-false");
-
- let filter = getFilter(this.data[row]);
- if (filter)
- list.push("filter-disabled-" + filter.disabled);
-
- state = "state-regular";
- if (filter && !filter.disabled)
- {
- if (filter instanceof WhitelistFilter)
- state = "state-whitelisted";
- else if (filter instanceof BlockingFilter)
- state = "state-filtered";
- else if (filter instanceof ElemHideFilter || filter instanceof ElemHideEmulationFilter)
- state = "state-hidden";
- else if (filter instanceof ElemHideException)
- state = "state-hiddenexception";
- }
- }
- else {
- list.push("dummy-true");
-
- state = "state-filtered";
- if (this.data && Policy.isWhitelisted(contentLocation))
- state = "state-whitelisted";
- }
- list.push(state);
- return this.generateProperties(list, properties);
- },
-
- getCellProperties: function(row, col, properties)
- {
- return this.getRowProperties(row, properties) + " " + this.getColumnProperties(col, properties);
- },
-
- cycleHeader: function(col) {
- col = col.id;
-
- col = E(col);
- if (!col)
- return;
-
- var cycle = {
- natural: 'ascending',
- ascending: 'descending',
- descending: 'natural'
- };
-
- var curDirection = "natural";
- if (this.sortColumn == col)
- curDirection = col.getAttribute("sortDirection");
- else if (this.sortColumn)
- this.sortColumn.removeAttribute("sortDirection");
-
- curDirection = cycle[curDirection];
-
- if (curDirection == "natural")
- this.sortProc = null;
- else
- this.sortProc = this.sortProcs[col.id + (curDirection == "descending" ? "Desc" : "")];
-
- if (this.data)
- this.refilter();
-
- col.setAttribute("sortDirection", curDirection);
- this.sortColumn = col;
-
- this.boxObject.invalidate();
- },
-
- isSorted: function() {
- return this.sortProc;
- },
-
- isContainer: function() {return false},
- isContainerOpen: function() {return false},
- isContainerEmpty: function() {return false},
- getLevel: function() {return 0},
- getParentIndex: function() {return -1},
- hasNextSibling: function() {return false},
- toggleOpenState: function() {},
- canDrop: function() {return false},
- drop: function() {},
- getCellValue: function() {return null},
- getProgressMode: function() {return null},
- getImageSrc: function() {return null},
- isSeparator: function() {return false},
- isEditable: function() {return false},
- cycleCell: function() {},
- performAction: function() {},
- performActionOnRow: function() {},
- performActionOnCell: function() {},
- selectionChanged: function() {},
-
- //
- // Custom properties and methods
- //
-
- boxObject: null,
- atoms: null,
- filter: "",
- data: null,
- allData: [],
- dataMap: Object.create(null),
- sortColumn: null,
- sortProc: null,
- resortTimeout: null,
- itemsDummy: null,
- whitelistDummy: null,
- itemsDummyTooltip: null,
- whitelistDummyTooltip: null,
- itemToSelect: null,
-
- sortProcs: {
- address: sortByAddress,
- addressDesc: sortByAddressDesc,
- type: createSortWithFallback(compareType, sortByAddress, false),
- typeDesc: createSortWithFallback(compareType, sortByAddress, true),
- filter: createSortWithFallback(compareFilter, sortByAddress, false),
- filterDesc: createSortWithFallback(compareFilter, sortByAddress, true),
- state: createSortWithFallback(compareState, sortByAddress, false),
- stateDesc: createSortWithFallback(compareState, sortByAddress, true),
- size: createSortWithFallback(compareSize, sortByAddress, false),
- sizeDesc: createSortWithFallback(compareSize, sortByAddress, true),
- docDomain: createSortWithFallback(compareDocDomain, sortByAddress, false),
- docDomainDesc: createSortWithFallback(compareDocDomain, sortByAddress, true),
- filterSource: createSortWithFallback(compareFilterSource, sortByAddress, false),
- filterSourceDesc: createSortWithFallback(compareFilterSource, sortByAddress, true)
- },
- clearData: function(data) {
- var oldRows = this.rowCount;
- this.allData = [];
- this.dataMap = Object.create(null);
- this.refilter();
-
- this.boxObject.rowCountChanged(0, -oldRows);
- this.boxObject.rowCountChanged(0, this.rowCount);
- },
-
- addItem: function(/**RequestEntry*/ item, /**Boolean*/ scanComplete)
- {
- // Merge duplicate entries
- let key = item.location + " " + item.type + " " + item.docDomain;
- if (key in this.dataMap)
- {
- // We know this item already - take over the filter if any and be done with it
- let existing = this.dataMap[key];
- if (item.filter)
- existing.filter = item.filter;
- existing.ids.push(item.id);
-
- this.invalidateItem(existing);
- return;
- }
-
- // Add new item to the list
- // Store original item in orig property - reading out prototype is messed up in Gecko 1.9.2
- item = {__proto__: item, orig: item, ids: [item.id]};
- this.allData.push(item);
- this.dataMap[key] = item;
-
- // Show disabled filters if no other filter applies
- if (!item.filter)
- {
- let disabledMatch = disabledMatcher.matchesAny(item.location, RegExpFilter.typeMap[item.type], item.docDomain, item.thirdParty);
- if (disabledMatch)
- item.filter = disabledMatch.text;
- }
-
- if (!this.matchesFilter(item))
- return;
-
- let index = -1;
- if (this.sortProc && this.sortColumn && this.sortColumn.id == "size")
- {
- // Sorting by size requires accessing content document, and that's
- // dangerous from a content policy (and we are likely called directly
- // from a content policy call). Size data will be inaccurate anyway,
- // delay sorting until later.
- if (this.resortTimeout)
- clearTimeout(this.resortTimeout);
- this.resortTimeout = setTimeout(function(me)
- {
- if (me.sortProc)
- me.data.sort(me.sortProc);
- me.boxObject.invalidate();
- }, 500, this);
- }
- else if (this.sortProc)
- for (var i = 0; index < 0 && i < this.data.length; i++)
- if (this.sortProc(item, this.data[i]) < 0)
- index = i;
-
- if (index >= 0)
- this.data.splice(index, 0, item);
- else {
- this.data.push(item);
- index = this.data.length - 1;
- }
-
- if (this.data.length == 1)
- this.boxObject.invalidateRow(0);
- else
- this.boxObject.rowCountChanged(index, 1);
-
- if (this.itemToSelect == key)
- {
- this.selection.select(index);
- this.boxObject.ensureRowIsVisible(index);
- this.itemToSelect = null;
- }
- else if (!scanComplete && this.selection.currentIndex >= 0) // Keep selected row visible while scanning
- this.boxObject.ensureRowIsVisible(this.selection.currentIndex);
- },
-
- updateFilters: function()
- {
- for (let item of this.allData)
- {
- let filter = getFilter(item);
- if (filter instanceof RegExpFilter && filter.disabled)
- delete item.filter;
- if (!filter)
- {
- let disabledMatch = disabledMatcher.matchesAny(item.location, RegExpFilter.typeMap[item.type], item.docDomain, item.thirdParty);
- if (disabledMatch)
- item.filter = disabledMatch.text;
- }
- }
- this.refilter();
- },
-
- /**
- * Updates the list after a filter or sorting change.
- */
- refilter: function()
- {
- if (this.resortTimeout)
- clearTimeout(this.resortTimeout);
-
- this.data = this.allData.filter(this.matchesFilter, this);
-
- if (this.sortProc)
- this.data.sort(this.sortProc);
- },
-
- /**
- * Tests whether an item matches current list filter.
- * @return {Boolean} true if the item should be shown
- */
- matchesFilter: function(item)
- {
- if (!this.filter)
- return true;
-
- return (item.location.toLowerCase().indexOf(this.filter) >= 0 ||
- (item.filter && item.filter.toLowerCase().indexOf(this.filter) >= 0) ||
- item.type.toLowerCase().indexOf(this.filter.replace(/-/g, "_")) >= 0 ||
- localizedTypes.get(item.type).toLowerCase().indexOf(this.filter) >= 0 ||
- (item.docDomain && item.docDomain.toLowerCase().indexOf(this.filter) >= 0) ||
- (item.docDomain && item.thirdParty && docDomainThirdParty.toLowerCase().indexOf(this.filter) >= 0) ||
- (item.docDomain && !item.thirdParty && docDomainFirstParty.toLowerCase().indexOf(this.filter) >= 0));
- },
-
- setFilter: function(filter) {
- var oldRows = this.rowCount;
-
- this.filter = filter.toLowerCase();
- this.refilter();
-
- var newRows = this.rowCount;
- if (oldRows != newRows)
- this.boxObject.rowCountChanged(oldRows < newRows ? oldRows : newRows, this.rowCount - oldRows);
- this.boxObject.invalidate();
- },
-
- selectAll: function() {
- this.selection.selectAll();
- },
-
- getSelectedItem: function() {
- if (!this.data || this.selection.currentIndex < 0 || this.selection.currentIndex >= this.data.length)
- return null;
-
- return this.data[this.selection.currentIndex];
- },
-
- getAllSelectedItems: function() {
- let result = [];
- if (!this.data)
- return result;
-
- let numRanges = this.selection.getRangeCount();
- for (let i = 0; i < numRanges; i++)
- {
- let min = {};
- let max = {};
- let range = this.selection.getRangeAt(i, min, max);
- for (let j = min.value; j <= max.value; j++)
- {
- if (j >= 0 && j < this.data.length)
- result.push(this.data[j]);
- }
- }
- return result;
- },
-
- getItemAt: function(x, y)
- {
- if (!this.data)
- return null;
-
- var row = this.boxObject.getRowAt(x, y);
- if (row < 0 || row >= this.data.length)
- return null;
-
- return this.data[row];
- },
-
- getColumnAt: function(x, y)
- {
- if (!this.data)
- return null;
-
- let col = {};
- this.boxObject.getCellAt(x, y, {}, col, {});
- return (col.value ? col.value.id : null);
- },
-
- getDummyTooltip: function() {
- if (!this.data || this.data.length)
- return null;
-
- var filter = Policy.isWhitelisted(contentLocation);
- if (filter)
- return {tooltip: this.whitelistDummyTooltip, filter: filter.text};
- else
- return {tooltip: this.itemsDummyTooltip};
- },
-
- invalidateItem: function(item)
- {
- let row = this.data.indexOf(item);
- if (row >= 0)
- this.boxObject.invalidateRow(row);
- }
-}