| LEFT | RIGHT |
| 1 /* | 1 /* |
| 2 * This file is part of Adblock Plus <http://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
| 3 * Copyright (C) 2006-2013 Eyeo GmbH | 3 * Copyright (C) 2006-2015 Eyeo GmbH |
| 4 * | 4 * |
| 5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
| 6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
| 7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
| 8 * | 8 * |
| 9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
| 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
| 13 * | 13 * |
| 14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
| 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| 16 */ | 16 */ |
| 17 | 17 |
| 18 with(require("filterClasses")) | 18 with(require("filterClasses")) |
| 19 { | 19 { |
| 20 this.Filter = Filter; | 20 this.Filter = Filter; |
| 21 this.RegExpFilter = RegExpFilter; | 21 this.RegExpFilter = RegExpFilter; |
| 22 this.BlockingFilter = BlockingFilter; | 22 this.BlockingFilter = BlockingFilter; |
| 23 this.WhitelistFilter = WhitelistFilter; | 23 this.WhitelistFilter = WhitelistFilter; |
| 24 } | 24 } |
| 25 with(require("subscriptionClasses")) | 25 with(require("subscriptionClasses")) |
| 26 { | 26 { |
| 27 this.Subscription = Subscription; | 27 this.Subscription = Subscription; |
| 28 this.DownloadableSubscription = DownloadableSubscription; | 28 this.DownloadableSubscription = DownloadableSubscription; |
| 29 this.SpecialSubscription = SpecialSubscription; |
| 29 } | 30 } |
| 30 with(require("whitelisting")) | 31 with(require("whitelisting")) |
| 31 { | 32 { |
| 32 this.isWhitelisted = isWhitelisted; | 33 this.isPageWhitelisted = isPageWhitelisted; |
| 33 this.isFrameWhitelisted = isFrameWhitelisted; | 34 this.isFrameWhitelisted = isFrameWhitelisted; |
| 34 this.processKeyException = processKeyException; | 35 this.processKey = processKey; |
| 36 this.getKey = getKey; |
| 37 } |
| 38 with(require("url")) |
| 39 { |
| 40 this.stringifyURL = stringifyURL; |
| 41 this.isThirdParty = isThirdParty; |
| 42 this.extractHostFromFrame = extractHostFromFrame; |
| 43 } |
| 44 with(require("icon")) |
| 45 { |
| 46 this.updateIcon = updateIcon; |
| 47 this.startIconAnimation = startIconAnimation; |
| 48 this.stopIconAnimation = stopIconAnimation; |
| 35 } | 49 } |
| 36 var FilterStorage = require("filterStorage").FilterStorage; | 50 var FilterStorage = require("filterStorage").FilterStorage; |
| 37 var ElemHide = require("elemHide").ElemHide; | 51 var ElemHide = require("elemHide").ElemHide; |
| 38 var defaultMatcher = require("matcher").defaultMatcher; | 52 var defaultMatcher = require("matcher").defaultMatcher; |
| 39 var Prefs = require("prefs").Prefs; | 53 var Prefs = require("prefs").Prefs; |
| 40 var Synchronizer = require("synchronizer").Synchronizer; | 54 var Synchronizer = require("synchronizer").Synchronizer; |
| 41 var Utils = require("utils").Utils; | 55 var Utils = require("utils").Utils; |
| 42 var Notification = require("notification").Notification; | 56 var NotificationStorage = require("notification").Notification; |
| 57 var initAntiAdblockNotification = require("antiadblockInit").initAntiAdblockNoti
fication; |
| 58 var parseFilters = require("filterValidation").parseFilters; |
| 43 | 59 |
| 44 // Some types cannot be distinguished | 60 // Some types cannot be distinguished |
| 45 RegExpFilter.typeMap.OBJECT_SUBREQUEST = RegExpFilter.typeMap.OBJECT; | 61 RegExpFilter.typeMap.OBJECT_SUBREQUEST = RegExpFilter.typeMap.OBJECT; |
| 46 RegExpFilter.typeMap.MEDIA = RegExpFilter.typeMap.FONT = RegExpFilter.typeMap.OT
HER; | 62 RegExpFilter.typeMap.MEDIA = RegExpFilter.typeMap.FONT = RegExpFilter.typeMap.OT
HER; |
| 47 | 63 |
| 48 var isFirstRun = false; | 64 // Chrome on Linux does not fully support chrome.notifications until version 35 |
| 65 // https://code.google.com/p/chromium/issues/detail?id=291485 |
| 66 var canUseChromeNotifications = require("info").platform == "chromium" |
| 67 && "notifications" in chrome |
| 68 && (navigator.platform.indexOf("Linux") == -1 || parseInt(require("info").appl
icationVersion, 10) > 34); |
| 69 |
| 49 var seenDataCorruption = false; | 70 var seenDataCorruption = false; |
| 71 var filterlistsReinitialized = false; |
| 50 require("filterNotifier").FilterNotifier.addListener(function(action) | 72 require("filterNotifier").FilterNotifier.addListener(function(action) |
| 51 { | 73 { |
| 52 if (action == "load") | 74 if (action == "load") |
| 53 { | 75 { |
| 54 var importingOldData = importOldData(); | |
| 55 | |
| 56 var addonVersion = require("info").addonVersion; | 76 var addonVersion = require("info").addonVersion; |
| 57 var prevVersion = localStorage.currentVersion; | 77 var prevVersion = ext.storage.currentVersion; |
| 58 if (prevVersion != addonVersion) | 78 |
| 59 { | 79 // There are no filters stored so we need to reinitialize all filterlists |
| 60 isFirstRun = !prevVersion; | 80 if (!FilterStorage.firstRun && FilterStorage.subscriptions.length === 0) |
| 61 localStorage.currentVersion = addonVersion; | 81 { |
| 62 if (!importingOldData) | 82 filterlistsReinitialized = true; |
| 63 addSubscription(prevVersion); | 83 prevVersion = null; |
| 64 } | 84 } |
| 85 |
| 86 if (prevVersion != addonVersion || FilterStorage.firstRun) |
| 87 { |
| 88 seenDataCorruption = prevVersion && FilterStorage.firstRun; |
| 89 ext.storage.currentVersion = addonVersion; |
| 90 addSubscription(prevVersion); |
| 91 } |
| 92 |
| 93 if (canUseChromeNotifications) |
| 94 initChromeNotifications(); |
| 95 initAntiAdblockNotification(); |
| 96 |
| 97 // The "Hide placeholders" option has been removed from the UI in 1.8.8.1285 |
| 98 // So we reset the option for users updating from older versions. |
| 99 if (prevVersion && Services.vc.compare(prevVersion, "1.8.8.1285") < 0) |
| 100 Prefs.hidePlaceholders = true; |
| 65 } | 101 } |
| 66 | 102 |
| 67 // update browser actions when whitelisting might have changed, | 103 // update browser actions when whitelisting might have changed, |
| 68 // due to loading filters or saving filter changes | 104 // due to loading filters or saving filter changes |
| 69 if (action == "load" || action == "save") | 105 if (action == "load" || action == "save") |
| 70 { | 106 refreshIconAndContextMenuForAllPages(); |
| 71 ext.windows.getAll(function(windows) | |
| 72 { | |
| 73 for (var i = 0; i < windows.length; i++) | |
| 74 { | |
| 75 windows[i].getAllTabs(function(tabs) | |
| 76 { | |
| 77 tabs.forEach(refreshIconAndContextMenu); | |
| 78 }); | |
| 79 } | |
| 80 }); | |
| 81 } | |
| 82 }); | 107 }); |
| 83 | 108 |
| 84 // Special-case domains for which we cannot use style-based hiding rules. | 109 // Special-case domains for which we cannot use style-based hiding rules. |
| 85 // See http://crbug.com/68705. | 110 // See http://crbug.com/68705. |
| 86 var noStyleRulesHosts = ["mail.google.com", "mail.yahoo.com", "www.google.com"]; | 111 var noStyleRulesHosts = ["mail.google.com", "mail.yahoo.com", "www.google.com"]; |
| 87 | 112 |
| 113 var htmlPages = new ext.PageMap(); |
| 114 |
| 88 function removeDeprecatedOptions() | 115 function removeDeprecatedOptions() |
| 89 { | 116 { |
| 90 var deprecatedOptions = ["specialCaseYouTube", "experimental", "disableInlineT
extAds"]; | 117 var deprecatedOptions = ["specialCaseYouTube", "experimental", "disableInlineT
extAds"]; |
| 91 deprecatedOptions.forEach(function(option) | 118 deprecatedOptions.forEach(function(option) |
| 92 { | 119 { |
| 93 if (option in localStorage) | 120 if (option in ext.storage) |
| 94 delete localStorage[option]; | 121 delete ext.storage[option]; |
| 95 }); | 122 }); |
| 96 } | 123 } |
| 97 | 124 |
| 98 // Remove deprecated options before we do anything else. | 125 // Remove deprecated options before we do anything else. |
| 99 removeDeprecatedOptions(); | 126 removeDeprecatedOptions(); |
| 100 | 127 |
| 101 var activeNotification = null; | 128 var activeNotification = null; |
| 102 | 129 |
| 103 function getTabStatus(tab) | 130 var contextMenuItem = { |
| 104 { | 131 title: ext.i18n.getMessage("block_element"), |
| 105 var whitelisted = isWhitelisted(tab.url); | 132 contexts: ["image", "video", "audio"], |
| 106 var animated = iconAnimation.tabs.has(tab); | 133 onclick: function(page) |
| 107 | 134 { |
| 108 var icon; | 135 page.sendMessage({type: "clickhide-new-filter"}); |
| 109 if (require("info").platform == "safari") | 136 } |
| 110 // pick an icon with approtiate size for Safari. However since | 137 }; |
| 111 // toolbar item icons can't use different colors, we don't have | 138 |
| 112 // different versions indicating whether the page is whitelisted | 139 // Adds or removes browser action icon according to options. |
| 113 icon = "icons/abp-16"; | 140 function refreshIconAndContextMenu(page) |
| 114 else | 141 { |
| 115 { | 142 var whitelisted = isPageWhitelisted(page); |
| 116 // pick an icon with the approtiate size for Chrome/Opera | 143 updateIcon(page, whitelisted); |
| 117 // that indicates whether the page is whitelisted | |
| 118 if (whitelisted && (!animated || iconAnimation.step < 10)) | |
| 119 icon = "icons/abp-19-whitelisted"; | |
| 120 else | |
| 121 icon = "icons/abp-19"; | |
| 122 } | |
| 123 | |
| 124 // pick the current frame if the icon is currently animating | |
| 125 // in order to indicate a pending notification | |
| 126 if (animated && iconAnimation.step > 0) | |
| 127 { | |
| 128 icon += "-notification-" + iconAnimation.severity; | |
| 129 | |
| 130 if (iconAnimation.step < 10) | |
| 131 icon += "-" + iconAnimation.step; | |
| 132 } | |
| 133 | |
| 134 icon += ".png"; | |
| 135 | |
| 136 return {icon: icon, whitelisted: whitelisted}; | |
| 137 } | |
| 138 | |
| 139 function refreshIconAndContextMenu(tab) | |
| 140 { | |
| 141 var status = getTabStatus(tab); | |
| 142 | |
| 143 // update icon indicating whether the page is whitelisted. | |
| 144 tab.browserAction.setIcon(status.icon); | |
| 145 | 144 |
| 146 // show or hide the context menu entry dependent on whether | 145 // show or hide the context menu entry dependent on whether |
| 147 // adblocking is active in that tab | 146 // adblocking is active on that page |
| 148 if (status.whitelisted || !/^https?:/.test(tab.url)) | 147 page.contextMenus.removeAll(); |
| 149 ext.contextMenus.hideMenuItems(); | 148 if (Prefs.shouldShowBlockElementMenu && !whitelisted && htmlPages.has(page)) |
| 150 else | 149 page.contextMenus.create(contextMenuItem); |
| 151 ext.contextMenus.showMenuItems(); | 150 } |
| 152 } | 151 |
| 153 | 152 function refreshIconAndContextMenuForAllPages() |
| 154 /** | 153 { |
| 155 * Old versions for Opera stored patterns.ini in the localStorage object, this | 154 ext.pages.query({}, function(pages) |
| 156 * will import it into FilterStorage properly. | 155 { |
| 157 * @return {Boolean} true if data import is in progress | 156 pages.forEach(refreshIconAndContextMenu); |
| 158 */ | 157 }); |
| 159 function importOldData() | |
| 160 { | |
| 161 if ("patterns.ini" in localStorage) | |
| 162 { | |
| 163 FilterStorage.loadFromDisk(localStorage["patterns.ini"]); | |
| 164 | |
| 165 var remove = []; | |
| 166 for (var key in localStorage) | |
| 167 if (key.indexOf("patterns.ini") == 0 || key.indexOf("patterns-backup") ==
0) | |
| 168 remove.push(key); | |
| 169 for (var i = 0; i < remove.length; i++) | |
| 170 delete localStorage[remove[i]]; | |
| 171 | |
| 172 return true; | |
| 173 } | |
| 174 else | |
| 175 return false; | |
| 176 } | 158 } |
| 177 | 159 |
| 178 /** | 160 /** |
| 179 * This function is called on an extension update. It will add the default | 161 * This function is called on an extension update. It will add the default |
| 180 * filter subscription if necessary. | 162 * filter subscription if necessary. |
| 181 */ | 163 */ |
| 182 function addSubscription(prevVersion) | 164 function addSubscription(prevVersion) |
| 183 { | 165 { |
| 184 // Make sure to remove "Recommended filters", no longer necessary | 166 // Make sure to remove "Recommended filters", no longer necessary |
| 185 var toRemove = "https://easylist-downloads.adblockplus.org/chrome_supplement.t
xt"; | 167 var toRemove = "https://easylist-downloads.adblockplus.org/chrome_supplement.t
xt"; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 { | 203 { |
| 222 subscription.title = "Allow non-intrusive advertising"; | 204 subscription.title = "Allow non-intrusive advertising"; |
| 223 FilterStorage.addSubscription(subscription); | 205 FilterStorage.addSubscription(subscription); |
| 224 if (subscription instanceof DownloadableSubscription && !subscription.last
Download) | 206 if (subscription instanceof DownloadableSubscription && !subscription.last
Download) |
| 225 Synchronizer.execute(subscription); | 207 Synchronizer.execute(subscription); |
| 226 } | 208 } |
| 227 else | 209 else |
| 228 addAcceptable = false; | 210 addAcceptable = false; |
| 229 } | 211 } |
| 230 | 212 |
| 213 // Add "anti-adblock messages" subscription for new users and users updating f
rom old ABP versions |
| 214 if (!prevVersion || Services.vc.compare(prevVersion, "1.8") < 0) |
| 215 { |
| 216 var subscription = Subscription.fromURL(Prefs.subscriptions_antiadblockurl); |
| 217 if (subscription && !(subscription.url in FilterStorage.knownSubscriptions)) |
| 218 { |
| 219 subscription.disabled = true; |
| 220 FilterStorage.addSubscription(subscription); |
| 221 if (subscription instanceof DownloadableSubscription && !subscription.last
Download) |
| 222 Synchronizer.execute(subscription); |
| 223 } |
| 224 } |
| 225 |
| 231 if (!addSubscription && !addAcceptable) | 226 if (!addSubscription && !addAcceptable) |
| 232 return; | 227 return; |
| 233 | 228 |
| 234 function notifyUser() | 229 function notifyUser() |
| 235 { | 230 { |
| 236 ext.windows.getLastFocused(function(win) | 231 ext.pages.open(ext.getURL("firstRun.html")); |
| 237 { | |
| 238 win.openTab(ext.getURL("firstRun.html")); | |
| 239 }); | |
| 240 } | 232 } |
| 241 | 233 |
| 242 if (addSubscription) | 234 if (addSubscription) |
| 243 { | 235 { |
| 244 // Load subscriptions data | 236 // Load subscriptions data |
| 245 var request = new XMLHttpRequest(); | 237 var request = new XMLHttpRequest(); |
| 246 request.open("GET", "subscriptions.xml"); | 238 request.open("GET", "subscriptions.xml"); |
| 247 request.addEventListener("load", function() | 239 request.addEventListener("load", function() |
| 248 { | 240 { |
| 249 var node = Utils.chooseFilterSubscription(request.responseXML.getElementsB
yTagName("subscription")); | 241 var node = Utils.chooseFilterSubscription(request.responseXML.getElementsB
yTagName("subscription")); |
| 250 var subscription = (node ? Subscription.fromURL(node.getAttribute("url"))
: null); | 242 var subscription = (node ? Subscription.fromURL(node.getAttribute("url"))
: null); |
| 251 if (subscription) | 243 if (subscription) |
| 252 { | 244 { |
| 253 FilterStorage.addSubscription(subscription); | 245 FilterStorage.addSubscription(subscription); |
| 254 subscription.disabled = false; | 246 subscription.disabled = false; |
| 255 subscription.title = node.getAttribute("title"); | 247 subscription.title = node.getAttribute("title"); |
| 256 subscription.homepage = node.getAttribute("homepage"); | 248 subscription.homepage = node.getAttribute("homepage"); |
| 257 if (subscription instanceof DownloadableSubscription && !subscription.la
stDownload) | 249 if (subscription instanceof DownloadableSubscription && !subscription.la
stDownload) |
| 258 Synchronizer.execute(subscription); | 250 Synchronizer.execute(subscription); |
| 259 | 251 |
| 260 notifyUser(); | 252 notifyUser(); |
| 261 } | 253 } |
| 262 }, false); | 254 }, false); |
| 263 request.send(null); | 255 request.send(null); |
| 264 } | 256 } |
| 265 else | 257 else |
| 266 notifyUser(); | 258 notifyUser(); |
| 267 } | 259 } |
| 268 | 260 |
| 269 function setContextMenu() | |
| 270 { | |
| 271 if (Prefs.shouldShowBlockElementMenu) | |
| 272 { | |
| 273 // Register context menu item | |
| 274 ext.contextMenus.addMenuItem(ext.i18n.getMessage("block_element"), ["image",
"video", "audio"], function(srcUrl, tab) | |
| 275 { | |
| 276 if (srcUrl) | |
| 277 tab.sendMessage({type: "clickhide-new-filter", filter: srcUrl}); | |
| 278 }); | |
| 279 } | |
| 280 else | |
| 281 ext.contextMenus.removeMenuItems(); | |
| 282 } | |
| 283 | |
| 284 Prefs.addListener(function(name) | 261 Prefs.addListener(function(name) |
| 285 { | 262 { |
| 286 if (name == "shouldShowBlockElementMenu") | 263 if (name == "shouldShowBlockElementMenu") |
| 287 setContextMenu(); | 264 refreshIconAndContextMenuForAllPages(); |
| 288 }); | 265 }); |
| 289 setContextMenu(); | |
| 290 | |
| 291 /** | |
| 292 * Opens options tab or focuses an existing one, within the last focused window
. | |
| 293 * @param {Function} callback function to be called with the | |
| 294 Tab object of the options tab | |
| 295 */ | |
| 296 function openOptions(callback) | |
| 297 { | |
| 298 ext.windows.getLastFocused(function(win) | |
| 299 { | |
| 300 win.getAllTabs(function(tabs) | |
| 301 { | |
| 302 var optionsUrl = ext.getURL("options.html"); | |
| 303 | |
| 304 for (var i = 0; i < tabs.length; i++) | |
| 305 { | |
| 306 if (tabs[i].url == optionsUrl) | |
| 307 { | |
| 308 tabs[i].activate(); | |
| 309 if (callback) | |
| 310 callback(tabs[i]); | |
| 311 return; | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 win.openTab(optionsUrl, callback && function(tab) | |
| 316 { | |
| 317 tab.onCompleted.addListener(callback); | |
| 318 }); | |
| 319 }); | |
| 320 }); | |
| 321 } | |
| 322 | 266 |
| 323 function prepareNotificationIconAndPopup() | 267 function prepareNotificationIconAndPopup() |
| 324 { | 268 { |
| 269 var animateIcon = (activeNotification.type !== "question"); |
| 325 activeNotification.onClicked = function() | 270 activeNotification.onClicked = function() |
| 326 { | 271 { |
| 327 iconAnimation.stop(); | 272 if (animateIcon) |
| 328 activeNotification = null; | 273 stopIconAnimation(); |
| 274 notificationClosed(); |
| 329 }; | 275 }; |
| 330 iconAnimation.update(activeNotification.severity); | 276 if (animateIcon) |
| 331 } | 277 startIconAnimation(activeNotification.type); |
| 332 | 278 } |
| 333 function openNotificationLinks() | 279 |
| 280 function openNotificationLinks() |
| 334 { | 281 { |
| 335 if (activeNotification.links) | 282 if (activeNotification.links) |
| 336 { | 283 { |
| 337 activeNotification.links.forEach(function(link) | 284 activeNotification.links.forEach(function(link) |
| 338 { | 285 { |
| 339 ext.windows.getLastFocused(function(win) | 286 ext.windows.getLastFocused(function(win) |
| 340 { | 287 { |
| 341 win.openTab(Utils.getDocLink(link)); | 288 win.openTab(Utils.getDocLink(link)); |
| 342 }); | 289 }); |
| 343 }); | 290 }); |
| 344 } | 291 } |
| 345 } | 292 } |
| 346 | 293 |
| 347 function notificationButtonClick(id, index) | 294 function notificationButtonClick(buttonIndex) |
| 348 { | 295 { |
| 349 if (activeNotification.links && activeNotification.links[index]) | 296 if (activeNotification.type === "question") |
| 297 { |
| 298 NotificationStorage.triggerQuestionListeners(activeNotification.id, buttonIn
dex === 0); |
| 299 NotificationStorage.markAsShown(activeNotification.id); |
| 300 activeNotification.onClicked(); |
| 301 } |
| 302 else if (activeNotification.links && activeNotification.links[buttonIndex]) |
| 350 { | 303 { |
| 351 ext.windows.getLastFocused(function(win) | 304 ext.windows.getLastFocused(function(win) |
| 352 { | 305 { |
| 353 win.openTab(Utils.getDocLink(activeNotification.links[index])); | 306 win.openTab(Utils.getDocLink(activeNotification.links[buttonIndex])); |
| 354 }); | 307 }); |
| 355 } | 308 } |
| 356 } | 309 } |
| 357 | 310 |
| 311 function notificationClosed() |
| 312 { |
| 313 activeNotification = null; |
| 314 } |
| 315 |
| 316 function imgToBase64(url, callback) |
| 317 { |
| 318 var canvas = document.createElement("canvas"), |
| 319 ctx = canvas.getContext("2d"), |
| 320 img = new Image; |
| 321 img.src = url; |
| 322 img.onload = function() |
| 323 { |
| 324 canvas.height = img.height; |
| 325 canvas.width = img.width; |
| 326 ctx.drawImage(img, 0, 0); |
| 327 callback(canvas.toDataURL("image/png")); |
| 328 canvas = null; |
| 329 }; |
| 330 } |
| 331 |
| 332 function initChromeNotifications() |
| 333 { |
| 334 // Chrome hides notifications in notification center when clicked so we need t
o clear them |
| 335 function clearActiveNotification(notificationId) |
| 336 { |
| 337 if (activeNotification && activeNotification.type != "question" && !("links"
in activeNotification)) |
| 338 return; |
| 339 |
| 340 chrome.notifications.clear(notificationId, function(wasCleared) |
| 341 { |
| 342 if (wasCleared) |
| 343 notificationClosed(); |
| 344 }); |
| 345 } |
| 346 |
| 347 chrome.notifications.onButtonClicked.addListener(function(notificationId, butt
onIndex) |
| 348 { |
| 349 notificationButtonClick(buttonIndex); |
| 350 clearActiveNotification(notificationId); |
| 351 }); |
| 352 chrome.notifications.onClicked.addListener(clearActiveNotification); |
| 353 chrome.notifications.onClosed.addListener(notificationClosed); |
| 354 } |
| 355 |
| 358 function showNotification(notification) | 356 function showNotification(notification) |
| 359 { | 357 { |
| 358 if (activeNotification && activeNotification.id === notification.id) |
| 359 return; |
| 360 |
| 360 activeNotification = notification; | 361 activeNotification = notification; |
| 361 if (activeNotification.severity === "critical") | 362 if (activeNotification.type === "critical" || activeNotification.type === "que
stion") |
| 362 { | 363 { |
| 363 var hasWebkitNotifications = typeof webkitNotifications !== "undefined"; | 364 var texts = NotificationStorage.getLocalizedTexts(notification); |
| 364 if (hasWebkitNotifications && "createHTMLNotification" in webkitNotification
s) | |
| 365 { | |
| 366 var notification = webkitNotifications.createHTMLNotification("notificatio
n.html"); | |
| 367 notification.show(); | |
| 368 notification.addEventListener("close", prepareNotificationIconAndPopup, fa
lse); | |
| 369 return; | |
| 370 } | |
| 371 | |
| 372 var texts = Notification.getLocalizedTexts(notification); | |
| 373 var title = texts.title || ""; | 365 var title = texts.title || ""; |
| 374 var message = texts.message ? texts.message.replace(/<\/?(a|strong)>/g, "")
: ""; | 366 var message = texts.message ? texts.message.replace(/<\/?(a|strong)>/g, "")
: ""; |
| 375 var iconUrl = ext.getURL("icons/abp-128.png"); | 367 var iconUrl = ext.getURL("icons/abp-128.png"); |
| 376 var hasLinks = activeNotification.links && activeNotification.links.length >
0; | 368 var hasLinks = activeNotification.links && activeNotification.links.length >
0; |
| 377 // Chrome on Linux does not fully support chrome.notifications yet | 369 |
| 378 // https://code.google.com/p/chromium/issues/detail?id=291485 | 370 if (canUseChromeNotifications) |
| 379 if (require("info").platform == "chromium" && | |
| 380 "notifications" in chrome && | |
| 381 navigator.platform.indexOf("Linux") == -1) | |
| 382 { | 371 { |
| 383 var opts = { | 372 var opts = { |
| 384 type: "basic", | 373 type: "basic", |
| 385 title: title, | 374 title: title, |
| 386 message: message, | 375 message: message, |
| 387 iconUrl: iconUrl, | 376 buttons: [], |
| 388 buttons: [] | 377 priority: 2 // We use the highest priority to prevent the notification f
rom closing automatically |
| 389 }; | 378 }; |
| 390 var regex = /<a>(.*?)<\/a>/g; | 379 if (activeNotification.type === "question") |
| 391 var plainMessage = texts.message || ""; | 380 { |
| 392 var match; | 381 opts.buttons.push({title: ext.i18n.getMessage("overlay_notification_butt
on_yes")}); |
| 393 while (match = regex.exec(plainMessage)) | 382 opts.buttons.push({title: ext.i18n.getMessage("overlay_notification_butt
on_no")}); |
| 394 opts.buttons.push({title: match[1]}); | 383 } |
| 395 | 384 else |
| 396 var notification = chrome.notifications; | 385 { |
| 397 notification.create("", opts, function() {}); | 386 var regex = /<a>(.*?)<\/a>/g; |
| 398 notification.onClosed.addListener(prepareNotificationIconAndPopup); | 387 var plainMessage = texts.message || ""; |
| 399 notification.onButtonClicked.addListener(notificationButtonClick); | 388 var match; |
| 400 } | 389 while (match = regex.exec(plainMessage)) |
| 401 else if (hasWebkitNotifications && "createNotification" in webkitNotificatio
ns) | 390 opts.buttons.push({title: match[1]}); |
| 391 } |
| 392 |
| 393 imgToBase64(iconUrl, function(iconData) |
| 394 { |
| 395 opts["iconUrl"] = iconData; |
| 396 chrome.notifications.create("", opts, function() {}); |
| 397 }); |
| 398 } |
| 399 else if ("Notification" in window && activeNotification.type !== "question") |
| 402 { | 400 { |
| 403 if (hasLinks) | 401 if (hasLinks) |
| 404 message += " " + ext.i18n.getMessage("notification_without_buttons"); | 402 message += " " + ext.i18n.getMessage("notification_without_buttons"); |
| 405 | 403 |
| 406 var notification = webkitNotifications.createNotification(iconUrl, title,
message); | 404 imgToBase64(iconUrl, function(iconData) |
| 407 notification.show(); | 405 { |
| 408 notification.addEventListener("close", prepareNotificationIconAndPopup, fa
lse); | 406 var notification = new Notification( |
| 409 notification.addEventListener("click", openNotificationLinks, false); | 407 title, |
| 408 { |
| 409 lang: Utils.appLocale, |
| 410 dir: ext.i18n.getMessage("@@bidi_dir"), |
| 411 body: message, |
| 412 icon: iconData |
| 413 } |
| 414 ); |
| 415 |
| 416 notification.addEventListener("click", openNotificationLinks); |
| 417 notification.addEventListener("close", notificationClosed); |
| 418 }); |
| 410 } | 419 } |
| 411 else | 420 else |
| 412 { | 421 { |
| 413 var message = title + "\n" + message; | 422 var message = title + "\n" + message; |
| 414 if (hasLinks) | 423 if (hasLinks) |
| 415 message += "\n\n" + ext.i18n.getMessage("notification_with_buttons"); | 424 message += "\n\n" + ext.i18n.getMessage("notification_with_buttons"); |
| 416 | 425 |
| 417 if (confirm(message)) | 426 var approved = confirm(message); |
| 427 if (activeNotification.type === "question") |
| 428 notificationButtonClick(approved ? 0 : 1); |
| 429 else if (approved) |
| 418 openNotificationLinks(); | 430 openNotificationLinks(); |
| 419 prepareNotificationIconAndPopup(); | 431 } |
| 420 } | 432 } |
| 421 } | 433 prepareNotificationIconAndPopup(); |
| 422 else | 434 } |
| 423 prepareNotificationIconAndPopup(); | 435 |
| 436 // This is a hack to speedup loading of the options page on Safari. |
| 437 // Once we replaced the background page proxy with message passing |
| 438 // this global function should removed. |
| 439 function getUserFilters() |
| 440 { |
| 441 var filters = []; |
| 442 var exceptions = []; |
| 443 |
| 444 for (var i = 0; i < FilterStorage.subscriptions.length; i++) |
| 445 { |
| 446 var subscription = FilterStorage.subscriptions[i]; |
| 447 if (!(subscription instanceof SpecialSubscription)) |
| 448 continue; |
| 449 |
| 450 for (var j = 0; j < subscription.filters.length; j++) |
| 451 { |
| 452 var filter = subscription.filters[j]; |
| 453 if (filter instanceof WhitelistFilter && /^@@\|\|([^\/:]+)\^\$document$/.
test(filter.text)) |
| 454 exceptions.push(RegExp.$1); |
| 455 else |
| 456 filters.push(filter.text); |
| 457 } |
| 458 } |
| 459 |
| 460 return {filters: filters, exceptions: exceptions}; |
| 424 } | 461 } |
| 425 | 462 |
| 426 ext.onMessage.addListener(function (msg, sender, sendResponse) | 463 ext.onMessage.addListener(function (msg, sender, sendResponse) |
| 427 { | 464 { |
| 428 switch (msg.type) | 465 switch (msg.type) |
| 429 { | 466 { |
| 430 case "get-selectors": | 467 case "get-selectors": |
| 431 var selectors = null; | 468 var selectors = []; |
| 432 | 469 |
| 433 if (!isFrameWhitelisted(sender.tab, sender.frame, "DOCUMENT") && | 470 if (!isFrameWhitelisted(sender.page, sender.frame, "DOCUMENT") && |
| 434 !isFrameWhitelisted(sender.tab, sender.frame, "ELEMHIDE")) | 471 !isFrameWhitelisted(sender.page, sender.frame, "ELEMHIDE")) |
| 435 { | 472 { |
| 436 var noStyleRules = false; | 473 var noStyleRules = false; |
| 437 var host = extractHostFromURL(sender.frame.url); | 474 var host = extractHostFromFrame(sender.frame); |
| 438 for (var i = 0; i < noStyleRulesHosts.length; i++) | 475 for (var i = 0; i < noStyleRulesHosts.length; i++) |
| 439 { | 476 { |
| 440 var noStyleHost = noStyleRulesHosts[i]; | 477 var noStyleHost = noStyleRulesHosts[i]; |
| 441 if (host == noStyleHost || (host.length > noStyleHost.length && | 478 if (host == noStyleHost || (host.length > noStyleHost.length && |
| 442 host.substr(host.length - noStyleHost.leng
th - 1) == "." + noStyleHost)) | 479 host.substr(host.length - noStyleHost.leng
th - 1) == "." + noStyleHost)) |
| 443 { | 480 { |
| 444 noStyleRules = true; | 481 noStyleRules = true; |
| 445 } | 482 } |
| 446 } | 483 } |
| 447 selectors = ElemHide.getSelectorsForDomain(host, false); | 484 selectors = ElemHide.getSelectorsForDomain(host, false); |
| 448 if (noStyleRules) | 485 if (noStyleRules) |
| 449 { | 486 { |
| 450 selectors = selectors.filter(function(s) | 487 selectors = selectors.filter(function(s) |
| 451 { | 488 { |
| 452 return !/\[style[\^\$]?=/.test(s); | 489 return !/\[style[\^\$]?=/.test(s); |
| 453 }); | 490 }); |
| 454 } | 491 } |
| 455 } | 492 } |
| 456 | 493 |
| 457 sendResponse(selectors); | 494 sendResponse(selectors); |
| 458 break; | 495 break; |
| 459 case "should-collapse": | 496 case "should-collapse": |
| 460 if (isFrameWhitelisted(sender.tab, sender.frame, "DOCUMENT")) | 497 if (isFrameWhitelisted(sender.page, sender.frame, "DOCUMENT")) |
| 461 { | 498 { |
| 462 sendResponse(false); | 499 sendResponse(false); |
| 463 break; | 500 break; |
| 464 } | 501 } |
| 465 | 502 |
| 466 var requestHost = extractHostFromURL(msg.url); | 503 var url = new URL(msg.url); |
| 467 var documentHost = extractHostFromURL(sender.frame.url); | 504 var documentHost = extractHostFromFrame(sender.frame); |
| 468 var thirdParty = isThirdParty(requestHost, documentHost); | 505 var filter = defaultMatcher.matchesAny( |
| 469 var filter = defaultMatcher.matchesAny(msg.url, msg.mediatype, documentHos
t, thirdParty); | 506 stringifyURL(url), msg.mediatype, |
| 507 documentHost, isThirdParty(url, documentHost) |
| 508 ); |
| 509 |
| 470 if (filter instanceof BlockingFilter) | 510 if (filter instanceof BlockingFilter) |
| 471 { | 511 { |
| 472 var collapse = filter.collapse; | 512 var collapse = filter.collapse; |
| 473 if (collapse == null) | 513 if (collapse == null) |
| 474 collapse = Prefs.hidePlaceholders; | 514 collapse = Prefs.hidePlaceholders; |
| 475 sendResponse(collapse); | 515 sendResponse(collapse); |
| 476 } | 516 } |
| 477 else | 517 else |
| 478 sendResponse(false); | 518 sendResponse(false); |
| 479 break; | 519 break; |
| 480 case "get-domain-enabled-state": | 520 case "get-domain-enabled-state": |
| 481 // Returns whether this domain is in the exclusion list. | 521 // Returns whether this domain is in the exclusion list. |
| 482 // The browser action popup asks us this. | 522 // The browser action popup asks us this. |
| 483 if(sender.tab) | 523 if(sender.page) |
| 484 { | 524 { |
| 485 sendResponse({enabled: !isWhitelisted(sender.tab.url)}); | 525 sendResponse({enabled: !isPageWhitelisted(sender.page)}); |
| 486 return; | 526 return; |
| 487 } | 527 } |
| 488 break; | 528 break; |
| 489 case "add-filters": | 529 case "add-filters": |
| 490 if (msg.filters && msg.filters.length) | 530 var filters; |
| 491 { | 531 try |
| 492 for (var i = 0; i < msg.filters.length; i++) | 532 { |
| 493 FilterStorage.addFilter(Filter.fromText(msg.filters[i])); | 533 filters = parseFilters(msg.text); |
| 494 } | 534 } |
| 535 catch (error) |
| 536 { |
| 537 sendResponse({status: "invalid", error: error}); |
| 538 break; |
| 539 } |
| 540 |
| 541 for (var i = 0; i < filters.length; i++) |
| 542 FilterStorage.addFilter(filters[i]); |
| 543 |
| 544 sendResponse({status: "ok"}); |
| 495 break; | 545 break; |
| 496 case "add-subscription": | 546 case "add-subscription": |
| 497 openOptions(function(tab) | 547 ext.showOptions(function(page) |
| 498 { | 548 { |
| 499 tab.sendMessage(msg); | 549 page.sendMessage(msg); |
| 500 }); | 550 }); |
| 501 break; | 551 break; |
| 502 case "add-key-exception": | 552 case "add-sitekey": |
| 503 processKeyException(msg.token, sender.tab, sender.frame); | 553 processKey(msg.token, sender.page, sender.frame); |
| 554 break; |
| 555 case "report-html-page": |
| 556 htmlPages.set(sender.page, null); |
| 557 refreshIconAndContextMenu(sender.page); |
| 504 break; | 558 break; |
| 505 case "forward": | 559 case "forward": |
| 506 if (sender.tab) | 560 if (sender.page) |
| 507 { | 561 { |
| 508 sender.tab.sendMessage(msg.payload, sendResponse); | 562 if (msg.expectsResponse) |
| 509 // Return true to indicate that we want to call | 563 { |
| 510 // sendResponse asynchronously | 564 sender.page.sendMessage(msg.payload, sendResponse); |
| 511 return true; | 565 return true; |
| 512 } | 566 } |
| 513 break; | 567 |
| 514 default: | 568 sender.page.sendMessage(msg.payload); |
| 515 sendResponse({}); | 569 } |
| 516 break; | 570 break; |
| 517 } | 571 } |
| 518 }); | 572 }); |
| 519 | 573 |
| 520 // Update icon if a tab changes location | 574 // update icon when page changes location |
| 521 ext.tabs.onLoading.addListener(function(tab) | 575 ext.pages.onLoading.addListener(function(page) |
| 522 { | 576 { |
| 523 tab.sendMessage({type: "clickhide-deactivate"}); | 577 page.sendMessage({type: "clickhide-deactivate"}); |
| 524 refreshIconAndContextMenu(tab); | 578 refreshIconAndContextMenu(page); |
| 525 }); | 579 }); |
| 526 | 580 |
| 527 setTimeout(function() | 581 setTimeout(function() |
| 528 { | 582 { |
| 529 var notificationToShow = Notification.getNextToShow(); | 583 var notificationToShow = NotificationStorage.getNextToShow(); |
| 530 if (notificationToShow) | 584 if (notificationToShow) |
| 531 showNotification(notificationToShow); | 585 showNotification(notificationToShow); |
| 532 }, 3 * 60 * 1000); | 586 }, 3 * 60 * 1000); |
| LEFT | RIGHT |