| Left: | ||
| Right: |
| LEFT | RIGHT |
|---|---|
| 1 /* | 1 /* |
| 2 * This file is part of Adblock Plus <http://adblockplus.org/>, | 2 * This file is part of Adblock Plus <http://adblockplus.org/>, |
| 3 * Copyright (C) 2006-2013 Eyeo GmbH | 3 * Copyright (C) 2006-2014 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 | |
| 25 // TODO: The import below isn't necessary anymore with | |
| 26 // https://hg.adblockplus.org/adblockplus/rev/7eb29729a72c | |
| 27 // However due to https://issues.adblockplus.org/ticket/189 | |
| 28 // we can't update the subrepo yet. So please remove this | |
| 29 // line when the issue was fixed and the subrepo was updated. | |
| 30 this.ActiveFilter = ActiveFilter; | |
| 24 } | 31 } |
| 25 with(require("subscriptionClasses")) | 32 with(require("subscriptionClasses")) |
| 26 { | 33 { |
| 27 this.Subscription = Subscription; | 34 this.Subscription = Subscription; |
| 28 this.DownloadableSubscription = DownloadableSubscription; | 35 this.DownloadableSubscription = DownloadableSubscription; |
| 29 } | 36 } |
| 30 with(require("whitelisting")) | 37 with(require("whitelisting")) |
| 31 { | 38 { |
| 32 this.isWhitelisted = isWhitelisted; | 39 this.isWhitelisted = isWhitelisted; |
| 33 this.isFrameWhitelisted = isFrameWhitelisted; | 40 this.isFrameWhitelisted = isFrameWhitelisted; |
| 34 this.processKeyException = processKeyException; | 41 this.processKeyException = processKeyException; |
| 35 } | 42 } |
| 36 var FilterStorage = require("filterStorage").FilterStorage; | 43 var FilterStorage = require("filterStorage").FilterStorage; |
| 37 var ElemHide = require("elemHide").ElemHide; | 44 var ElemHide = require("elemHide").ElemHide; |
| 38 var defaultMatcher = require("matcher").defaultMatcher; | 45 var defaultMatcher = require("matcher").defaultMatcher; |
| 39 var Prefs = require("prefs").Prefs; | 46 var Prefs = require("prefs").Prefs; |
| 40 var Synchronizer = require("synchronizer").Synchronizer; | 47 var Synchronizer = require("synchronizer").Synchronizer; |
| 41 var Utils = require("utils").Utils; | 48 var Utils = require("utils").Utils; |
| 42 var Notification = require("notification").Notification; | 49 var Notification = require("notification").Notification; |
| 50 var initAntiAdblockNotification = require("antiadblockInit").initAntiAdblockNoti fication; | |
| 43 | 51 |
| 44 // Some types cannot be distinguished | 52 // Some types cannot be distinguished |
| 45 RegExpFilter.typeMap.OBJECT_SUBREQUEST = RegExpFilter.typeMap.OBJECT; | 53 RegExpFilter.typeMap.OBJECT_SUBREQUEST = RegExpFilter.typeMap.OBJECT; |
| 46 RegExpFilter.typeMap.MEDIA = RegExpFilter.typeMap.FONT = RegExpFilter.typeMap.OT HER; | 54 RegExpFilter.typeMap.MEDIA = RegExpFilter.typeMap.FONT = RegExpFilter.typeMap.OT HER; |
| 47 | 55 |
| 56 // Chrome on Linux does not fully support chrome.notifications until version 35 | |
| 57 // https://code.google.com/p/chromium/issues/detail?id=291485 | |
| 58 var canUseChromeNotifications = require("info").platform == "chromium" | |
| 59 && "notifications" in chrome | |
| 60 && (navigator.platform.indexOf("Linux") == -1 || parseInt(require("info").appl icationVersion) > 34); | |
| 61 | |
| 48 var isFirstRun = false; | 62 var isFirstRun = false; |
| 49 var seenDataCorruption = false; | 63 var seenDataCorruption = false; |
| 50 require("filterNotifier").FilterNotifier.addListener(function(action) | 64 require("filterNotifier").FilterNotifier.addListener(function(action) |
| 51 { | 65 { |
| 52 if (action == "load") | 66 if (action == "load") |
| 53 { | 67 { |
| 54 var importingOldData = importOldData(); | 68 var importingOldData = importOldData(); |
| 55 | 69 |
| 56 var addonVersion = require("info").addonVersion; | 70 var addonVersion = require("info").addonVersion; |
| 57 var prevVersion = localStorage.currentVersion; | 71 var prevVersion = ext.storage.currentVersion; |
| 58 if (prevVersion != addonVersion) | 72 if (prevVersion != addonVersion) |
| 59 { | 73 { |
| 60 isFirstRun = !prevVersion; | 74 isFirstRun = !prevVersion; |
| 61 localStorage.currentVersion = addonVersion; | 75 ext.storage.currentVersion = addonVersion; |
| 62 if (!importingOldData) | 76 if (!importingOldData) |
| 63 addSubscription(prevVersion); | 77 addSubscription(prevVersion); |
| 64 } | 78 } |
| 79 | |
| 80 if (canUseChromeNotifications) | |
| 81 initChromeNotifications(); | |
| 82 initAntiAdblockNotification(); | |
| 83 } | |
| 84 | |
| 85 // update browser actions when whitelisting might have changed, | |
| 86 // due to loading filters or saving filter changes | |
| 87 if (action == "load" || action == "save") | |
| 88 { | |
| 89 ext.pages.query({}, function(pages) | |
| 90 { | |
| 91 pages.forEach(refreshIconAndContextMenu); | |
| 92 }); | |
| 65 } | 93 } |
| 66 }); | 94 }); |
| 67 | 95 |
| 68 // Special-case domains for which we cannot use style-based hiding rules. | 96 // Special-case domains for which we cannot use style-based hiding rules. |
| 69 // See http://crbug.com/68705. | 97 // See http://crbug.com/68705. |
| 70 var noStyleRulesHosts = ["mail.google.com", "mail.yahoo.com", "www.google.com"]; | 98 var noStyleRulesHosts = ["mail.google.com", "mail.yahoo.com", "www.google.com"]; |
| 71 | 99 |
| 72 function removeDeprecatedOptions() | 100 function removeDeprecatedOptions() |
| 73 { | 101 { |
| 74 var deprecatedOptions = ["specialCaseYouTube", "experimental", "disableInlineT extAds"]; | 102 var deprecatedOptions = ["specialCaseYouTube", "experimental", "disableInlineT extAds"]; |
| 75 deprecatedOptions.forEach(function(option) | 103 deprecatedOptions.forEach(function(option) |
| 76 { | 104 { |
| 77 if (option in localStorage) | 105 if (option in ext.storage) |
| 78 delete localStorage[option]; | 106 delete ext.storage[option]; |
| 79 }); | 107 }); |
| 80 } | 108 } |
| 81 | 109 |
| 82 // Remove deprecated options before we do anything else. | 110 // Remove deprecated options before we do anything else. |
| 83 removeDeprecatedOptions(); | 111 removeDeprecatedOptions(); |
| 84 | 112 |
| 85 var activeNotification = null; | 113 var activeNotification = null; |
| 86 | 114 |
| 87 // Adds or removes browser action icon according to options. | 115 // Adds or removes browser action icon according to options. |
| 88 function refreshIconAndContextMenu(page) | 116 function refreshIconAndContextMenu(page) |
| 89 { | 117 { |
| 90 if(!/^https?:/.test(page.url)) | 118 var whitelisted = isWhitelisted(page.url); |
| 91 return; | |
| 92 | 119 |
| 93 var iconFilename; | 120 var iconFilename; |
| 94 if (require("info").platform == "safari") | 121 if (whitelisted && require("info").platform != "safari") |
| 95 // There is no grayscale version of the icon for whitelisted pages | 122 // There is no grayscale version of the icon for whitelisted pages |
| 96 // when using Safari, because icons are grayscale already and icons | 123 // when using Safari, because icons are grayscale already and icons |
| 97 // aren't per page in Safari. | 124 // aren't per page in Safari. |
| 98 iconFilename = "icons/abp-16.png" | 125 iconFilename = "icons/abp-$size-whitelisted.png"; |
| 99 else | 126 else |
| 100 { | 127 iconFilename = "icons/abp-$size.png"; |
| 101 var excluded = isWhitelisted(page.url); | |
| 102 iconFilename = excluded ? "icons/abp-19-whitelisted.png" : "icons/abp-19.png "; | |
| 103 } | |
| 104 | 128 |
| 105 page.browserAction.setIcon(iconFilename); | 129 page.browserAction.setIcon(iconFilename); |
| 106 iconAnimation.registerPage(page, iconFilename); | 130 iconAnimation.registerPage(page, iconFilename); |
| 107 | 131 |
| 108 // Set context menu status according to whether the page has a whitelisted dom ain | 132 // show or hide the context menu entry dependent on whether |
| 109 if (excluded) | 133 // adblocking is active on that page |
| 134 if (whitelisted || !/^https?:/.test(page.url)) | |
| 110 ext.contextMenus.hideMenuItems(); | 135 ext.contextMenus.hideMenuItems(); |
| 111 else | 136 else |
| 112 ext.contextMenus.showMenuItems(); | 137 ext.contextMenus.showMenuItems(); |
| 113 } | 138 } |
| 114 | 139 |
| 115 /** | 140 /** |
| 116 * Old versions for Opera stored patterns.ini in the localStorage object, this | 141 * Old versions for Opera stored patterns.ini in the localStorage object, this |
| 117 * will import it into FilterStorage properly. | 142 * will import it into FilterStorage properly. |
| 118 * @return {Boolean} true if data import is in progress | 143 * @return {Boolean} true if data import is in progress |
| 119 */ | 144 */ |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 182 { | 207 { |
| 183 subscription.title = "Allow non-intrusive advertising"; | 208 subscription.title = "Allow non-intrusive advertising"; |
| 184 FilterStorage.addSubscription(subscription); | 209 FilterStorage.addSubscription(subscription); |
| 185 if (subscription instanceof DownloadableSubscription && !subscription.last Download) | 210 if (subscription instanceof DownloadableSubscription && !subscription.last Download) |
| 186 Synchronizer.execute(subscription); | 211 Synchronizer.execute(subscription); |
| 187 } | 212 } |
| 188 else | 213 else |
| 189 addAcceptable = false; | 214 addAcceptable = false; |
| 190 } | 215 } |
| 191 | 216 |
| 217 // Add "anti-adblock messages" subscription | |
| 218 var subscription = Subscription.fromURL(Prefs.subscriptions_antiadblockurl); | |
| 219 if (subscription) | |
| 220 { | |
| 221 subscription.disabled = true; | |
| 222 FilterStorage.addSubscription(subscription); | |
| 223 if (subscription instanceof DownloadableSubscription && !subscription.lastDo wnload) | |
| 224 Synchronizer.execute(subscription); | |
| 225 } | |
| 226 | |
| 192 if (!addSubscription && !addAcceptable) | 227 if (!addSubscription && !addAcceptable) |
| 193 return; | 228 return; |
| 194 | 229 |
| 195 function notifyUser() | 230 function notifyUser() |
| 196 { | 231 { |
| 197 ext.pages.open(ext.getURL("firstRun.html")); | 232 ext.pages.open(ext.getURL("firstRun.html")); |
| 198 } | 233 } |
| 199 | 234 |
| 200 if (addSubscription) | 235 if (addSubscription) |
| 201 { | 236 { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 246 }); | 281 }); |
| 247 setContextMenu(); | 282 setContextMenu(); |
| 248 | 283 |
| 249 /** | 284 /** |
| 250 * Opens options page or focuses an existing one, within the last focused windo w. | 285 * Opens options page or focuses an existing one, within the last focused windo w. |
| 251 * @param {Function} callback function to be called with the | 286 * @param {Function} callback function to be called with the |
| 252 Page object of the options page | 287 Page object of the options page |
| 253 */ | 288 */ |
| 254 function openOptions(callback) | 289 function openOptions(callback) |
| 255 { | 290 { |
| 256 ext.pages.query({lastFocusedWindow: true}, function(pages) | 291 ext.pages.query({lastFocusedWindow: true}, function(pages) |
|
Wladimir Palant
2014/04/04 14:00:35
The compat info for Chrome needs to be changed - l
Sebastian Noack
2014/04/07 13:15:25
Done.
| |
| 257 { | 292 { |
| 258 var optionsUrl = ext.getURL("options.html"); | 293 var optionsUrl = ext.getURL("options.html"); |
| 259 | 294 |
| 260 for (var i = 0; i < pages.length; i++) | 295 for (var i = 0; i < pages.length; i++) |
| 261 { | 296 { |
| 262 var page = pages[i]; | 297 var page = pages[i]; |
| 263 if (page.url == optionsUrl) | 298 if (page.url == optionsUrl) |
| 264 { | 299 { |
| 265 page.activate(); | 300 page.activate(); |
| 266 if (callback) | 301 if (callback) |
| 267 callback(page); | 302 callback(page); |
| 268 return; | 303 return; |
| 269 } | 304 } |
| 270 } | 305 } |
| 271 | 306 |
| 272 ext.pages.open(optionsUrl, callback); | 307 ext.pages.open(optionsUrl, callback); |
|
Wladimir Palant
2014/04/04 14:00:35
Won't this break abp: links? The add-subscription
Sebastian Noack
2014/04/07 13:15:25
It was wishful thinking, that the callback passed
| |
| 273 }); | 308 }); |
| 274 } | 309 } |
| 275 | 310 |
| 276 function prepareNotificationIconAndPopup() | 311 function prepareNotificationIconAndPopup() |
| 277 { | 312 { |
| 313 var animateIcon = (activeNotification.type !== "question"); | |
| 278 activeNotification.onClicked = function() | 314 activeNotification.onClicked = function() |
| 279 { | 315 { |
| 280 iconAnimation.stop(); | 316 if (animateIcon) |
| 281 activeNotification = null; | 317 iconAnimation.stop(); |
| 318 notificationClosed(); | |
| 282 }; | 319 }; |
| 283 | 320 if (animateIcon) |
| 284 iconAnimation.update(activeNotification.severity); | 321 iconAnimation.update(activeNotification.type); |
| 322 } | |
| 323 | |
| 324 function openNotificationLinks() | |
| 325 { | |
| 326 if (activeNotification.links) | |
| 327 { | |
| 328 activeNotification.links.forEach(function(link) | |
| 329 { | |
| 330 ext.windows.getLastFocused(function(win) | |
| 331 { | |
| 332 win.openTab(Utils.getDocLink(link)); | |
| 333 }); | |
| 334 }); | |
| 335 } | |
| 336 } | |
| 337 | |
| 338 function notificationButtonClick(buttonIndex) | |
| 339 { | |
| 340 if (activeNotification.type === "question") | |
| 341 { | |
| 342 Notification.triggerQuestionListeners(activeNotification.id, buttonIndex === 0); | |
| 343 Notification.markAsShown(activeNotification.id); | |
| 344 activeNotification.onClicked(); | |
| 345 } | |
| 346 else if (activeNotification.links && activeNotification.links[buttonIndex]) | |
| 347 { | |
| 348 ext.windows.getLastFocused(function(win) | |
| 349 { | |
| 350 win.openTab(Utils.getDocLink(activeNotification.links[buttonIndex])); | |
| 351 }); | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 function notificationClosed() | |
| 356 { | |
| 357 activeNotification = null; | |
| 358 } | |
| 359 | |
| 360 function imgToBase64(url, callback) | |
| 361 { | |
| 362 var canvas = document.createElement("canvas"), | |
| 363 ctx = canvas.getContext("2d"), | |
| 364 img = new Image; | |
| 365 img.src = url; | |
| 366 img.onload = function() | |
| 367 { | |
| 368 canvas.height = img.height; | |
| 369 canvas.width = img.width; | |
| 370 ctx.drawImage(img, 0, 0); | |
| 371 callback(canvas.toDataURL("image/png")); | |
| 372 canvas = null; | |
| 373 }; | |
| 374 } | |
| 375 | |
| 376 function initChromeNotifications() | |
| 377 { | |
| 378 // Chrome hides notifications in notification center when clicked so we need t o clear them | |
| 379 function clearActiveNotification(notificationId) | |
| 380 { | |
| 381 if (activeNotification && activeNotification.type != "question" && !("links" in activeNotification)) | |
| 382 return; | |
| 383 | |
| 384 chrome.notifications.clear(notificationId, function(wasCleared) | |
| 385 { | |
| 386 if (wasCleared) | |
| 387 notificationClosed(); | |
| 388 }); | |
| 389 } | |
| 390 | |
| 391 chrome.notifications.onButtonClicked.addListener(function(notificationId, butt onIndex) | |
| 392 { | |
| 393 notificationButtonClick(buttonIndex); | |
| 394 clearActiveNotification(notificationId); | |
| 395 }); | |
| 396 chrome.notifications.onClicked.addListener(clearActiveNotification); | |
| 397 chrome.notifications.onClosed.addListener(notificationClosed); | |
| 285 } | 398 } |
| 286 | 399 |
| 287 function showNotification(notification) | 400 function showNotification(notification) |
| 288 { | 401 { |
| 402 if (activeNotification && activeNotification.id === notification.id) | |
| 403 return; | |
| 404 | |
| 289 activeNotification = notification; | 405 activeNotification = notification; |
| 290 | 406 if (activeNotification.type === "critical" || activeNotification.type === "que stion") |
| 291 if (activeNotification.severity === "critical" | 407 { |
| 292 && typeof webkitNotifications !== "undefined") | 408 var hasWebkitNotifications = typeof webkitNotifications !== "undefined"; |
| 293 { | 409 if (hasWebkitNotifications && "createHTMLNotification" in webkitNotification s) |
| 294 var notification = webkitNotifications.createHTMLNotification("notification. html"); | 410 { |
| 295 notification.show(); | 411 var notification = webkitNotifications.createHTMLNotification("notificatio n.html"); |
| 296 notification.addEventListener("close", prepareNotificationIconAndPopup); | 412 notification.show(); |
| 297 } | 413 prepareNotificationIconAndPopup(); |
| 298 else | 414 return; |
| 299 prepareNotificationIconAndPopup(); | 415 } |
| 416 | |
| 417 var texts = Notification.getLocalizedTexts(notification); | |
| 418 var title = texts.title || ""; | |
| 419 var message = texts.message ? texts.message.replace(/<\/?(a|strong)>/g, "") : ""; | |
| 420 var iconUrl = ext.getURL("icons/abp-128.png"); | |
| 421 var hasLinks = activeNotification.links && activeNotification.links.length > 0; | |
| 422 | |
| 423 if (canUseChromeNotifications) | |
| 424 { | |
| 425 var opts = { | |
| 426 type: "basic", | |
| 427 title: title, | |
| 428 message: message, | |
| 429 buttons: [], | |
| 430 priority: 2 // We use the highest priority to prevent the notification f rom closing automatically | |
| 431 }; | |
| 432 if (activeNotification.type === "question") | |
| 433 { | |
| 434 opts.buttons.push({title: ext.i18n.getMessage("overlay_notification_butt on_yes")}); | |
| 435 opts.buttons.push({title: ext.i18n.getMessage("overlay_notification_butt on_no")}); | |
| 436 } | |
| 437 else | |
| 438 { | |
| 439 var regex = /<a>(.*?)<\/a>/g; | |
| 440 var plainMessage = texts.message || ""; | |
| 441 var match; | |
| 442 while (match = regex.exec(plainMessage)) | |
| 443 opts.buttons.push({title: match[1]}); | |
| 444 } | |
| 445 | |
| 446 imgToBase64(iconUrl, function(iconData) | |
| 447 { | |
| 448 opts["iconUrl"] = iconData; | |
| 449 chrome.notifications.create("", opts, function() {}); | |
| 450 }); | |
| 451 } | |
| 452 else if (hasWebkitNotifications && "createNotification" in webkitNotificatio ns && activeNotification.type !== "question") | |
| 453 { | |
| 454 if (hasLinks) | |
| 455 message += " " + ext.i18n.getMessage("notification_without_buttons"); | |
| 456 | |
| 457 imgToBase64(iconUrl, function(iconData) | |
| 458 { | |
| 459 var notification = webkitNotifications.createNotification(iconData, titl e, message); | |
| 460 notification.show(); | |
| 461 notification.addEventListener("click", openNotificationLinks, false); | |
| 462 notification.addEventListener("close", notificationClosed, false); | |
| 463 }); | |
| 464 } | |
| 465 else | |
| 466 { | |
| 467 var message = title + "\n" + message; | |
| 468 if (hasLinks) | |
| 469 message += "\n\n" + ext.i18n.getMessage("notification_with_buttons"); | |
| 470 | |
| 471 var approved = confirm(message); | |
| 472 if (activeNotification.type === "question") | |
| 473 notificationButtonClick(approved ? 0 : 1); | |
| 474 else if (approved) | |
| 475 openNotificationLinks(); | |
| 476 } | |
| 477 } | |
| 478 prepareNotificationIconAndPopup(); | |
| 300 } | 479 } |
| 301 | 480 |
| 302 ext.onMessage.addListener(function (msg, sender, sendResponse) | 481 ext.onMessage.addListener(function (msg, sender, sendResponse) |
| 303 { | 482 { |
| 304 switch (msg.type) | 483 switch (msg.type) |
| 305 { | 484 { |
| 306 case "get-selectors": | 485 case "get-selectors": |
| 307 var selectors = null; | 486 var selectors = null; |
| 308 | 487 |
| 309 if (!isFrameWhitelisted(sender.page, sender.frame, "DOCUMENT") && | 488 if (!isFrameWhitelisted(sender.page, sender.frame, "DOCUMENT") && |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 386 // sendResponse asynchronously | 565 // sendResponse asynchronously |
| 387 return true; | 566 return true; |
| 388 } | 567 } |
| 389 break; | 568 break; |
| 390 default: | 569 default: |
| 391 sendResponse({}); | 570 sendResponse({}); |
| 392 break; | 571 break; |
| 393 } | 572 } |
| 394 }); | 573 }); |
| 395 | 574 |
| 396 // Show icon as browser action for all pages that already exist | 575 // update icon when page changes location |
| 397 ext.pages.query({}, function(pages) | |
| 398 { | |
| 399 pages.forEach(refreshIconAndContextMenu); | |
| 400 }); | |
| 401 | |
| 402 // Update icon for new loaded pages | |
| 403 ext.pages.onLoading.addListener(function(page) | 576 ext.pages.onLoading.addListener(function(page) |
| 404 { | 577 { |
| 405 page.sendMessage({type: "clickhide-deactivate"}); | 578 page.sendMessage({type: "clickhide-deactivate"}); |
| 406 refreshIconAndContextMenu(page); | 579 refreshIconAndContextMenu(page); |
| 407 }); | 580 }); |
| 408 | 581 |
| 409 setTimeout(function() | 582 setTimeout(function() |
| 410 { | 583 { |
| 411 var notificationToShow = Notification.getNextToShow(); | 584 var notificationToShow = Notification.getNextToShow(); |
| 412 if (notificationToShow) | 585 if (notificationToShow) |
| 413 showNotification(notificationToShow); | 586 showNotification(notificationToShow); |
| 414 }, 3 * 60 * 1000); | 587 }, 3 * 60 * 1000); |
| LEFT | RIGHT |