Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: background.js

Issue 5088751004942336: Issue 370 - Right-clicked element is removed independent of created filter (Closed)
Patch Set: Rebase to rev 3c9cea80c481 Created July 18, 2014, 8:54 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « _locales/zh_TW/store.description ('k') | block.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: background.js
===================================================================
--- a/background.js
+++ b/background.js
@@ -1,6 +1,6 @@
/*
* This file is part of Adblock Plus <http://adblockplus.org/>,
- * Copyright (C) 2006-2013 Eyeo GmbH
+ * Copyright (C) 2006-2014 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
@@ -26,6 +26,13 @@
{
this.Subscription = Subscription;
this.DownloadableSubscription = DownloadableSubscription;
+ this.SpecialSubscription = SpecialSubscription;
+}
+with(require("whitelisting"))
+{
+ this.isWhitelisted = isWhitelisted;
+ this.isFrameWhitelisted = isFrameWhitelisted;
+ this.processKeyException = processKeyException;
}
var FilterStorage = require("filterStorage").FilterStorage;
var ElemHide = require("elemHide").ElemHide;
@@ -34,13 +41,20 @@
var Synchronizer = require("synchronizer").Synchronizer;
var Utils = require("utils").Utils;
var Notification = require("notification").Notification;
+var initAntiAdblockNotification = require("antiadblockInit").initAntiAdblockNotification;
// Some types cannot be distinguished
RegExpFilter.typeMap.OBJECT_SUBREQUEST = RegExpFilter.typeMap.OBJECT;
RegExpFilter.typeMap.MEDIA = RegExpFilter.typeMap.FONT = RegExpFilter.typeMap.OTHER;
-var isFirstRun = false;
+// Chrome on Linux does not fully support chrome.notifications until version 35
+// https://code.google.com/p/chromium/issues/detail?id=291485
+var canUseChromeNotifications = require("info").platform == "chromium"
+ && "notifications" in chrome
+ && (navigator.platform.indexOf("Linux") == -1 || parseInt(require("info").applicationVersion) > 34);
+
var seenDataCorruption = false;
+var filterlistsReinitialized = false;
require("filterNotifier").FilterNotifier.addListener(function(action)
{
if (action == "load")
@@ -48,15 +62,32 @@
var importingOldData = importOldData();
var addonVersion = require("info").addonVersion;
- var prevVersion = localStorage.currentVersion;
- if (prevVersion != addonVersion)
+ var prevVersion = ext.storage.currentVersion;
+
+ // There are no filters stored so we need to reinitialize all filterlists
+ if (!FilterStorage.firstRun && FilterStorage.subscriptions.length === 0)
{
- isFirstRun = !prevVersion;
- localStorage.currentVersion = addonVersion;
+ filterlistsReinitialized = true;
+ prevVersion = null;
+ }
+
+ if (prevVersion != addonVersion || FilterStorage.firstRun)
+ {
+ seenDataCorruption = prevVersion && FilterStorage.firstRun;
+ ext.storage.currentVersion = addonVersion;
if (!importingOldData)
addSubscription(prevVersion);
}
+
+ if (canUseChromeNotifications)
+ initChromeNotifications();
+ initAntiAdblockNotification();
}
+
+ // update browser actions when whitelisting might have changed,
+ // due to loading filters or saving filter changes
+ if (action == "load" || action == "save")
+ refreshIconAndContextMenuForAllPages();
});
// Special-case domains for which we cannot use style-based hiding rules.
@@ -68,76 +99,57 @@
var deprecatedOptions = ["specialCaseYouTube", "experimental", "disableInlineTextAds"];
deprecatedOptions.forEach(function(option)
{
- if (option in localStorage)
- delete localStorage[option];
+ if (option in ext.storage)
+ delete ext.storage[option];
});
}
-// Sets options to defaults, upgrading old options from previous versions as necessary
-function setDefaultOptions()
-{
- function defaultOptionValue(opt, val)
- {
- if(!(opt in localStorage))
- localStorage[opt] = val;
- }
-
- defaultOptionValue("shouldShowBlockElementMenu", "true");
-
- removeDeprecatedOptions();
-}
-
-// Upgrade options before we do anything else.
-setDefaultOptions();
-
-/**
- * Checks whether a page is whitelisted.
- * @param {String} url
- * @param {String} [parentUrl] URL of the parent frame
- * @param {String} [type] content type to be checked, default is "DOCUMENT"
- * @return {Filter} filter that matched the URL or null if not whitelisted
- */
-function isWhitelisted(url, parentUrl, type)
-{
- // Ignore fragment identifier
- var index = url.indexOf("#");
- if (index >= 0)
- url = url.substring(0, index);
-
- var result = defaultMatcher.matchesAny(url, type || "DOCUMENT", extractHostFromURL(parentUrl || url), false);
- return (result instanceof WhitelistFilter ? result : null);
-}
+// Remove deprecated options before we do anything else.
+removeDeprecatedOptions();
var activeNotification = null;
+var contextMenuItem = {
+ title: ext.i18n.getMessage("block_element"),
+ contexts: ["image", "video", "audio"],
+ onclick: function(srcUrl, page)
+ {
+ if (srcUrl)
+ page.sendMessage({type: "clickhide-new-filter", filter: srcUrl});
+ }
+};
+
// Adds or removes browser action icon according to options.
-function refreshIconAndContextMenu(tab)
+function refreshIconAndContextMenu(page)
{
- if(!/^https?:/.test(tab.url))
- return;
+ var whitelisted = isWhitelisted(page.url);
var iconFilename;
- if (require("info").platform == "safari")
- // There is no grayscale version of the icon for whitelisted tabs
+ if (whitelisted && require("info").platform != "safari")
+ // There is no grayscale version of the icon for whitelisted pages
// when using Safari, because icons are grayscale already and icons
- // aren't per tab in Safari.
- iconFilename = "icons/abp-16.png"
+ // aren't per page in Safari.
+ iconFilename = "icons/abp-$size-whitelisted.png";
else
+ iconFilename = "icons/abp-$size.png";
+
+ page.browserAction.setIcon(iconFilename);
+ iconAnimation.registerPage(page, iconFilename);
+
+ // show or hide the context menu entry dependent on whether
+ // adblocking is active on that page
+ page.contextMenus.removeAll();
+
+ if (Prefs.shouldShowBlockElementMenu && !whitelisted && /^https?:/.test(page.url))
+ page.contextMenus.create(contextMenuItem);
+}
+
+function refreshIconAndContextMenuForAllPages()
+{
+ ext.pages.query({}, function(pages)
{
- var excluded = isWhitelisted(tab.url);
- iconFilename = excluded ? "icons/abp-19-whitelisted.png" : "icons/abp-19.png";
- }
-
- tab.browserAction.setIcon(iconFilename);
- tab.browserAction.setTitle(ext.i18n.getMessage("name"));
-
- iconAnimation.registerTab(tab, iconFilename);
-
- // Set context menu status according to whether current tab has whitelisted domain
- if (excluded)
- chrome.contextMenus.removeAll();
- else
- showContextMenu();
+ pages.forEach(refreshIconAndContextMenu);
+ });
}
/**
@@ -217,15 +229,25 @@
addAcceptable = false;
}
+ // Add "anti-adblock messages" subscription for new users and users updating from old ABP versions
+ if (!prevVersion || Services.vc.compare(prevVersion, "1.8") < 0)
+ {
+ var 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);
+ }
+ }
+
if (!addSubscription && !addAcceptable)
return;
function notifyUser()
{
- ext.windows.getLastFocused(function(win)
- {
- win.openTab(ext.getURL("firstRun.html"));
- });
+ ext.pages.open(ext.getURL("firstRun.html"));
}
if (addSubscription)
@@ -255,91 +277,234 @@
notifyUser();
}
-// Set up context menu for user selection of elements to block
-function showContextMenu()
+Prefs.addListener(function(name)
{
- ext.contextMenus.removeAll(function()
- {
- if(typeof localStorage["shouldShowBlockElementMenu"] == "string" && localStorage["shouldShowBlockElementMenu"] == "true")
- {
- ext.contextMenus.create(ext.i18n.getMessage("block_element"), ["image", "video", "audio"], function(srcUrl, tab)
- {
- if(srcUrl)
- tab.sendMessage({type: "clickhide-new-filter", filter: srcUrl});
- });
- }
- });
-}
+ if (name == "shouldShowBlockElementMenu")
+ refreshIconAndContextMenuForAllPages();
+});
/**
- * Opens options tab or focuses an existing one, within the last focused window.
+ * Opens options page or focuses an existing one, within the last focused window.
* @param {Function} callback function to be called with the
- Tab object of the options tab
+ Page object of the options page
*/
function openOptions(callback)
{
- ext.windows.getLastFocused(function(win)
+ ext.pages.query({lastFocusedWindow: true}, function(pages)
{
- win.getAllTabs(function(tabs)
+ var optionsUrl = ext.getURL("options.html");
+
+ for (var i = 0; i < pages.length; i++)
{
- var optionsUrl = ext.getURL("options.html");
+ var page = pages[i];
+ if (page.url == optionsUrl)
+ {
+ page.activate();
+ if (callback)
+ callback(page);
+ return;
+ }
+ }
- for (var i = 0; i < tabs.length; i++)
- {
- if (tabs[i].url == optionsUrl)
- {
- tabs[i].activate();
- if (callback)
- callback(tabs[i]);
- return;
- }
- }
-
- win.openTab(optionsUrl, callback && function(tab)
- {
- tab.onCompleted.addListener(callback);
- });
- });
+ ext.pages.open(optionsUrl, callback);
});
}
function prepareNotificationIconAndPopup()
{
+ var animateIcon = (activeNotification.type !== "question");
activeNotification.onClicked = function()
{
- iconAnimation.stop();
- activeNotification = null;
+ if (animateIcon)
+ iconAnimation.stop();
+ notificationClosed();
};
+ if (animateIcon)
+ iconAnimation.update(activeNotification.type);
+}
- iconAnimation.update(activeNotification.severity);
+function openNotificationLinks()
+{
+ if (activeNotification.links)
+ {
+ activeNotification.links.forEach(function(link)
+ {
+ ext.windows.getLastFocused(function(win)
+ {
+ win.openTab(Utils.getDocLink(link));
+ });
+ });
+ }
+}
+
+function notificationButtonClick(buttonIndex)
+{
+ if (activeNotification.type === "question")
+ {
+ Notification.triggerQuestionListeners(activeNotification.id, buttonIndex === 0);
+ Notification.markAsShown(activeNotification.id);
+ activeNotification.onClicked();
+ }
+ else if (activeNotification.links && activeNotification.links[buttonIndex])
+ {
+ ext.windows.getLastFocused(function(win)
+ {
+ win.openTab(Utils.getDocLink(activeNotification.links[buttonIndex]));
+ });
+ }
+}
+
+function notificationClosed()
+{
+ activeNotification = null;
+}
+
+function imgToBase64(url, callback)
+{
+ var canvas = document.createElement("canvas"),
+ ctx = canvas.getContext("2d"),
+ img = new Image;
+ img.src = url;
+ img.onload = function()
+ {
+ canvas.height = img.height;
+ canvas.width = img.width;
+ ctx.drawImage(img, 0, 0);
+ callback(canvas.toDataURL("image/png"));
+ canvas = null;
+ };
+}
+
+function initChromeNotifications()
+{
+ // Chrome hides notifications in notification center when clicked so we need to clear them
+ function clearActiveNotification(notificationId)
+ {
+ if (activeNotification && activeNotification.type != "question" && !("links" in activeNotification))
+ return;
+
+ chrome.notifications.clear(notificationId, function(wasCleared)
+ {
+ if (wasCleared)
+ notificationClosed();
+ });
+ }
+
+ chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex)
+ {
+ notificationButtonClick(buttonIndex);
+ clearActiveNotification(notificationId);
+ });
+ chrome.notifications.onClicked.addListener(clearActiveNotification);
+ chrome.notifications.onClosed.addListener(notificationClosed);
}
function showNotification(notification)
{
+ if (activeNotification && activeNotification.id === notification.id)
+ return;
+
activeNotification = notification;
+ if (activeNotification.type === "critical" || activeNotification.type === "question")
+ {
+ var hasWebkitNotifications = typeof webkitNotifications !== "undefined";
+ if (hasWebkitNotifications && "createHTMLNotification" in webkitNotifications)
+ {
+ var notification = webkitNotifications.createHTMLNotification("notification.html");
+ notification.show();
+ prepareNotificationIconAndPopup();
+ return;
+ }
- if (activeNotification.severity === "critical"
- && typeof webkitNotifications !== "undefined")
- {
- var notification = webkitNotifications.createHTMLNotification("notification.html");
- notification.show();
- notification.addEventListener("close", prepareNotificationIconAndPopup);
+ var texts = Notification.getLocalizedTexts(notification);
+ var title = texts.title || "";
+ var message = texts.message ? texts.message.replace(/<\/?(a|strong)>/g, "") : "";
+ var iconUrl = ext.getURL("icons/abp-128.png");
+ var hasLinks = activeNotification.links && activeNotification.links.length > 0;
+
+ if (canUseChromeNotifications)
+ {
+ var opts = {
+ type: "basic",
+ title: title,
+ message: message,
+ buttons: [],
+ priority: 2 // We use the highest priority to prevent the notification from closing automatically
+ };
+ if (activeNotification.type === "question")
+ {
+ opts.buttons.push({title: ext.i18n.getMessage("overlay_notification_button_yes")});
+ opts.buttons.push({title: ext.i18n.getMessage("overlay_notification_button_no")});
+ }
+ else
+ {
+ var regex = /<a>(.*?)<\/a>/g;
+ var plainMessage = texts.message || "";
+ var match;
+ while (match = regex.exec(plainMessage))
+ opts.buttons.push({title: match[1]});
+ }
+
+ imgToBase64(iconUrl, function(iconData)
+ {
+ opts["iconUrl"] = iconData;
+ chrome.notifications.create("", opts, function() {});
+ });
+ }
+ else if (hasWebkitNotifications && "createNotification" in webkitNotifications && activeNotification.type !== "question")
+ {
+ if (hasLinks)
+ message += " " + ext.i18n.getMessage("notification_without_buttons");
+
+ imgToBase64(iconUrl, function(iconData)
+ {
+ var notification = webkitNotifications.createNotification(iconData, title, message);
+ notification.show();
+ notification.addEventListener("click", openNotificationLinks, false);
+ notification.addEventListener("close", notificationClosed, false);
+ });
+ }
+ else
+ {
+ var message = title + "\n" + message;
+ if (hasLinks)
+ message += "\n\n" + ext.i18n.getMessage("notification_with_buttons");
+
+ var approved = confirm(message);
+ if (activeNotification.type === "question")
+ notificationButtonClick(approved ? 0 : 1);
+ else if (approved)
+ openNotificationLinks();
+ }
}
- else
- prepareNotificationIconAndPopup();
+ prepareNotificationIconAndPopup();
}
-/**
- * This function is a hack - we only know the tabId and document URL for a
- * message but we need to know the frame ID. Try to find it in webRequest"s
- * frame data.
- */
-function getFrameId(tab, url)
+// This is a hack to speedup loading of the options page on Safari.
+// Once we replaced the background page proxy with message passing
+// this global function should removed.
+function getUserFilters()
{
- for (var frameId in frames.get(tab))
- if (getFrameUrl(tab, frameId) == url)
- return frameId;
- return -1;
+ var filters = [];
+ var exceptions = [];
+
+ for (var i = 0; i < FilterStorage.subscriptions.length; i++)
+ {
+ var subscription = FilterStorage.subscriptions[i];
+ if (!(subscription instanceof SpecialSubscription))
+ continue;
+
+ for (var j = 0; j < subscription.filters.length; j++)
+ {
+ var filter = subscription.filters[j];
+ if (filter instanceof WhitelistFilter && /^@@\|\|([^\/:]+)\^\$document$/.test(filter.text))
+ exceptions.push(RegExp.$1);
+ else
+ filters.push(filter.text);
+ }
+ }
+
+ return {filters: filters, exceptions: exceptions};
}
ext.onMessage.addListener(function (msg, sender, sendResponse)
@@ -347,14 +512,13 @@
switch (msg.type)
{
case "get-selectors":
- var selectors = null;
- var frameId = sender.tab ? getFrameId(sender.tab, msg.frameUrl) : -1;
+ var selectors = [];
- if (!isFrameWhitelisted(sender.tab, frameId, "DOCUMENT") &&
- !isFrameWhitelisted(sender.tab, frameId, "ELEMHIDE"))
+ if (!isFrameWhitelisted(sender.page, sender.frame, "DOCUMENT") &&
+ !isFrameWhitelisted(sender.page, sender.frame, "ELEMHIDE"))
{
var noStyleRules = false;
- var host = extractHostFromURL(msg.frameUrl);
+ var host = extractHostFromFrame(sender.frame);
for (var i = 0; i < noStyleRulesHosts.length; i++)
{
var noStyleHost = noStyleRulesHosts[i];
@@ -377,23 +541,21 @@
sendResponse(selectors);
break;
case "should-collapse":
- var frameId = sender.tab ? getFrameId(sender.tab, msg.documentUrl) : -1;
-
- if (isFrameWhitelisted(sender.tab, frameId, "DOCUMENT"))
+ if (isFrameWhitelisted(sender.page, sender.frame, "DOCUMENT"))
{
sendResponse(false);
break;
}
var requestHost = extractHostFromURL(msg.url);
- var documentHost = extractHostFromURL(msg.documentUrl);
+ var documentHost = extractHostFromFrame(sender.frame);
var thirdParty = isThirdParty(requestHost, documentHost);
var filter = defaultMatcher.matchesAny(msg.url, msg.mediatype, documentHost, thirdParty);
if (filter instanceof BlockingFilter)
{
var collapse = filter.collapse;
if (collapse == null)
- collapse = (localStorage.hidePlaceholders != "false");
+ collapse = Prefs.hidePlaceholders;
sendResponse(collapse);
}
else
@@ -402,9 +564,9 @@
case "get-domain-enabled-state":
// Returns whether this domain is in the exclusion list.
// The browser action popup asks us this.
- if(sender.tab)
+ if(sender.page)
{
- sendResponse({enabled: !isWhitelisted(sender.tab.url)});
+ sendResponse({enabled: !isWhitelisted(sender.page.url)});
return;
}
break;
@@ -416,15 +578,18 @@
}
break;
case "add-subscription":
- openOptions(function(tab)
+ openOptions(function(page)
{
- tab.sendMessage(msg);
+ page.sendMessage(msg);
});
break;
+ case "add-key-exception":
+ processKeyException(msg.token, sender.page, sender.frame);
+ break;
case "forward":
- if (sender.tab)
+ if (sender.page)
{
- sender.tab.sendMessage(msg.payload, sendResponse);
+ sender.page.sendMessage(msg.payload, sendResponse);
// Return true to indicate that we want to call
// sendResponse asynchronously
return true;
@@ -436,23 +601,11 @@
}
});
-// Show icon as browser action for all tabs that already exist
-ext.windows.getAll(function(windows)
+// update icon when page changes location
+ext.pages.onLoading.addListener(function(page)
{
- for (var i = 0; i < windows.length; i++)
- {
- windows[i].getAllTabs(function(tabs)
- {
- tabs.forEach(refreshIconAndContextMenu);
- });
- }
-});
-
-// Update icon if a tab changes location
-ext.tabs.onLoading.addListener(function(tab)
-{
- tab.sendMessage({type: "clickhide-deactivate"});
- refreshIconAndContextMenu(tab);
+ page.sendMessage({type: "clickhide-deactivate"});
+ refreshIconAndContextMenu(page);
});
setTimeout(function()
« no previous file with comments | « _locales/zh_TW/store.description ('k') | block.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld