| 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() |