1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/test/general/browser_popupNotification.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1215 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +function test() { 1.9 + waitForExplicitFinish(); 1.10 + 1.11 + ok(PopupNotifications, "PopupNotifications object exists"); 1.12 + ok(PopupNotifications.panel, "PopupNotifications panel exists"); 1.13 + 1.14 + // Disable transitions as they slow the test down and we want to click the 1.15 + // mouse buttons in a predictable location. 1.16 + PopupNotifications.transitionsEnabled = false; 1.17 + 1.18 + registerCleanupFunction(cleanUp); 1.19 + 1.20 + runNextTest(); 1.21 +} 1.22 + 1.23 +function cleanUp() { 1.24 + for (var topic in gActiveObservers) 1.25 + Services.obs.removeObserver(gActiveObservers[topic], topic); 1.26 + for (var eventName in gActiveListeners) 1.27 + PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); 1.28 + PopupNotifications.buttonDelay = PREF_SECURITY_DELAY_INITIAL; 1.29 + PopupNotifications.transitionsEnabled = true; 1.30 +} 1.31 + 1.32 +const PREF_SECURITY_DELAY_INITIAL = Services.prefs.getIntPref("security.notification_enable_delay"); 1.33 + 1.34 +var gActiveListeners = {}; 1.35 +var gActiveObservers = {}; 1.36 +var gShownState = {}; 1.37 + 1.38 +function goNext() { 1.39 + if (++gTestIndex == tests.length) 1.40 + executeSoon(finish); 1.41 + else 1.42 + executeSoon(runNextTest); 1.43 +} 1.44 + 1.45 +function runNextTest() { 1.46 + let nextTest = tests[gTestIndex]; 1.47 + 1.48 + function addObserver(topic) { 1.49 + function observer() { 1.50 + Services.obs.removeObserver(observer, "PopupNotifications-" + topic); 1.51 + delete gActiveObservers["PopupNotifications-" + topic]; 1.52 + 1.53 + info("[Test #" + gTestIndex + "] observer for " + topic + " called"); 1.54 + nextTest[topic](); 1.55 + goNext(); 1.56 + } 1.57 + Services.obs.addObserver(observer, "PopupNotifications-" + topic, false); 1.58 + gActiveObservers["PopupNotifications-" + topic] = observer; 1.59 + } 1.60 + 1.61 + if (nextTest.backgroundShow) { 1.62 + addObserver("backgroundShow"); 1.63 + } else if (nextTest.updateNotShowing) { 1.64 + addObserver("updateNotShowing"); 1.65 + } else if (nextTest.onShown) { 1.66 + doOnPopupEvent("popupshowing", function () { 1.67 + info("[Test #" + gTestIndex + "] popup showing"); 1.68 + }); 1.69 + doOnPopupEvent("popupshown", function () { 1.70 + gShownState[gTestIndex] = true; 1.71 + info("[Test #" + gTestIndex + "] popup shown"); 1.72 + nextTest.onShown(this); 1.73 + }); 1.74 + 1.75 + // We allow multiple onHidden functions to be defined in an array. They're 1.76 + // called in the order they appear. 1.77 + let onHiddenArray = nextTest.onHidden instanceof Array ? 1.78 + nextTest.onHidden : 1.79 + [nextTest.onHidden]; 1.80 + doOnPopupEvent("popuphidden", function () { 1.81 + if (!gShownState[gTestIndex]) { 1.82 + // This is expected to happen for test 9, so let's not treat it as a failure. 1.83 + info("Popup from test " + gTestIndex + " was hidden before its popupshown fired"); 1.84 + } 1.85 + 1.86 + let onHidden = onHiddenArray.shift(); 1.87 + info("[Test #" + gTestIndex + "] popup hidden (" + onHiddenArray.length + " hides remaining)"); 1.88 + executeSoon(function () { 1.89 + onHidden.call(nextTest, this); 1.90 + if (!onHiddenArray.length) 1.91 + goNext(); 1.92 + }.bind(this)); 1.93 + }, onHiddenArray.length); 1.94 + info("[Test #" + gTestIndex + "] added listeners; panel state: " + PopupNotifications.isPanelOpen); 1.95 + } 1.96 + 1.97 + info("[Test #" + gTestIndex + "] running test"); 1.98 + nextTest.run(); 1.99 +} 1.100 + 1.101 +function doOnPopupEvent(eventName, callback, numExpected) { 1.102 + gActiveListeners[eventName] = function (event) { 1.103 + if (event.target != PopupNotifications.panel) 1.104 + return; 1.105 + if (typeof(numExpected) === "number") 1.106 + numExpected--; 1.107 + if (!numExpected) { 1.108 + PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); 1.109 + delete gActiveListeners[eventName]; 1.110 + } 1.111 + 1.112 + callback.call(PopupNotifications.panel); 1.113 + } 1.114 + PopupNotifications.panel.addEventListener(eventName, gActiveListeners[eventName], false); 1.115 +} 1.116 + 1.117 +var gTestIndex = 0; 1.118 +var gNewTab; 1.119 + 1.120 +function basicNotification() { 1.121 + var self = this; 1.122 + this.browser = gBrowser.selectedBrowser; 1.123 + this.id = "test-notification-" + gTestIndex; 1.124 + this.message = "This is popup notification " + this.id + " from test " + gTestIndex; 1.125 + this.anchorID = null; 1.126 + this.mainAction = { 1.127 + label: "Main Action", 1.128 + accessKey: "M", 1.129 + callback: function () { 1.130 + self.mainActionClicked = true; 1.131 + } 1.132 + }; 1.133 + this.secondaryActions = [ 1.134 + { 1.135 + label: "Secondary Action", 1.136 + accessKey: "S", 1.137 + callback: function () { 1.138 + self.secondaryActionClicked = true; 1.139 + } 1.140 + } 1.141 + ]; 1.142 + this.options = { 1.143 + eventCallback: function (eventName) { 1.144 + switch (eventName) { 1.145 + case "dismissed": 1.146 + self.dismissalCallbackTriggered = true; 1.147 + break; 1.148 + case "showing": 1.149 + self.showingCallbackTriggered = true; 1.150 + break; 1.151 + case "shown": 1.152 + self.shownCallbackTriggered = true; 1.153 + break; 1.154 + case "removed": 1.155 + self.removedCallbackTriggered = true; 1.156 + break; 1.157 + case "swapping": 1.158 + self.swappingCallbackTriggered = true; 1.159 + break; 1.160 + } 1.161 + } 1.162 + }; 1.163 +} 1.164 + 1.165 +basicNotification.prototype.addOptions = function(options) { 1.166 + for (let [name, value] in Iterator(options)) 1.167 + this.options[name] = value; 1.168 +}; 1.169 + 1.170 +function errorNotification() { 1.171 + var self = this; 1.172 + this.mainAction.callback = function () { 1.173 + self.mainActionClicked = true; 1.174 + throw new Error("Oops!"); 1.175 + }; 1.176 + this.secondaryActions[0].callback = function () { 1.177 + self.secondaryActionClicked = true; 1.178 + throw new Error("Oops!"); 1.179 + }; 1.180 +} 1.181 + 1.182 +errorNotification.prototype = new basicNotification(); 1.183 +errorNotification.prototype.constructor = errorNotification; 1.184 + 1.185 +var wrongBrowserNotificationObject = new basicNotification(); 1.186 +var wrongBrowserNotification; 1.187 + 1.188 +var tests = [ 1.189 + { // Test #0 1.190 + run: function () { 1.191 + this.notifyObj = new basicNotification(); 1.192 + showNotification(this.notifyObj); 1.193 + }, 1.194 + onShown: function (popup) { 1.195 + checkPopup(popup, this.notifyObj); 1.196 + triggerMainCommand(popup); 1.197 + }, 1.198 + onHidden: function (popup) { 1.199 + ok(this.notifyObj.mainActionClicked, "mainAction was clicked"); 1.200 + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered"); 1.201 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.202 + } 1.203 + }, 1.204 + { // Test #1 1.205 + run: function () { 1.206 + this.notifyObj = new basicNotification(); 1.207 + showNotification(this.notifyObj); 1.208 + }, 1.209 + onShown: function (popup) { 1.210 + checkPopup(popup, this.notifyObj); 1.211 + triggerSecondaryCommand(popup, 0); 1.212 + }, 1.213 + onHidden: function (popup) { 1.214 + ok(this.notifyObj.secondaryActionClicked, "secondaryAction was clicked"); 1.215 + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered"); 1.216 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.217 + } 1.218 + }, 1.219 + { // Test #2 1.220 + run: function () { 1.221 + this.notifyObj = new basicNotification(); 1.222 + this.notification = showNotification(this.notifyObj); 1.223 + }, 1.224 + onShown: function (popup) { 1.225 + checkPopup(popup, this.notifyObj); 1.226 + dismissNotification(popup); 1.227 + }, 1.228 + onHidden: function (popup) { 1.229 + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); 1.230 + this.notification.remove(); 1.231 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.232 + } 1.233 + }, 1.234 + // test opening a notification for a background browser 1.235 + { // Test #3 1.236 + run: function () { 1.237 + gNewTab = gBrowser.addTab("about:blank"); 1.238 + isnot(gBrowser.selectedTab, gNewTab, "new tab isn't selected"); 1.239 + wrongBrowserNotificationObject.browser = gBrowser.getBrowserForTab(gNewTab); 1.240 + wrongBrowserNotification = showNotification(wrongBrowserNotificationObject); 1.241 + }, 1.242 + backgroundShow: function () { 1.243 + is(PopupNotifications.isPanelOpen, false, "panel isn't open"); 1.244 + ok(!wrongBrowserNotificationObject.mainActionClicked, "main action wasn't clicked"); 1.245 + ok(!wrongBrowserNotificationObject.secondaryActionClicked, "secondary action wasn't clicked"); 1.246 + ok(!wrongBrowserNotificationObject.dismissalCallbackTriggered, "dismissal callback wasn't called"); 1.247 + } 1.248 + }, 1.249 + // now select that browser and test to see that the notification appeared 1.250 + { // Test #4 1.251 + run: function () { 1.252 + this.oldSelectedTab = gBrowser.selectedTab; 1.253 + gBrowser.selectedTab = gNewTab; 1.254 + }, 1.255 + onShown: function (popup) { 1.256 + checkPopup(popup, wrongBrowserNotificationObject); 1.257 + is(PopupNotifications.isPanelOpen, true, "isPanelOpen getter doesn't lie"); 1.258 + 1.259 + // switch back to the old browser 1.260 + gBrowser.selectedTab = this.oldSelectedTab; 1.261 + }, 1.262 + onHidden: function (popup) { 1.263 + // actually remove the notification to prevent it from reappearing 1.264 + ok(wrongBrowserNotificationObject.dismissalCallbackTriggered, "dismissal callback triggered due to tab switch"); 1.265 + wrongBrowserNotification.remove(); 1.266 + ok(wrongBrowserNotificationObject.removedCallbackTriggered, "removed callback triggered"); 1.267 + wrongBrowserNotification = null; 1.268 + } 1.269 + }, 1.270 + // test that the removed notification isn't shown on browser re-select 1.271 + { // Test #5 1.272 + run: function () { 1.273 + gBrowser.selectedTab = gNewTab; 1.274 + }, 1.275 + updateNotShowing: function () { 1.276 + is(PopupNotifications.isPanelOpen, false, "panel isn't open"); 1.277 + gBrowser.removeTab(gNewTab); 1.278 + } 1.279 + }, 1.280 + // Test that two notifications with the same ID result in a single displayed 1.281 + // notification. 1.282 + { // Test #6 1.283 + run: function () { 1.284 + this.notifyObj = new basicNotification(); 1.285 + // Show the same notification twice 1.286 + this.notification1 = showNotification(this.notifyObj); 1.287 + this.notification2 = showNotification(this.notifyObj); 1.288 + }, 1.289 + onShown: function (popup) { 1.290 + checkPopup(popup, this.notifyObj); 1.291 + this.notification2.remove(); 1.292 + }, 1.293 + onHidden: function (popup) { 1.294 + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered"); 1.295 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.296 + } 1.297 + }, 1.298 + // Test that two notifications with different IDs are displayed 1.299 + { // Test #7 1.300 + run: function () { 1.301 + this.testNotif1 = new basicNotification(); 1.302 + this.testNotif1.message += " 1"; 1.303 + showNotification(this.testNotif1); 1.304 + this.testNotif2 = new basicNotification(); 1.305 + this.testNotif2.message += " 2"; 1.306 + this.testNotif2.id += "-2"; 1.307 + showNotification(this.testNotif2); 1.308 + }, 1.309 + onShown: function (popup) { 1.310 + is(popup.childNodes.length, 2, "two notifications are shown"); 1.311 + // Trigger the main command for the first notification, and the secondary 1.312 + // for the second. Need to do mainCommand first since the secondaryCommand 1.313 + // triggering is async. 1.314 + triggerMainCommand(popup); 1.315 + is(popup.childNodes.length, 1, "only one notification left"); 1.316 + triggerSecondaryCommand(popup, 0); 1.317 + }, 1.318 + onHidden: function (popup) { 1.319 + ok(this.testNotif1.mainActionClicked, "main action #1 was clicked"); 1.320 + ok(!this.testNotif1.secondaryActionClicked, "secondary action #1 wasn't clicked"); 1.321 + ok(!this.testNotif1.dismissalCallbackTriggered, "dismissal callback #1 wasn't called"); 1.322 + 1.323 + ok(!this.testNotif2.mainActionClicked, "main action #2 wasn't clicked"); 1.324 + ok(this.testNotif2.secondaryActionClicked, "secondary action #2 was clicked"); 1.325 + ok(!this.testNotif2.dismissalCallbackTriggered, "dismissal callback #2 wasn't called"); 1.326 + } 1.327 + }, 1.328 + // Test notification without mainAction 1.329 + { // Test #8 1.330 + run: function () { 1.331 + this.notifyObj = new basicNotification(); 1.332 + this.notifyObj.mainAction = null; 1.333 + this.notification = showNotification(this.notifyObj); 1.334 + }, 1.335 + onShown: function (popup) { 1.336 + checkPopup(popup, this.notifyObj); 1.337 + dismissNotification(popup); 1.338 + }, 1.339 + onHidden: function (popup) { 1.340 + this.notification.remove(); 1.341 + } 1.342 + }, 1.343 + // Test two notifications with different anchors 1.344 + { // Test #9 1.345 + run: function () { 1.346 + this.notifyObj = new basicNotification(); 1.347 + this.firstNotification = showNotification(this.notifyObj); 1.348 + this.notifyObj2 = new basicNotification(); 1.349 + this.notifyObj2.id += "-2"; 1.350 + this.notifyObj2.anchorID = "addons-notification-icon"; 1.351 + // Second showNotification() overrides the first 1.352 + this.secondNotification = showNotification(this.notifyObj2); 1.353 + }, 1.354 + onShown: function (popup) { 1.355 + // This also checks that only one element is shown. 1.356 + checkPopup(popup, this.notifyObj2); 1.357 + is(document.getElementById("geo-notification-icon").boxObject.width, 0, 1.358 + "geo anchor shouldn't be visible"); 1.359 + dismissNotification(popup); 1.360 + }, 1.361 + onHidden: [ 1.362 + // The second showing triggers a popuphidden event that we should ignore. 1.363 + function (popup) {}, 1.364 + function (popup) { 1.365 + // Remove the notifications 1.366 + this.firstNotification.remove(); 1.367 + this.secondNotification.remove(); 1.368 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.369 + ok(this.notifyObj2.removedCallbackTriggered, "removed callback triggered"); 1.370 + } 1.371 + ] 1.372 + }, 1.373 + // Test optional params 1.374 + { // Test #10 1.375 + run: function () { 1.376 + this.notifyObj = new basicNotification(); 1.377 + this.notifyObj.secondaryActions = undefined; 1.378 + this.notification = showNotification(this.notifyObj); 1.379 + }, 1.380 + onShown: function (popup) { 1.381 + checkPopup(popup, this.notifyObj); 1.382 + dismissNotification(popup); 1.383 + }, 1.384 + onHidden: function (popup) { 1.385 + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); 1.386 + this.notification.remove(); 1.387 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.388 + } 1.389 + }, 1.390 + // Test that icons appear 1.391 + { // Test #11 1.392 + run: function () { 1.393 + this.notifyObj = new basicNotification(); 1.394 + this.notifyObj.id = "geolocation"; 1.395 + this.notifyObj.anchorID = "geo-notification-icon"; 1.396 + this.notification = showNotification(this.notifyObj); 1.397 + }, 1.398 + onShown: function (popup) { 1.399 + checkPopup(popup, this.notifyObj); 1.400 + isnot(document.getElementById("geo-notification-icon").boxObject.width, 0, 1.401 + "geo anchor should be visible"); 1.402 + dismissNotification(popup); 1.403 + }, 1.404 + onHidden: function (popup) { 1.405 + let icon = document.getElementById("geo-notification-icon"); 1.406 + isnot(icon.boxObject.width, 0, 1.407 + "geo anchor should be visible after dismissal"); 1.408 + this.notification.remove(); 1.409 + is(icon.boxObject.width, 0, 1.410 + "geo anchor should not be visible after removal"); 1.411 + } 1.412 + }, 1.413 + // Test that persistence allows the notification to persist across reloads 1.414 + { // Test #12 1.415 + run: function () { 1.416 + this.oldSelectedTab = gBrowser.selectedTab; 1.417 + gBrowser.selectedTab = gBrowser.addTab("about:blank"); 1.418 + 1.419 + let self = this; 1.420 + loadURI("http://example.com/", function() { 1.421 + self.notifyObj = new basicNotification(); 1.422 + self.notifyObj.addOptions({ 1.423 + persistence: 2 1.424 + }); 1.425 + self.notification = showNotification(self.notifyObj); 1.426 + }); 1.427 + }, 1.428 + onShown: function (popup) { 1.429 + this.complete = false; 1.430 + 1.431 + let self = this; 1.432 + loadURI("http://example.org/", function() { 1.433 + loadURI("http://example.com/", function() { 1.434 + 1.435 + // Next load will remove the notification 1.436 + self.complete = true; 1.437 + 1.438 + loadURI("http://example.org/"); 1.439 + }); 1.440 + }); 1.441 + }, 1.442 + onHidden: function (popup) { 1.443 + ok(this.complete, "Should only have hidden the notification after 3 page loads"); 1.444 + ok(this.notifyObj.removedCallbackTriggered, "removal callback triggered"); 1.445 + gBrowser.removeTab(gBrowser.selectedTab); 1.446 + gBrowser.selectedTab = this.oldSelectedTab; 1.447 + } 1.448 + }, 1.449 + // Test that a timeout allows the notification to persist across reloads 1.450 + { // Test #13 1.451 + run: function () { 1.452 + this.oldSelectedTab = gBrowser.selectedTab; 1.453 + gBrowser.selectedTab = gBrowser.addTab("about:blank"); 1.454 + 1.455 + let self = this; 1.456 + loadURI("http://example.com/", function() { 1.457 + self.notifyObj = new basicNotification(); 1.458 + // Set a timeout of 10 minutes that should never be hit 1.459 + self.notifyObj.addOptions({ 1.460 + timeout: Date.now() + 600000 1.461 + }); 1.462 + self.notification = showNotification(self.notifyObj); 1.463 + }); 1.464 + }, 1.465 + onShown: function (popup) { 1.466 + this.complete = false; 1.467 + 1.468 + let self = this; 1.469 + loadURI("http://example.org/", function() { 1.470 + loadURI("http://example.com/", function() { 1.471 + 1.472 + // Next load will hide the notification 1.473 + self.notification.options.timeout = Date.now() - 1; 1.474 + self.complete = true; 1.475 + 1.476 + loadURI("http://example.org/"); 1.477 + }); 1.478 + }); 1.479 + }, 1.480 + onHidden: function (popup) { 1.481 + ok(this.complete, "Should only have hidden the notification after the timeout was passed"); 1.482 + this.notification.remove(); 1.483 + gBrowser.removeTab(gBrowser.selectedTab); 1.484 + gBrowser.selectedTab = this.oldSelectedTab; 1.485 + } 1.486 + }, 1.487 + // Test that setting persistWhileVisible allows a visible notification to 1.488 + // persist across location changes 1.489 + { // Test #14 1.490 + run: function () { 1.491 + this.oldSelectedTab = gBrowser.selectedTab; 1.492 + gBrowser.selectedTab = gBrowser.addTab("about:blank"); 1.493 + 1.494 + let self = this; 1.495 + loadURI("http://example.com/", function() { 1.496 + self.notifyObj = new basicNotification(); 1.497 + self.notifyObj.addOptions({ 1.498 + persistWhileVisible: true 1.499 + }); 1.500 + self.notification = showNotification(self.notifyObj); 1.501 + }); 1.502 + }, 1.503 + onShown: function (popup) { 1.504 + this.complete = false; 1.505 + 1.506 + let self = this; 1.507 + loadURI("http://example.org/", function() { 1.508 + loadURI("http://example.com/", function() { 1.509 + 1.510 + // Notification should persist across location changes 1.511 + self.complete = true; 1.512 + dismissNotification(popup); 1.513 + }); 1.514 + }); 1.515 + }, 1.516 + onHidden: function (popup) { 1.517 + ok(this.complete, "Should only have hidden the notification after it was dismissed"); 1.518 + this.notification.remove(); 1.519 + gBrowser.removeTab(gBrowser.selectedTab); 1.520 + gBrowser.selectedTab = this.oldSelectedTab; 1.521 + } 1.522 + }, 1.523 + // Test that nested icon nodes correctly activate popups 1.524 + { // Test #15 1.525 + run: function() { 1.526 + // Add a temporary box as the anchor with a button 1.527 + this.box = document.createElement("box"); 1.528 + PopupNotifications.iconBox.appendChild(this.box); 1.529 + 1.530 + let button = document.createElement("button"); 1.531 + button.setAttribute("label", "Please click me!"); 1.532 + this.box.appendChild(button); 1.533 + 1.534 + // The notification should open up on the box 1.535 + this.notifyObj = new basicNotification(); 1.536 + this.notifyObj.anchorID = this.box.id = "nested-box"; 1.537 + this.notifyObj.addOptions({dismissed: true}); 1.538 + this.notification = showNotification(this.notifyObj); 1.539 + 1.540 + // This test places a normal button in the notification area, which has 1.541 + // standard GTK styling and dimensions. Due to the clip-path, this button 1.542 + // gets clipped off, which makes it necessary to synthesize the mouse click 1.543 + // a little bit downward. To be safe, I adjusted the x-offset with the same 1.544 + // amount. 1.545 + EventUtils.synthesizeMouse(button, 4, 4, {}); 1.546 + }, 1.547 + onShown: function(popup) { 1.548 + checkPopup(popup, this.notifyObj); 1.549 + dismissNotification(popup); 1.550 + }, 1.551 + onHidden: function(popup) { 1.552 + this.notification.remove(); 1.553 + this.box.parentNode.removeChild(this.box); 1.554 + } 1.555 + }, 1.556 + // Test that popupnotifications without popups have anchor icons shown 1.557 + { // Test #16 1.558 + run: function() { 1.559 + let notifyObj = new basicNotification(); 1.560 + notifyObj.anchorID = "geo-notification-icon"; 1.561 + notifyObj.addOptions({neverShow: true}); 1.562 + showNotification(notifyObj); 1.563 + }, 1.564 + updateNotShowing: function() { 1.565 + isnot(document.getElementById("geo-notification-icon").boxObject.width, 0, 1.566 + "geo anchor should be visible"); 1.567 + } 1.568 + }, 1.569 + // Test notification "Not Now" menu item 1.570 + { // Test #17 1.571 + run: function () { 1.572 + this.notifyObj = new basicNotification(); 1.573 + this.notification = showNotification(this.notifyObj); 1.574 + }, 1.575 + onShown: function (popup) { 1.576 + checkPopup(popup, this.notifyObj); 1.577 + triggerSecondaryCommand(popup, 1); 1.578 + }, 1.579 + onHidden: function (popup) { 1.580 + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); 1.581 + this.notification.remove(); 1.582 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.583 + } 1.584 + }, 1.585 + // Test notification close button 1.586 + { // Test #18 1.587 + run: function () { 1.588 + this.notifyObj = new basicNotification(); 1.589 + this.notification = showNotification(this.notifyObj); 1.590 + }, 1.591 + onShown: function (popup) { 1.592 + checkPopup(popup, this.notifyObj); 1.593 + let notification = popup.childNodes[0]; 1.594 + EventUtils.synthesizeMouseAtCenter(notification.closebutton, {}); 1.595 + }, 1.596 + onHidden: function (popup) { 1.597 + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); 1.598 + this.notification.remove(); 1.599 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.600 + } 1.601 + }, 1.602 + // Test notification when chrome is hidden 1.603 + { // Test #19 1.604 + run: function () { 1.605 + window.locationbar.visible = false; 1.606 + this.notifyObj = new basicNotification(); 1.607 + this.notification = showNotification(this.notifyObj); 1.608 + window.locationbar.visible = true; 1.609 + }, 1.610 + onShown: function (popup) { 1.611 + checkPopup(popup, this.notifyObj); 1.612 + is(popup.anchorNode.className, "tabbrowser-tab", "notification anchored to tab"); 1.613 + dismissNotification(popup); 1.614 + }, 1.615 + onHidden: function (popup) { 1.616 + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered"); 1.617 + this.notification.remove(); 1.618 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.619 + } 1.620 + }, 1.621 + // Test notification is removed when dismissed if removeOnDismissal is true 1.622 + { // Test #20 1.623 + run: function () { 1.624 + this.notifyObj = new basicNotification(); 1.625 + this.notifyObj.addOptions({ 1.626 + removeOnDismissal: true 1.627 + }); 1.628 + this.notification = showNotification(this.notifyObj); 1.629 + }, 1.630 + onShown: function (popup) { 1.631 + checkPopup(popup, this.notifyObj); 1.632 + dismissNotification(popup); 1.633 + }, 1.634 + onHidden: function (popup) { 1.635 + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered"); 1.636 + ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 1.637 + } 1.638 + }, 1.639 + // Test multiple notification icons are shown 1.640 + { // Test #21 1.641 + run: function () { 1.642 + this.notifyObj1 = new basicNotification(); 1.643 + this.notifyObj1.id += "_1"; 1.644 + this.notifyObj1.anchorID = "default-notification-icon"; 1.645 + this.notification1 = showNotification(this.notifyObj1); 1.646 + 1.647 + this.notifyObj2 = new basicNotification(); 1.648 + this.notifyObj2.id += "_2"; 1.649 + this.notifyObj2.anchorID = "geo-notification-icon"; 1.650 + this.notification2 = showNotification(this.notifyObj2); 1.651 + }, 1.652 + onShown: function (popup) { 1.653 + checkPopup(popup, this.notifyObj2); 1.654 + 1.655 + // check notifyObj1 anchor icon is showing 1.656 + isnot(document.getElementById("default-notification-icon").boxObject.width, 0, 1.657 + "default anchor should be visible"); 1.658 + // check notifyObj2 anchor icon is showing 1.659 + isnot(document.getElementById("geo-notification-icon").boxObject.width, 0, 1.660 + "geo anchor should be visible"); 1.661 + 1.662 + dismissNotification(popup); 1.663 + }, 1.664 + onHidden: [ 1.665 + function (popup) { 1.666 + }, 1.667 + function (popup) { 1.668 + this.notification1.remove(); 1.669 + ok(this.notifyObj1.removedCallbackTriggered, "removed callback triggered"); 1.670 + 1.671 + this.notification2.remove(); 1.672 + ok(this.notifyObj2.removedCallbackTriggered, "removed callback triggered"); 1.673 + } 1.674 + ] 1.675 + }, 1.676 + // Test that multiple notification icons are removed when switching tabs 1.677 + { // Test #22 1.678 + run: function () { 1.679 + // show the notification on old tab. 1.680 + this.notifyObjOld = new basicNotification(); 1.681 + this.notifyObjOld.anchorID = "default-notification-icon"; 1.682 + this.notificationOld = showNotification(this.notifyObjOld); 1.683 + 1.684 + // switch tab 1.685 + this.oldSelectedTab = gBrowser.selectedTab; 1.686 + gBrowser.selectedTab = gBrowser.addTab("about:blank"); 1.687 + 1.688 + // show the notification on new tab. 1.689 + this.notifyObjNew = new basicNotification(); 1.690 + this.notifyObjNew.anchorID = "geo-notification-icon"; 1.691 + this.notificationNew = showNotification(this.notifyObjNew); 1.692 + }, 1.693 + onShown: function (popup) { 1.694 + checkPopup(popup, this.notifyObjNew); 1.695 + 1.696 + // check notifyObjOld anchor icon is removed 1.697 + is(document.getElementById("default-notification-icon").boxObject.width, 0, 1.698 + "default anchor shouldn't be visible"); 1.699 + // check notifyObjNew anchor icon is showing 1.700 + isnot(document.getElementById("geo-notification-icon").boxObject.width, 0, 1.701 + "geo anchor should be visible"); 1.702 + 1.703 + dismissNotification(popup); 1.704 + }, 1.705 + onHidden: [ 1.706 + function (popup) { 1.707 + }, 1.708 + function (popup) { 1.709 + this.notificationNew.remove(); 1.710 + gBrowser.removeTab(gBrowser.selectedTab); 1.711 + 1.712 + gBrowser.selectedTab = this.oldSelectedTab; 1.713 + this.notificationOld.remove(); 1.714 + } 1.715 + ] 1.716 + }, 1.717 + { // Test #23 - test security delay - too early 1.718 + run: function () { 1.719 + // Set the security delay to 100s 1.720 + PopupNotifications.buttonDelay = 100000; 1.721 + 1.722 + this.notifyObj = new basicNotification(); 1.723 + showNotification(this.notifyObj); 1.724 + }, 1.725 + onShown: function (popup) { 1.726 + checkPopup(popup, this.notifyObj); 1.727 + triggerMainCommand(popup); 1.728 + 1.729 + // Wait to see if the main command worked 1.730 + executeSoon(function delayedDismissal() { 1.731 + dismissNotification(popup); 1.732 + }); 1.733 + 1.734 + }, 1.735 + onHidden: function (popup) { 1.736 + ok(!this.notifyObj.mainActionClicked, "mainAction was not clicked because it was too soon"); 1.737 + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback was triggered"); 1.738 + } 1.739 + }, 1.740 + { // Test #24 - test security delay - after delay 1.741 + run: function () { 1.742 + // Set the security delay to 10ms 1.743 + PopupNotifications.buttonDelay = 10; 1.744 + 1.745 + this.notifyObj = new basicNotification(); 1.746 + showNotification(this.notifyObj); 1.747 + }, 1.748 + onShown: function (popup) { 1.749 + checkPopup(popup, this.notifyObj); 1.750 + 1.751 + // Wait until after the delay to trigger the main action 1.752 + setTimeout(function delayedDismissal() { 1.753 + triggerMainCommand(popup); 1.754 + }, 500); 1.755 + 1.756 + }, 1.757 + onHidden: function (popup) { 1.758 + ok(this.notifyObj.mainActionClicked, "mainAction was clicked after the delay"); 1.759 + ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback was not triggered"); 1.760 + PopupNotifications.buttonDelay = PREF_SECURITY_DELAY_INITIAL; 1.761 + } 1.762 + }, 1.763 + { // Test #25 - reload removes notification 1.764 + run: function () { 1.765 + loadURI("http://example.com/", function() { 1.766 + let notifyObj = new basicNotification(); 1.767 + notifyObj.options.eventCallback = function (eventName) { 1.768 + if (eventName == "removed") { 1.769 + ok(true, "Notification removed in background tab after reloading"); 1.770 + executeSoon(function () { 1.771 + goNext(); 1.772 + }); 1.773 + } 1.774 + }; 1.775 + showNotification(notifyObj); 1.776 + executeSoon(function () { 1.777 + gBrowser.selectedBrowser.reload(); 1.778 + }); 1.779 + }); 1.780 + } 1.781 + }, 1.782 + { // Test #26 - location change in background tab removes notification 1.783 + run: function () { 1.784 + let oldSelectedTab = gBrowser.selectedTab; 1.785 + let newTab = gBrowser.addTab("about:blank"); 1.786 + gBrowser.selectedTab = newTab; 1.787 + 1.788 + loadURI("http://example.com/", function() { 1.789 + gBrowser.selectedTab = oldSelectedTab; 1.790 + let browser = gBrowser.getBrowserForTab(newTab); 1.791 + 1.792 + let notifyObj = new basicNotification(); 1.793 + notifyObj.browser = browser; 1.794 + notifyObj.options.eventCallback = function (eventName) { 1.795 + if (eventName == "removed") { 1.796 + ok(true, "Notification removed in background tab after reloading"); 1.797 + executeSoon(function () { 1.798 + gBrowser.removeTab(newTab); 1.799 + goNext(); 1.800 + }); 1.801 + } 1.802 + }; 1.803 + showNotification(notifyObj); 1.804 + executeSoon(function () { 1.805 + browser.reload(); 1.806 + }); 1.807 + }); 1.808 + } 1.809 + }, 1.810 + { // Test #27 - Popup notification anchor shouldn't disappear when a notification with the same ID is re-added in a background tab 1.811 + run: function () { 1.812 + loadURI("http://example.com/", function () { 1.813 + let originalTab = gBrowser.selectedTab; 1.814 + let bgTab = gBrowser.addTab("about:blank"); 1.815 + gBrowser.selectedTab = bgTab; 1.816 + loadURI("http://example.com/", function () { 1.817 + let anchor = document.createElement("box"); 1.818 + anchor.id = "test26-anchor"; 1.819 + anchor.className = "notification-anchor-icon"; 1.820 + PopupNotifications.iconBox.appendChild(anchor); 1.821 + 1.822 + gBrowser.selectedTab = originalTab; 1.823 + 1.824 + let fgNotifyObj = new basicNotification(); 1.825 + fgNotifyObj.anchorID = anchor.id; 1.826 + fgNotifyObj.options.dismissed = true; 1.827 + let fgNotification = showNotification(fgNotifyObj); 1.828 + 1.829 + let bgNotifyObj = new basicNotification(); 1.830 + bgNotifyObj.anchorID = anchor.id; 1.831 + bgNotifyObj.browser = gBrowser.getBrowserForTab(bgTab); 1.832 + // show the notification in the background tab ... 1.833 + let bgNotification = showNotification(bgNotifyObj); 1.834 + // ... and re-show it 1.835 + bgNotification = showNotification(bgNotifyObj); 1.836 + 1.837 + ok(fgNotification.id, "notification has id"); 1.838 + is(fgNotification.id, bgNotification.id, "notification ids are the same"); 1.839 + is(anchor.getAttribute("showing"), "true", "anchor still showing"); 1.840 + 1.841 + fgNotification.remove(); 1.842 + gBrowser.removeTab(bgTab); 1.843 + goNext(); 1.844 + }); 1.845 + }); 1.846 + } 1.847 + }, 1.848 + { // Test #28 - location change in an embedded frame should not remove a notification 1.849 + run: function () { 1.850 + loadURI("data:text/html;charset=utf8,<iframe id='iframe' src='http://example.com/'>", function () { 1.851 + this.notifyObj = new basicNotification(); 1.852 + this.notifyObj.options.eventCallback = function (eventName) { 1.853 + if (eventName == "removed") { 1.854 + ok(false, "Test 28: Notification removed from browser when subframe navigated"); 1.855 + } 1.856 + }; 1.857 + showNotification(this.notifyObj); 1.858 + }.bind(this)); 1.859 + }, 1.860 + onShown: function (popup) { 1.861 + let self = this; 1.862 + let progressListener = { 1.863 + onLocationChange: function onLocationChange(aBrowser) { 1.864 + if (aBrowser != gBrowser.selectedBrowser) { 1.865 + return; 1.866 + } 1.867 + let notification = PopupNotifications.getNotification(self.notifyObj.id, 1.868 + self.notifyObj.browser); 1.869 + ok(notification != null, "Test 28: Notification remained when subframe navigated"); 1.870 + self.notifyObj.options.eventCallback = undefined; 1.871 + 1.872 + notification.remove(); 1.873 + gBrowser.removeTabsProgressListener(progressListener); 1.874 + }, 1.875 + }; 1.876 + 1.877 + info("Test 28: Adding progress listener and performing navigation"); 1.878 + gBrowser.addTabsProgressListener(progressListener); 1.879 + content.document.getElementById("iframe") 1.880 + .setAttribute("src", "http://example.org/"); 1.881 + }, 1.882 + onHidden: function () {} 1.883 + }, 1.884 + { // Test #29 - Popup Notifications should catch exceptions from callbacks 1.885 + run: function () { 1.886 + let callbackCount = 0; 1.887 + this.testNotif1 = new basicNotification(); 1.888 + this.testNotif1.message += " 1"; 1.889 + this.notification1 = showNotification(this.testNotif1); 1.890 + this.testNotif1.options.eventCallback = function (eventName) { 1.891 + info("notifyObj1.options.eventCallback: " + eventName); 1.892 + if (eventName == "dismissed") { 1.893 + throw new Error("Oops 1!"); 1.894 + if (++callbackCount == 2) { 1.895 + executeSoon(goNext); 1.896 + } 1.897 + } 1.898 + }; 1.899 + 1.900 + this.testNotif2 = new basicNotification(); 1.901 + this.testNotif2.message += " 2"; 1.902 + this.testNotif2.id += "-2"; 1.903 + this.testNotif2.options.eventCallback = function (eventName) { 1.904 + info("notifyObj2.options.eventCallback: " + eventName); 1.905 + if (eventName == "dismissed") { 1.906 + throw new Error("Oops 2!"); 1.907 + if (++callbackCount == 2) { 1.908 + executeSoon(goNext); 1.909 + } 1.910 + } 1.911 + }; 1.912 + this.notification2 = showNotification(this.testNotif2); 1.913 + }, 1.914 + onShown: function (popup) { 1.915 + is(popup.childNodes.length, 2, "two notifications are shown"); 1.916 + dismissNotification(popup); 1.917 + }, 1.918 + onHidden: function () { 1.919 + this.notification1.remove(); 1.920 + this.notification2.remove(); 1.921 + } 1.922 + }, 1.923 + { // Test #30 - Popup Notifications main actions should catch exceptions from callbacks 1.924 + run: function () { 1.925 + this.testNotif = new errorNotification(); 1.926 + showNotification(this.testNotif); 1.927 + }, 1.928 + onShown: function (popup) { 1.929 + checkPopup(popup, this.testNotif); 1.930 + triggerMainCommand(popup); 1.931 + }, 1.932 + onHidden: function (popup) { 1.933 + ok(this.testNotif.mainActionClicked, "main action has been triggered"); 1.934 + } 1.935 + }, 1.936 + { // Test #31 - Popup Notifications secondary actions should catch exceptions from callbacks 1.937 + run: function () { 1.938 + this.testNotif = new errorNotification(); 1.939 + showNotification(this.testNotif); 1.940 + }, 1.941 + onShown: function (popup) { 1.942 + checkPopup(popup, this.testNotif); 1.943 + triggerSecondaryCommand(popup, 0); 1.944 + }, 1.945 + onHidden: function (popup) { 1.946 + ok(this.testNotif.secondaryActionClicked, "secondary action has been triggered"); 1.947 + } 1.948 + }, 1.949 + { // Test #32 - Existing popup notification shouldn't disappear when adding a dismissed notification 1.950 + run: function () { 1.951 + this.notifyObj1 = new basicNotification(); 1.952 + this.notifyObj1.id += "_1"; 1.953 + this.notifyObj1.anchorID = "default-notification-icon"; 1.954 + this.notification1 = showNotification(this.notifyObj1); 1.955 + }, 1.956 + onShown: function (popup) { 1.957 + // Now show a dismissed notification, and check that it doesn't clobber 1.958 + // the showing one. 1.959 + this.notifyObj2 = new basicNotification(); 1.960 + this.notifyObj2.id += "_2"; 1.961 + this.notifyObj2.anchorID = "geo-notification-icon"; 1.962 + this.notifyObj2.options.dismissed = true; 1.963 + this.notification2 = showNotification(this.notifyObj2); 1.964 + 1.965 + checkPopup(popup, this.notifyObj1); 1.966 + 1.967 + // check that both anchor icons are showing 1.968 + is(document.getElementById("default-notification-icon").getAttribute("showing"), "true", 1.969 + "notification1 anchor should be visible"); 1.970 + is(document.getElementById("geo-notification-icon").getAttribute("showing"), "true", 1.971 + "notification2 anchor should be visible"); 1.972 + 1.973 + dismissNotification(popup); 1.974 + }, 1.975 + onHidden: function(popup) { 1.976 + this.notification1.remove(); 1.977 + this.notification2.remove(); 1.978 + } 1.979 + }, 1.980 + { // Test #33 - Showing should be able to modify the popup data 1.981 + run: function() { 1.982 + this.notifyObj = new basicNotification(); 1.983 + var normalCallback = this.notifyObj.options.eventCallback; 1.984 + this.notifyObj.options.eventCallback = function (eventName) { 1.985 + if (eventName == "showing") { 1.986 + this.mainAction.label = "Alternate Label"; 1.987 + } 1.988 + normalCallback.call(this, eventName); 1.989 + }; 1.990 + showNotification(this.notifyObj); 1.991 + }, 1.992 + onShown: function(popup) { 1.993 + // checkPopup checks for the matching label. Note that this assumes that 1.994 + // this.notifyObj.mainAction is the same as notification.mainAction, 1.995 + // which could be a problem if we ever decided to deep-copy. 1.996 + checkPopup(popup, this.notifyObj); 1.997 + triggerMainCommand(popup); 1.998 + }, 1.999 + onHidden: function() { } 1.1000 + }, 1.1001 + { // Test #34 - Moving a tab to a new window should remove non-swappable 1.1002 + // notifications. 1.1003 + run: function() { 1.1004 + gBrowser.selectedTab = gBrowser.addTab("about:blank"); 1.1005 + let notifyObj = new basicNotification(); 1.1006 + showNotification(notifyObj); 1.1007 + let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab); 1.1008 + whenDelayedStartupFinished(win, function() { 1.1009 + let [tab] = win.gBrowser.tabs; 1.1010 + let anchor = win.document.getElementById("default-notification-icon"); 1.1011 + win.PopupNotifications._reshowNotifications(anchor); 1.1012 + ok(win.PopupNotifications.panel.childNodes.length == 0, 1.1013 + "no notification displayed in new window"); 1.1014 + ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered"); 1.1015 + ok(notifyObj.removedCallbackTriggered, "the removed callback was triggered"); 1.1016 + win.close(); 1.1017 + goNext(); 1.1018 + }); 1.1019 + } 1.1020 + }, 1.1021 + { // Test #35 - Moving a tab to a new window should preserve swappable notifications. 1.1022 + run: function() { 1.1023 + gBrowser.selectedTab = gBrowser.addTab("about:blank"); 1.1024 + let notifyObj = new basicNotification(); 1.1025 + let originalCallback = notifyObj.options.eventCallback; 1.1026 + notifyObj.options.eventCallback = function (eventName) { 1.1027 + originalCallback(eventName); 1.1028 + return eventName == "swapping"; 1.1029 + }; 1.1030 + 1.1031 + showNotification(notifyObj); 1.1032 + let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab); 1.1033 + whenDelayedStartupFinished(win, function() { 1.1034 + let [tab] = win.gBrowser.tabs; 1.1035 + let anchor = win.document.getElementById("default-notification-icon"); 1.1036 + win.PopupNotifications._reshowNotifications(anchor); 1.1037 + checkPopup(win.PopupNotifications.panel, notifyObj); 1.1038 + ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered"); 1.1039 + win.close(); 1.1040 + goNext(); 1.1041 + }); 1.1042 + } 1.1043 + }, 1.1044 + { // Test #36 - the hideNotNow option 1.1045 + run: function () { 1.1046 + this.notifyObj = new basicNotification(); 1.1047 + this.notifyObj.options.hideNotNow = true; 1.1048 + this.notifyObj.mainAction.dismiss = true; 1.1049 + this.notification = showNotification(this.notifyObj); 1.1050 + }, 1.1051 + onShown: function (popup) { 1.1052 + // checkPopup verifies that the Not Now item is hidden, and that no separator is added. 1.1053 + checkPopup(popup, this.notifyObj); 1.1054 + triggerMainCommand(popup); 1.1055 + }, 1.1056 + onHidden: function (popup) { 1.1057 + this.notification.remove(); 1.1058 + } 1.1059 + }, 1.1060 + { // Test #37 - the main action callback can keep the notification. 1.1061 + run: function () { 1.1062 + this.notifyObj = new basicNotification(); 1.1063 + this.notifyObj.mainAction.dismiss = true; 1.1064 + this.notification = showNotification(this.notifyObj); 1.1065 + }, 1.1066 + onShown: function (popup) { 1.1067 + checkPopup(popup, this.notifyObj); 1.1068 + triggerMainCommand(popup); 1.1069 + }, 1.1070 + onHidden: function (popup) { 1.1071 + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback was triggered"); 1.1072 + ok(!this.notifyObj.removedCallbackTriggered, "removed callback wasn't triggered"); 1.1073 + this.notification.remove(); 1.1074 + } 1.1075 + }, 1.1076 + { // Test #38 - a secondary action callback can keep the notification. 1.1077 + run: function () { 1.1078 + this.notifyObj = new basicNotification(); 1.1079 + this.notifyObj.secondaryActions[0].dismiss = true; 1.1080 + this.notification = showNotification(this.notifyObj); 1.1081 + }, 1.1082 + onShown: function (popup) { 1.1083 + checkPopup(popup, this.notifyObj); 1.1084 + triggerSecondaryCommand(popup, 0); 1.1085 + }, 1.1086 + onHidden: function (popup) { 1.1087 + ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback was triggered"); 1.1088 + ok(!this.notifyObj.removedCallbackTriggered, "removed callback wasn't triggered"); 1.1089 + this.notification.remove(); 1.1090 + } 1.1091 + }, 1.1092 + { // Test #39 - returning true in the showing callback should dismiss the notification. 1.1093 + run: function() { 1.1094 + let notifyObj = new basicNotification(); 1.1095 + let originalCallback = notifyObj.options.eventCallback; 1.1096 + notifyObj.options.eventCallback = function (eventName) { 1.1097 + originalCallback(eventName); 1.1098 + return eventName == "showing"; 1.1099 + }; 1.1100 + 1.1101 + let notification = showNotification(notifyObj); 1.1102 + ok(notifyObj.showingCallbackTriggered, "the showing callback was triggered"); 1.1103 + ok(!notifyObj.shownCallbackTriggered, "the shown callback wasn't triggered"); 1.1104 + notification.remove(); 1.1105 + goNext(); 1.1106 + } 1.1107 + } 1.1108 +]; 1.1109 + 1.1110 +function showNotification(notifyObj) { 1.1111 + return PopupNotifications.show(notifyObj.browser, 1.1112 + notifyObj.id, 1.1113 + notifyObj.message, 1.1114 + notifyObj.anchorID, 1.1115 + notifyObj.mainAction, 1.1116 + notifyObj.secondaryActions, 1.1117 + notifyObj.options); 1.1118 +} 1.1119 + 1.1120 +function checkPopup(popup, notificationObj) { 1.1121 + info("[Test #" + gTestIndex + "] checking popup"); 1.1122 + 1.1123 + ok(notificationObj.showingCallbackTriggered, "showing callback was triggered"); 1.1124 + ok(notificationObj.shownCallbackTriggered, "shown callback was triggered"); 1.1125 + 1.1126 + let notifications = popup.childNodes; 1.1127 + is(notifications.length, 1, "one notification displayed"); 1.1128 + let notification = notifications[0]; 1.1129 + if (!notification) 1.1130 + return; 1.1131 + let icon = document.getAnonymousElementByAttribute(notification, "class", "popup-notification-icon"); 1.1132 + if (notificationObj.id == "geolocation") { 1.1133 + isnot(icon.boxObject.width, 0, "icon for geo displayed"); 1.1134 + is(popup.anchorNode.className, "notification-anchor-icon", "notification anchored to icon"); 1.1135 + } 1.1136 + is(notification.getAttribute("label"), notificationObj.message, "message matches"); 1.1137 + is(notification.id, notificationObj.id + "-notification", "id matches"); 1.1138 + if (notificationObj.mainAction) { 1.1139 + is(notification.getAttribute("buttonlabel"), notificationObj.mainAction.label, "main action label matches"); 1.1140 + is(notification.getAttribute("buttonaccesskey"), notificationObj.mainAction.accessKey, "main action accesskey matches"); 1.1141 + } 1.1142 + let actualSecondaryActions = Array.filter(notification.childNodes, 1.1143 + function (child) child.nodeName == "menuitem"); 1.1144 + let secondaryActions = notificationObj.secondaryActions || []; 1.1145 + let actualSecondaryActionsCount = actualSecondaryActions.length; 1.1146 + if (notificationObj.options.hideNotNow) { 1.1147 + is(notification.getAttribute("hidenotnow"), "true", "Not Now item hidden"); 1.1148 + if (secondaryActions.length) 1.1149 + is(notification.lastChild.tagName, "menuitem", "no menuseparator"); 1.1150 + } 1.1151 + else if (secondaryActions.length) { 1.1152 + is(notification.lastChild.tagName, "menuseparator", "menuseparator exists"); 1.1153 + } 1.1154 + is(actualSecondaryActionsCount, secondaryActions.length, actualSecondaryActions.length + " secondary actions"); 1.1155 + secondaryActions.forEach(function (a, i) { 1.1156 + is(actualSecondaryActions[i].getAttribute("label"), a.label, "label for secondary action " + i + " matches"); 1.1157 + is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey, "accessKey for secondary action " + i + " matches"); 1.1158 + }); 1.1159 +} 1.1160 + 1.1161 +function triggerMainCommand(popup) { 1.1162 + info("[Test #" + gTestIndex + "] triggering main command"); 1.1163 + let notifications = popup.childNodes; 1.1164 + ok(notifications.length > 0, "at least one notification displayed"); 1.1165 + let notification = notifications[0]; 1.1166 + 1.1167 + // 20, 10 so that the inner button is hit 1.1168 + EventUtils.synthesizeMouse(notification.button, 20, 10, {}); 1.1169 +} 1.1170 + 1.1171 +function triggerSecondaryCommand(popup, index) { 1.1172 + info("[Test #" + gTestIndex + "] triggering secondary command"); 1.1173 + let notifications = popup.childNodes; 1.1174 + ok(notifications.length > 0, "at least one notification displayed"); 1.1175 + let notification = notifications[0]; 1.1176 + 1.1177 + // Cancel the arrow panel slide-in transition (bug 767133) such that 1.1178 + // it won't interfere with us interacting with the dropdown. 1.1179 + document.getAnonymousNodes(popup)[0].style.transition = "none"; 1.1180 + 1.1181 + notification.button.focus(); 1.1182 + 1.1183 + popup.addEventListener("popupshown", function () { 1.1184 + popup.removeEventListener("popupshown", arguments.callee, false); 1.1185 + 1.1186 + // Press down until the desired command is selected 1.1187 + for (let i = 0; i <= index; i++) 1.1188 + EventUtils.synthesizeKey("VK_DOWN", {}); 1.1189 + 1.1190 + // Activate 1.1191 + EventUtils.synthesizeKey("VK_RETURN", {}); 1.1192 + }, false); 1.1193 + 1.1194 + // One down event to open the popup 1.1195 + EventUtils.synthesizeKey("VK_DOWN", { altKey: !navigator.platform.contains("Mac") }); 1.1196 +} 1.1197 + 1.1198 +function loadURI(uri, callback) { 1.1199 + if (callback) { 1.1200 + gBrowser.addEventListener("load", function() { 1.1201 + // Ignore the about:blank load 1.1202 + if (gBrowser.currentURI.spec == "about:blank") 1.1203 + return; 1.1204 + 1.1205 + gBrowser.removeEventListener("load", arguments.callee, true); 1.1206 + 1.1207 + callback(); 1.1208 + }, true); 1.1209 + } 1.1210 + gBrowser.loadURI(uri); 1.1211 +} 1.1212 + 1.1213 +function dismissNotification(popup) { 1.1214 + info("[Test #" + gTestIndex + "] dismissing notification"); 1.1215 + executeSoon(function () { 1.1216 + EventUtils.synthesizeKey("VK_ESCAPE", {}); 1.1217 + }); 1.1218 +}