Index: background.js |
=================================================================== |
--- a/background.js |
+++ b/background.js |
@@ -113,33 +113,33 @@ |
// Adds or removes page action icon according to options. |
function refreshIconAndContextMenu(tab) |
{ |
- // The tab could have been closed by the time this function is called |
- if(!tab) |
+ if(!/^https?:/.test(tab.url)) |
Felix Dahlke
2013/10/24 16:30:34
What if tab is null or undefined? Can that still h
Sebastian Noack
2013/10/25 09:55:03
Did you had a look at the calling code? Maybe I mi
|
return; |
var excluded = isWhitelisted(tab.url); |
- var iconFilename = excluded ? "icons/abp-19-whitelisted.png" : "icons/abp-19.png"; |
+ var iconFilename; |
- if (activeNotification) |
- startIconAnimation(tab, iconFilename); |
+ if (window.safari || !excluded) |
Felix Dahlke
2013/10/24 16:30:34
Why do we always show the normal icon on Safari? D
Sebastian Noack
2013/10/25 09:55:03
First of all because of in Safari icons are global
|
+ iconFilename = "icons/abp-18.png"; |
else |
- chrome.pageAction.setIcon({tabId: tab.id, path: iconFilename}); |
+ iconFilename = "icons/abp-18-whitelisted.png"; |
- // Only show icon for pages we can influence (http: and https:) |
- if(/^https?:/.test(tab.url)) |
- { |
- chrome.pageAction.setTitle({tabId: tab.id, title: "Adblock Plus"}); |
- if ("shouldShowIcon" in localStorage && localStorage["shouldShowIcon"] == "false") |
- chrome.pageAction.hide(tab.id); |
- else |
- chrome.pageAction.show(tab.id); |
+ tab.pageAction.setIcon(iconFilename); |
+ tab.pageAction.setTitle("Adblock Plus"); |
- // Set context menu status according to whether current tab has whitelisted domain |
- if (excluded) |
+ iconAnimation.registerTab(tab, iconFilename); |
+ |
+ if (localStorage.shouldShowIcon == "false") |
+ tab.pageAction.hide(); |
+ else |
+ tab.pageAction.show(); |
+ |
+ // Set context menu status according to whether current tab has whitelisted domain |
+ if (excluded) |
+ if (window.chrome) // TODO: Implement context menus for Safari |
Felix Dahlke
2013/10/24 16:30:34
The surrounding |if (excluded)| needs braces, othe
Sebastian Noack
2013/10/25 09:55:03
Oh, you are right.
|
chrome.contextMenus.removeAll(); |
- else |
- showContextMenu(); |
- } |
+ else |
+ showContextMenu(); |
} |
/** |
@@ -367,8 +367,8 @@ |
function notifyUser() |
{ |
- chrome.tabs.create({ |
- url: chrome.extension.getURL("firstRun.html") |
+ ext.windows.getLastFocused(function(win) { |
Felix Dahlke
2013/10/24 16:30:34
Opening braces always go on their own line, except
Sebastian Noack
2013/10/25 09:55:03
I thought we go with Mozilla coding style?
Felix Dahlke
2013/10/25 14:43:08
No, we have our own: https://adblockplus.org/en/co
|
+ win.openTab(ext.getURL("firstRun.html")); |
}); |
} |
@@ -402,11 +402,14 @@ |
// Set up context menu for user selection of elements to block |
function showContextMenu() |
{ |
+ if (!window.chrome) // TODO: Implement context menus for Safari |
+ return; |
+ |
chrome.contextMenus.removeAll(function() |
{ |
if(typeof localStorage["shouldShowBlockElementMenu"] == "string" && localStorage["shouldShowBlockElementMenu"] == "true") |
{ |
- chrome.contextMenus.create({'title': chrome.i18n.getMessage('block_element'), 'contexts': ['image', 'video', 'audio'], 'onclick': function(info, tab) |
+ chrome.contextMenus.create({"title": chrome.i18n.getMessage("block_element"), "contexts": ["image", "video", "audio"], "onclick": function(info, tab) |
{ |
if(info.srcUrl) |
chrome.tabs.sendRequest(tab.id, {reqtype: "clickhide-new-filter", filter: info.srcUrl}); |
@@ -415,171 +418,139 @@ |
}); |
} |
-/** |
- * Opens Options window or focuses an existing one. |
- * @param {Function} callback function to be called with the window object of |
- * the Options window |
- */ |
function openOptions(callback) |
Felix Dahlke
2013/10/24 16:30:34
Why remove the docs?
Sebastian Noack
2013/10/25 09:55:03
The old docs are deprecated now (the callback is c
Felix Dahlke
2013/11/10 01:07:00
Yeah I guess it's fine, most functions here don't
Wladimir Palant
2013/11/13 07:16:27
I would actually prefer keeping and updating the d
|
{ |
- function findOptions(selectTab) |
- { |
- var views = chrome.extension.getViews({type: "tab"}); |
- for (var i = 0; i < views.length; i++) |
- if ("startSubscriptionSelection" in views[i]) |
- return views[i]; |
+ ext.windows.getLastFocused(function(win) { |
+ win.getAllTabs(function(tabs) { |
+ var optionsUrl = ext.getURL("options.html"); |
- return null; |
- } |
+ for (var i = 0; i < tabs.length; i++) |
+ if (tabs[i].url == optionsUrl) { |
+ tabs[i].activate(); |
+ if (callback) |
+ callback(tabs[i]); |
+ return; |
+ } |
- function selectOptionsTab() |
- { |
- chrome.windows.getAll({populate: true}, function(windows) |
- { |
- var url = chrome.extension.getURL("options.html"); |
- for (var i = 0; i < windows.length; i++) |
- for (var j = 0; j < windows[i].tabs.length; j++) |
- if (windows[i].tabs[j].url == url) |
- chrome.tabs.update(windows[i].tabs[j].id, {selected: true}); |
- }); |
- } |
- |
- var view = findOptions(); |
- if (view) |
- { |
- selectOptionsTab(); |
- callback(view); |
- } |
- else |
- { |
- var onLoad = function() |
- { |
- var view = findOptions(); |
- if (view) |
- callback(view); |
- }; |
- |
- chrome.tabs.create({url: chrome.extension.getURL("options.html")}, function(tab) |
- { |
- if (tab.status == "complete") |
- onLoad(); |
- else |
- { |
- var id = tab.id; |
- var listener = function(tabId, changeInfo, tab) |
- { |
- if (tabId == id && changeInfo.status == "complete") |
- { |
- chrome.tabs.onUpdated.removeListener(listener); |
- onLoad(); |
- } |
- }; |
- chrome.tabs.onUpdated.addListener(listener); |
- } |
- }); |
- } |
-} |
- |
-var iconAnimationTimer = null; |
-var animatedIconTab = null; |
- |
-function stopIconAnimation() |
-{ |
- if (!iconAnimationTimer) |
- return; |
- |
- clearTimeout(iconAnimationTimer); |
- iconAnimationTimer = null; |
- animatedIconTab = null; |
-} |
- |
-function loadImages(imageFiles, callback) |
-{ |
- var images = {}; |
- var imagesLoaded = 0; |
- imageFiles.forEach(function(imageFile) |
- { |
- var image = new Image(); |
- image.src = imageFile; |
- image.addEventListener("load", function() |
- { |
- images[imageFile] = image; |
- if (++imagesLoaded === imageFiles.length) |
- callback(images); |
+ win.openTab(optionsUrl, callback && function(tab) { |
+ tab.onCompleted.addListener(callback); |
+ }); |
}); |
}); |
} |
-function startIconAnimation(tab, iconPath) |
-{ |
- stopIconAnimation(); |
- animatedIconTab = tab; |
+function IconAnimation() { |
+ this._icons = new TabMap(); |
+ this._animatedTabs = new TabMap(); |
+ this._step = 0; |
+}; |
+IconAnimation.prototype = { |
+ update: function(severity) { |
+ if (severity == this._severity) |
+ return; |
- var severitySuffix = activeNotification.severity === "critical" |
- ? "critical" : "information"; |
- var notificationIconPath = "icons/notification-" + severitySuffix + ".png"; |
- var iconFiles = [iconPath, notificationIconPath]; |
- loadImages(iconFiles, function(images) |
- { |
- var icon = images[iconPath]; |
- var notificationIcon = images[notificationIconPath]; |
+ if (!this._severity) |
+ this._start(); |
- var canvas = document.createElement("canvas"); |
- canvas.width = icon.width; |
- canvas.height = icon.height; |
- var context = canvas.getContext("2d"); |
+ this._severity = severity; |
+ }, |
+ stop: function() { |
+ clearInterval(this._interval); |
- var currentFrame = 0; |
- var frameOpacities = [0, 0.2, 0.4, 0.6, 0.8, |
- 1, 1, 1, 1, 1, |
- 0.8, 0.6, 0.4, 0.2, 0]; |
+ delete this._interval; |
+ delete this._severity; |
- function animationStep() |
- { |
- var opacity = frameOpacities[currentFrame]; |
- context.clearRect(0, 0, canvas.width, canvas.height); |
- context.globalAlpha = 1; |
- context.drawImage(icon, 0, 0); |
- context.globalAlpha = opacity; |
- context.drawImage(notificationIcon, 0, 0); |
- var imageData = context.getImageData(0, 0, canvas.width, canvas.height); |
- chrome.pageAction.setIcon({tabId: tab.id, imageData: imageData}); |
+ this._animatedTabs.clear(); |
+ }, |
+ registerTab: function(tab, icon) { |
+ this._icons.set(tab, icon); |
- var interval; |
- currentFrame++; |
- if (currentFrame < frameOpacities.length) |
- { |
- var duration = 3000; |
- interval = duration / frameOpacities.length; |
+ if (this._animatedTabs.has(tab)) |
+ this._updateIcon(tab); |
+ }, |
+ _start: function() { |
+ this._interval = setInterval(function() { |
+ this._getVisibleTabs(function(tabs) { |
+ if (tabs.length == 0) |
+ return; |
+ |
+ for (var i = 0; i < tabs.length; i++) |
+ this._animatedTabs.set(tabs[i], null); |
+ |
+ var interval = setInterval(function() { |
+ this._step++; |
+ tabs.forEach(this._updateIcon.bind(this)); |
Felix Dahlke
2013/10/24 16:30:34
Is the |bind(this)| actually necessary here?
Sebastian Noack
2013/10/25 09:55:03
Yes, it is. _updateIcon() needs this._step and thi
|
+ |
+ if (this._step < 10) |
+ return; |
+ |
+ clearInterval(interval); |
+ setTimeout(function() { |
+ interval = setInterval(function() { |
+ this._step--; |
+ tabs.forEach(this._updateIcon.bind(this)); |
+ |
+ if (this._step > 0) |
+ return; |
+ |
+ clearInterval(interval); |
+ this._animatedTabs.clear(); |
+ }.bind(this), 100); |
+ }.bind(this), 1000); |
+ }.bind(this), 100); |
+ }.bind(this)); |
+ }.bind(this), 15000); |
+ }, |
+ _getVisibleTabs: function(callback) { |
+ ext.windows.getAll(function(windows) { |
+ var tabs = []; |
+ var n = windows.length; |
Felix Dahlke
2013/10/24 16:30:34
I'd love a more descriptive variable name here. So
|
+ |
+ for (var i = 0; i < windows.length; i++) { |
+ if (!windows[i].visible) { |
+ if (tabs.length == --n) |
Felix Dahlke
2013/10/24 16:30:34
tabs.length will always be 0 here, won't it? In th
Sebastian Noack
2013/10/25 09:55:03
Yes it is. When writing the code I felt it would b
|
+ callback(tabs); |
+ |
+ continue; |
+ } |
+ |
+ windows[i].getActiveTab(function(tab) { |
+ tabs.push(tab); |
+ |
+ if (tabs.length == n) |
+ callback(tabs); |
+ }); |
} |
- else |
- { |
- currentFrame = 0; |
- interval = 10000; |
- } |
- iconAnimationTimer = setTimeout(animationStep, interval); |
+ }); |
+ }, |
+ _updateIcon: function(tab) { |
+ var path = this._icons.get(tab); |
+ |
+ if (!path) |
+ return; |
+ |
+ if (this._step > 0) { |
+ var suffix = "-notification-" + this._severity; |
+ |
+ if (this._step < 10) |
+ suffix += "-" + this._step; |
+ |
+ path = path.replace(/(?=\..+$)/, suffix); |
} |
- animationStep(); |
- }); |
-} |
+ |
+ tab.pageAction.setIcon(path); |
+ } |
+}; |
+var iconAnimation = new IconAnimation(); |
Felix Dahlke
2013/10/24 16:30:34
If there's only one instance of IconAnimation, I'd
|
function prepareNotificationIconAndPopup() |
{ |
- activeNotification.onClicked = function() |
- { |
- var tab = animatedIconTab; |
- stopIconAnimation(); |
+ activeNotification.onClicked = function() { |
+ iconAnimation.stop(); |
activeNotification = null; |
- refreshIconAndContextMenu(tab); |
}; |
- chrome.windows.getLastFocused({populate: true}, function(window) |
- { |
- chrome.tabs.query({active: true, windowId: window.id}, function(tabs) |
- { |
- tabs.forEach(refreshIconAndContextMenu); |
- }); |
- }); |
+ iconAnimation.update(activeNotification.severity); |
} |
function showNotification(notification) |
@@ -599,84 +570,71 @@ |
/** |
* 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 |
+ * message but we need to know the frame ID. Try to find it in webRequest"s |
* frame data. |
*/ |
-function getFrameId(tabId, url) |
+function getFrameId(tab, url) |
{ |
- if (tabId in frames) |
- { |
- for (var f in frames[tabId]) |
- { |
- if (getFrameUrl(tabId, f) == url) |
- return f; |
- } |
- } |
+ for (var frameId in frames.get(tab)) |
+ if (getFrameUrl(tab, frameId) == url) |
+ return frameId; |
return -1; |
} |
-chrome.extension.onRequest.addListener(function(request, sender, sendResponse) |
-{ |
- switch (request.reqtype) |
+ext.onMessage.addListener(function (msg, sender, sendResponse) { |
+ switch (msg.type) |
{ |
case "get-settings": |
var hostDomain = null; |
var selectors = null; |
- var tabId = -1; |
- var frameId = -1; |
- if (sender.tab) |
- { |
- tabId = sender.tab.id; |
- frameId = getFrameId(tabId, request.frameUrl); |
- } |
+ var frameId = sender.tab ? getFrameId(sender.tab, msg.frameUrl) : -1; |
+ var enabled = false; |
- var enabled = !isFrameWhitelisted(tabId, frameId, "DOCUMENT") && !isFrameWhitelisted(tabId, frameId, "ELEMHIDE"); |
- if (enabled && request.selectors) |
- { |
- var noStyleRules = false; |
- var host = extractHostFromURL(request.frameUrl); |
- hostDomain = getBaseDomain(host); |
- for (var i = 0; i < noStyleRulesHosts.length; i++) |
+ if (!isFrameWhitelisted(sender.tab, frameId, "DOCUMENT")) |
Felix Dahlke
2013/10/24 16:30:34
I prefer the old code (enabled variable) to nested
|
+ if (!isFrameWhitelisted(sender.tab, frameId, "ELEMHIDE")) { |
+ var enabled = true; |
+ |
+ if (msg.selectors) |
{ |
- var noStyleHost = noStyleRulesHosts[i]; |
- if (host == noStyleHost || (host.length > noStyleHost.length && |
- host.substr(host.length - noStyleHost.length - 1) == "." + noStyleHost)) |
+ var noStyleRules = false; |
+ var host = extractHostFromURL(msg.frameUrl); |
+ hostDomain = getBaseDomain(host); |
+ for (var i = 0; i < noStyleRulesHosts.length; i++) |
{ |
- noStyleRules = true; |
+ var noStyleHost = noStyleRulesHosts[i]; |
+ if (host == noStyleHost || (host.length > noStyleHost.length && |
+ host.substr(host.length - noStyleHost.length - 1) == "." + noStyleHost)) |
+ { |
+ noStyleRules = true; |
+ } |
} |
- } |
- selectors = ElemHide.getSelectorsForDomain(host, false); |
- if (noStyleRules) |
- { |
- selectors = selectors.filter(function(s) |
+ selectors = ElemHide.getSelectorsForDomain(host, false); |
+ if (noStyleRules) |
{ |
- return !/\[style[\^\$]?=/.test(s); |
- }); |
+ selectors = selectors.filter(function(s) |
+ { |
+ return !/\[style[\^\$]?=/.test(s); |
+ }); |
+ } |
} |
} |
sendResponse({enabled: enabled, hostDomain: hostDomain, selectors: selectors}); |
break; |
case "should-collapse": |
- var tabId = -1; |
- var frameId = -1; |
- if (sender.tab) |
- { |
- tabId = sender.tab.id; |
- frameId = getFrameId(tabId, request.documentUrl); |
- } |
+ var frameId = sender.tab ? getFrameId(sender.tab, msg.documentUrl) : -1; |
- if (isFrameWhitelisted(tabId, frameId, "DOCUMENT")) |
+ if (isFrameWhitelisted(sender.tab, frameId, "DOCUMENT")) |
{ |
sendResponse(false); |
break; |
} |
- var requestHost = extractHostFromURL(request.url); |
- var documentHost = extractHostFromURL(request.documentUrl); |
+ var requestHost = extractHostFromURL(msg.url); |
+ var documentHost = extractHostFromURL(msg.documentUrl); |
var thirdParty = isThirdParty(requestHost, documentHost); |
- var filter = defaultMatcher.matchesAny(request.url, request.type, documentHost, thirdParty); |
+ var filter = defaultMatcher.matchesAny(msg.url, msg.mediatype, documentHost, thirdParty); |
if (filter instanceof BlockingFilter) |
{ |
var collapse = filter.collapse; |
@@ -697,20 +655,19 @@ |
} |
break; |
case "add-filters": |
- if (request.filters && request.filters.length) |
+ if (msg.filters && msg.filters.length) |
{ |
- for (var i = 0; i < request.filters.length; i++) |
- FilterStorage.addFilter(Filter.fromText(request.filters[i])); |
+ for (var i = 0; i < msg.filters.length; i++) |
+ FilterStorage.addFilter(Filter.fromText(msg.filters[i])); |
} |
break; |
case "add-subscription": |
- openOptions(function(view) |
- { |
- view.startSubscriptionSelection(request.title, request.url); |
+ openOptions(function(tab) { |
+ tab.sendMessage(msg); |
}); |
break; |
case "forward": |
- chrome.tabs.sendRequest(sender.tab.id, request.request, sendResponse); |
+ tab.sendMessage(msg.payload, sendResponse); |
break; |
default: |
sendResponse({}); |
@@ -719,34 +676,17 @@ |
}); |
// Show icon as page action for all tabs that already exist |
-chrome.windows.getAll({populate: true}, function(windows) |
-{ |
+ext.windows.getAll(function(windows) { |
for (var i = 0; i < windows.length; i++) |
- for (var j = 0; j < windows[i].tabs.length; j++) |
- refreshIconAndContextMenu(windows[i].tabs[j]); |
+ windows[i].getAllTabs(function(tabs) { |
+ tabs.forEach(refreshIconAndContextMenu); |
+ }); |
}); |
// Update icon if a tab changes location |
-chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) |
-{ |
- chrome.tabs.sendRequest(tabId, {reqtype: "clickhide-deactivate"}) |
- if(changeInfo.status == "loading") |
- refreshIconAndContextMenu(tab); |
-}); |
- |
-// Refresh icon when switching tabs or windows |
-chrome.tabs.onActivated.addListener(function(activeInfo) |
-{ |
- refreshIconAndContextMenu(animatedIconTab); |
- chrome.tabs.get(activeInfo.tabId, refreshIconAndContextMenu); |
-}); |
-chrome.windows.onFocusChanged.addListener(function(windowId) |
-{ |
- refreshIconAndContextMenu(animatedIconTab); |
- chrome.tabs.query({active: true, windowId: windowId}, function(tabs) |
- { |
- tabs.forEach(refreshIconAndContextMenu); |
- }); |
+ext.tabs.onBeforeNavigate.addListener(function(tab) { |
+ tab.sendMessage({type: "clickhide-deactivate"}); |
+ refreshIconAndContextMenu(tab); |
}); |
setTimeout(function() |