browser_popupNotification_keyboard.js (9323B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 function test() { 6 waitForExplicitFinish(); 7 8 ok(PopupNotifications, "PopupNotifications object exists"); 9 ok(PopupNotifications.panel, "PopupNotifications panel exists"); 10 11 // Force tabfocus for all elements on OSX. 12 SpecialPowers.pushPrefEnv({ 13 set: [ 14 ["accessibility.tabfocus", 7], 15 ["browser.urlbar.scotchBonnet.enableOverride", true], 16 ], 17 }).then(setup); 18 } 19 20 // Focusing on notification icon buttons is handled by the ToolbarKeyboardNavigator 21 // component and arrow keys (see browser/base/content/browser-toolbarKeyNav.js). 22 async function focusNotificationAnchor(anchor) { 23 // To happen focus event on urlbar, remove the focus once. 24 // We intentionally turn off this a11y check, because the following click is 25 // purposefully targeting a non-interactive element. 26 AccessibilityUtils.setEnv({ mustHaveAccessibleRule: false }); 27 EventUtils.synthesizeMouseAtCenter(document.getElementById("browser"), {}); 28 AccessibilityUtils.resetEnv(); 29 await BrowserTestUtils.waitForCondition(() => 30 document.activeElement.closest("#browser") 31 ); 32 33 // Move focus to left side button of urlbar. 34 EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {}); 35 EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); 36 37 // Move focus to the target. 38 while (document.activeElement !== anchor) { 39 EventUtils.synthesizeKey("ArrowRight"); 40 } 41 } 42 43 var tests = [ 44 // Test that for persistent notifications, 45 // the secondary action is triggered by pressing the escape key. 46 { 47 id: "Test#1", 48 run() { 49 this.notifyObj = new BasicNotification(this.id); 50 this.notifyObj.options.persistent = true; 51 showNotification(this.notifyObj); 52 }, 53 onShown(popup) { 54 checkPopup(popup, this.notifyObj); 55 EventUtils.synthesizeKey("KEY_Escape"); 56 }, 57 onHidden() { 58 ok(!this.notifyObj.mainActionClicked, "mainAction was not clicked"); 59 ok(this.notifyObj.secondaryActionClicked, "secondaryAction was clicked"); 60 ok( 61 !this.notifyObj.dismissalCallbackTriggered, 62 "dismissal callback wasn't triggered" 63 ); 64 ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); 65 is( 66 this.notifyObj.mainActionSource, 67 undefined, 68 "shouldn't have a main action source." 69 ); 70 is( 71 this.notifyObj.secondaryActionSource, 72 "esc-press", 73 "secondary action should be from ESC key press" 74 ); 75 }, 76 }, 77 // Test that for non-persistent notifications, the escape key dismisses the notification. 78 { 79 id: "Test#2", 80 run() { 81 this.notifyObj = new BasicNotification(this.id); 82 this.notification = showNotification(this.notifyObj); 83 }, 84 onShown(popup) { 85 checkPopup(popup, this.notifyObj); 86 EventUtils.synthesizeKey("KEY_Escape"); 87 }, 88 onHidden() { 89 ok(!this.notifyObj.mainActionClicked, "mainAction was not clicked"); 90 ok( 91 !this.notifyObj.secondaryActionClicked, 92 "secondaryAction was not clicked" 93 ); 94 ok( 95 this.notifyObj.dismissalCallbackTriggered, 96 "dismissal callback triggered" 97 ); 98 ok( 99 !this.notifyObj.removedCallbackTriggered, 100 "removed callback was not triggered" 101 ); 102 is( 103 this.notifyObj.mainActionSource, 104 undefined, 105 "shouldn't have a main action source." 106 ); 107 is( 108 this.notifyObj.secondaryActionSource, 109 undefined, 110 "shouldn't have a secondary action source." 111 ); 112 this.notification.remove(); 113 }, 114 }, 115 // Test that the space key on an anchor element focuses an active notification 116 { 117 id: "Test#3", 118 run() { 119 this.notifyObj = new BasicNotification(this.id); 120 this.notifyObj.anchorID = "geo-notification-icon"; 121 this.notifyObj.addOptions({ 122 persistent: true, 123 }); 124 this.notification = showNotification(this.notifyObj); 125 }, 126 async onShown(popup) { 127 checkPopup(popup, this.notifyObj); 128 let anchor = document.getElementById(this.notifyObj.anchorID); 129 await focusNotificationAnchor(anchor); 130 EventUtils.sendString(" "); 131 is(document.activeElement, popup.children[0].closebutton); 132 this.notification.remove(); 133 }, 134 onHidden() {}, 135 }, 136 // Test that you can switch between active notifications with the space key 137 // and that the notification is focused on selection. 138 { 139 id: "Test#4", 140 async run() { 141 let notifyObj1 = new BasicNotification(this.id); 142 notifyObj1.id += "_1"; 143 notifyObj1.anchorID = "default-notification-icon"; 144 notifyObj1.addOptions({ 145 hideClose: true, 146 checkbox: { 147 label: "Test that elements inside the panel can be focused", 148 }, 149 persistent: true, 150 }); 151 let opened = waitForNotificationPanel(); 152 let notification1 = showNotification(notifyObj1); 153 await opened; 154 155 let notifyObj2 = new BasicNotification(this.id); 156 notifyObj2.id += "_2"; 157 notifyObj2.anchorID = "geo-notification-icon"; 158 notifyObj2.addOptions({ 159 persistent: true, 160 }); 161 opened = waitForNotificationPanel(); 162 let notification2 = showNotification(notifyObj2); 163 let popup = await opened; 164 165 // Make sure notification 2 is visible 166 checkPopup(popup, notifyObj2); 167 168 // Activate the anchor for notification 1 and wait until it's shown. 169 let anchor = document.getElementById(notifyObj1.anchorID); 170 await focusNotificationAnchor(anchor); 171 is(document.activeElement, anchor); 172 opened = waitForNotificationPanel(); 173 EventUtils.sendString(" "); 174 popup = await opened; 175 checkPopup(popup, notifyObj1); 176 177 is(document.activeElement, popup.children[0].checkbox); 178 179 // Activate the anchor for notification 2 and wait until it's shown. 180 anchor = document.getElementById(notifyObj2.anchorID); 181 await focusNotificationAnchor(anchor); 182 is(document.activeElement, anchor); 183 opened = waitForNotificationPanel(); 184 EventUtils.sendString(" "); 185 popup = await opened; 186 checkPopup(popup, notifyObj2); 187 188 is(document.activeElement, popup.children[0].closebutton); 189 190 notification1.remove(); 191 notification2.remove(); 192 goNext(); 193 }, 194 }, 195 // Test that passing the autofocus option will focus an opened notification. 196 { 197 id: "Test#5", 198 run() { 199 this.notifyObj = new BasicNotification(this.id); 200 this.notifyObj.anchorID = "geo-notification-icon"; 201 this.notifyObj.addOptions({ 202 autofocus: true, 203 }); 204 this.notification = showNotification(this.notifyObj); 205 }, 206 onShown(popup) { 207 checkPopup(popup, this.notifyObj); 208 209 // Initial focus on open is null because a panel itself 210 // can not be focused, next tab focus will be inside the panel. 211 is(Services.focus.focusedElement, null); 212 213 EventUtils.synthesizeKey("KEY_Tab"); 214 is(Services.focus.focusedElement, popup.children[0].closebutton); 215 dismissNotification(popup); 216 }, 217 async onHidden() { 218 // Focus the urlbar to check that it stays focused. 219 gURLBar.focus(); 220 221 // Show another notification and make sure it's not autofocused. 222 let notifyObj = new BasicNotification(this.id); 223 notifyObj.id += "_2"; 224 notifyObj.anchorID = "default-notification-icon"; 225 226 let opened = waitForNotificationPanel(); 227 let notification = showNotification(notifyObj); 228 let popup = await opened; 229 checkPopup(popup, notifyObj); 230 231 // Check that the urlbar is still focused. 232 is(Services.focus.focusedElement, gURLBar.inputField); 233 234 this.notification.remove(); 235 notification.remove(); 236 }, 237 }, 238 // Test that focus is not moved out of a content element if autofocus is not set. 239 { 240 id: "Test#6", 241 async run() { 242 let id = this.id; 243 await BrowserTestUtils.withNewTab( 244 "data:text/html,<input id='test-input'/>", 245 async function (browser) { 246 let notifyObj = new BasicNotification(id); 247 await SpecialPowers.spawn(browser, [], function () { 248 content.document.getElementById("test-input").focus(); 249 }); 250 251 let opened = waitForNotificationPanel(); 252 let notification = showNotification(notifyObj); 253 await opened; 254 255 // Check that the focused element in the chrome window 256 // is either the browser in case we're running on e10s 257 // or the input field in case of non-e10s. 258 if (gMultiProcessBrowser) { 259 is(Services.focus.focusedElement, browser); 260 } else { 261 is( 262 Services.focus.focusedElement, 263 browser.contentDocument.getElementById("test-input") 264 ); 265 } 266 267 // Check that the input field is still focused inside the browser. 268 await SpecialPowers.spawn(browser, [], function () { 269 is( 270 content.document.activeElement, 271 content.document.getElementById("test-input") 272 ); 273 }); 274 275 notification.remove(); 276 } 277 ); 278 goNext(); 279 }, 280 }, 281 ];