browser_applications_selection.js (13399B)
1 SimpleTest.requestCompleteLog(); 2 const { HandlerServiceTestUtils } = ChromeUtils.importESModule( 3 "resource://testing-common/HandlerServiceTestUtils.sys.mjs" 4 ); 5 6 let gHandlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService( 7 Ci.nsIHandlerService 8 ); 9 10 let gOldMailHandlers = []; 11 let gDummyHandlers = []; 12 let gOriginalPreferredMailHandler; 13 let gOriginalPreferredPDFHandler; 14 15 registerCleanupFunction(function () { 16 function removeDummyHandlers(handlers) { 17 // Remove any of the dummy handlers we created. 18 for (let i = handlers.Count() - 1; i >= 0; i--) { 19 try { 20 if ( 21 gDummyHandlers.some( 22 h => 23 h.uriTemplate == 24 handlers.queryElementAt(i, Ci.nsIWebHandlerApp).uriTemplate 25 ) 26 ) { 27 handlers.removeElementAt(i); 28 } 29 } catch (ex) { 30 /* ignore non-web-app handlers */ 31 } 32 } 33 } 34 // Re-add the original protocol handlers: 35 let mailHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("mailto"); 36 let mailHandlers = mailHandlerInfo.possibleApplicationHandlers; 37 for (let h of gOldMailHandlers) { 38 mailHandlers.appendElement(h); 39 } 40 removeDummyHandlers(mailHandlers); 41 mailHandlerInfo.preferredApplicationHandler = gOriginalPreferredMailHandler; 42 gHandlerService.store(mailHandlerInfo); 43 44 let pdfHandlerInfo = 45 HandlerServiceTestUtils.getHandlerInfo("application/pdf"); 46 removeDummyHandlers(pdfHandlerInfo.possibleApplicationHandlers); 47 pdfHandlerInfo.preferredApplicationHandler = gOriginalPreferredPDFHandler; 48 gHandlerService.store(pdfHandlerInfo); 49 50 gBrowser.removeCurrentTab(); 51 }); 52 53 function scrubMailtoHandlers(handlerInfo) { 54 // Remove extant web handlers because they have icons that 55 // we fetch from the web, which isn't allowed in tests. 56 let handlers = handlerInfo.possibleApplicationHandlers; 57 for (let i = handlers.Count() - 1; i >= 0; i--) { 58 try { 59 let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp); 60 gOldMailHandlers.push(handler); 61 // If we get here, this is a web handler app. Remove it: 62 handlers.removeElementAt(i); 63 } catch (ex) {} 64 } 65 } 66 67 add_setup(async function () { 68 // Create our dummy handlers 69 let handler1 = Cc["@mozilla.org/uriloader/web-handler-app;1"].createInstance( 70 Ci.nsIWebHandlerApp 71 ); 72 handler1.name = "Handler 1"; 73 handler1.uriTemplate = "https://example.com/first/%s"; 74 75 let handler2 = Cc["@mozilla.org/uriloader/web-handler-app;1"].createInstance( 76 Ci.nsIWebHandlerApp 77 ); 78 handler2.name = "Handler 2"; 79 handler2.uriTemplate = "https://example.org/second/%s"; 80 gDummyHandlers.push(handler1, handler2); 81 82 function substituteWebHandlers(handlerInfo) { 83 // Append the dummy handlers to replace them: 84 let handlers = handlerInfo.possibleApplicationHandlers; 85 handlers.appendElement(handler1); 86 handlers.appendElement(handler2); 87 gHandlerService.store(handlerInfo); 88 } 89 // Set up our mailto handler test infrastructure. 90 let mailtoHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("mailto"); 91 scrubMailtoHandlers(mailtoHandlerInfo); 92 gOriginalPreferredMailHandler = mailtoHandlerInfo.preferredApplicationHandler; 93 substituteWebHandlers(mailtoHandlerInfo); 94 95 // Now do the same for pdf handler: 96 let pdfHandlerInfo = 97 HandlerServiceTestUtils.getHandlerInfo("application/pdf"); 98 // PDF doesn't have built-in web handlers, so no need to scrub. 99 gOriginalPreferredPDFHandler = pdfHandlerInfo.preferredApplicationHandler; 100 substituteWebHandlers(pdfHandlerInfo); 101 102 await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true }); 103 info("Preferences page opened on the general pane."); 104 105 await gBrowser.selectedBrowser.contentWindow.promiseLoadHandlersList; 106 info("Apps list loaded."); 107 }); 108 109 async function selectStandardOptions(itemToUse) { 110 async function selectItemInPopup(item) { 111 let popupShown = BrowserTestUtils.waitForEvent(popup, "popupshown"); 112 // Synthesizing the mouse on the .actionsMenu menulist somehow just selects 113 // the top row. Probably something to do with the multiple layers of anon 114 // content - workaround by using the `.open` setter instead. 115 list.open = true; 116 await popupShown; 117 let popupHidden = BrowserTestUtils.waitForEvent(popup, "popuphidden"); 118 if (typeof item == "function") { 119 item = item(); 120 } 121 popup.activateItem(item); 122 await popupHidden; 123 return item; 124 } 125 126 let itemType = itemToUse.getAttribute("type"); 127 // Center the item. Center rather than top so it doesn't get blocked by 128 // the search header. 129 itemToUse.scrollIntoView({ block: "center" }); 130 itemToUse.closest("richlistbox").selectItem(itemToUse); 131 Assert.ok(itemToUse.selected, "Should be able to select our item."); 132 // Force reflow to make sure it's visible and the container dropdown isn't 133 // hidden. 134 itemToUse.getBoundingClientRect().top; 135 let list = itemToUse.querySelector(".actionsMenu"); 136 let popup = list.menupopup; 137 138 // select one of our test cases: 139 let handlerItem = list.querySelector("menuitem[data-l10n-args*='Handler 1']"); 140 await selectItemInPopup(handlerItem); 141 let { preferredAction, alwaysAskBeforeHandling } = 142 HandlerServiceTestUtils.getHandlerInfo(itemType); 143 Assert.notEqual( 144 preferredAction, 145 Ci.nsIHandlerInfo.alwaysAsk, 146 "Should have selected something other than 'always ask' (" + itemType + ")" 147 ); 148 Assert.ok( 149 !alwaysAskBeforeHandling, 150 "Should have turned off asking before handling (" + itemType + ")" 151 ); 152 153 // Test the alwaysAsk option 154 let alwaysAskItem = list.getElementsByAttribute( 155 "action", 156 Ci.nsIHandlerInfo.alwaysAsk 157 )[0]; 158 await selectItemInPopup(alwaysAskItem); 159 Assert.equal( 160 list.selectedItem, 161 alwaysAskItem, 162 "Should have selected always ask item (" + itemType + ")" 163 ); 164 alwaysAskBeforeHandling = 165 HandlerServiceTestUtils.getHandlerInfo(itemType).alwaysAskBeforeHandling; 166 Assert.ok( 167 alwaysAskBeforeHandling, 168 "Should have turned on asking before handling (" + itemType + ")" 169 ); 170 171 let useDefaultItem = list.getElementsByAttribute( 172 "action", 173 Ci.nsIHandlerInfo.useSystemDefault 174 ); 175 useDefaultItem = useDefaultItem && useDefaultItem[0]; 176 if (useDefaultItem) { 177 await selectItemInPopup(useDefaultItem); 178 Assert.equal( 179 list.selectedItem, 180 useDefaultItem, 181 "Should have selected 'use default' item (" + itemType + ")" 182 ); 183 preferredAction = 184 HandlerServiceTestUtils.getHandlerInfo(itemType).preferredAction; 185 Assert.equal( 186 preferredAction, 187 Ci.nsIHandlerInfo.useSystemDefault, 188 "Should have selected 'use default' (" + itemType + ")" 189 ); 190 } else { 191 // Whether there's a "use default" item depends on the OS, so it's not 192 // possible to rely on it being the case or not. 193 info("No 'Use default' item, so not testing (" + itemType + ")"); 194 } 195 196 // Select a web app item. 197 let webAppItems = Array.from( 198 popup.getElementsByAttribute("action", Ci.nsIHandlerInfo.useHelperApp) 199 ); 200 webAppItems = webAppItems.filter( 201 item => item.handlerApp instanceof Ci.nsIWebHandlerApp 202 ); 203 Assert.equal( 204 webAppItems.length, 205 2, 206 "Should have 2 web application handler. (" + itemType + ")" 207 ); 208 Assert.notEqual( 209 webAppItems[0].label, 210 webAppItems[1].label, 211 "Should have 2 different web app handlers" 212 ); 213 let selectedItem = await selectItemInPopup(webAppItems[0]); 214 215 // Test that the selected item label is the same as the label 216 // of the menu item. 217 let win = gBrowser.selectedBrowser.contentWindow; 218 await win.document.l10n.translateFragment(selectedItem); 219 await win.document.l10n.translateFragment(itemToUse); 220 Assert.equal( 221 selectedItem.label, 222 itemToUse.querySelector(".actionContainer label").value, 223 "Should have selected correct item (" + itemType + ")" 224 ); 225 let { preferredApplicationHandler } = 226 HandlerServiceTestUtils.getHandlerInfo(itemType); 227 preferredApplicationHandler.QueryInterface(Ci.nsIWebHandlerApp); 228 Assert.equal( 229 selectedItem.handlerApp.uriTemplate, 230 preferredApplicationHandler.uriTemplate, 231 "App should actually be selected in the backend. (" + itemType + ")" 232 ); 233 234 // select the other web app item 235 selectedItem = await selectItemInPopup(webAppItems[1]); 236 237 // Test that the selected item label is the same as the label 238 // of the menu item 239 await win.document.l10n.translateFragment(selectedItem); 240 await win.document.l10n.translateFragment(itemToUse); 241 Assert.equal( 242 selectedItem.label, 243 itemToUse.querySelector(".actionContainer label").value, 244 "Should have selected correct item (" + itemType + ")" 245 ); 246 preferredApplicationHandler = 247 HandlerServiceTestUtils.getHandlerInfo( 248 itemType 249 ).preferredApplicationHandler; 250 preferredApplicationHandler.QueryInterface(Ci.nsIWebHandlerApp); 251 Assert.equal( 252 selectedItem.handlerApp.uriTemplate, 253 preferredApplicationHandler.uriTemplate, 254 "App should actually be selected in the backend. (" + itemType + ")" 255 ); 256 } 257 258 add_task(async function checkDropdownBehavior() { 259 let win = gBrowser.selectedBrowser.contentWindow; 260 261 let container = win.document.getElementById("handlersView"); 262 263 // First check a protocol handler item. 264 let mailItem = container.querySelector("richlistitem[type='mailto']"); 265 Assert.ok(mailItem, "mailItem is present in handlersView."); 266 await selectStandardOptions(mailItem); 267 268 // Then check a content menu item. 269 let pdfItem = container.querySelector("richlistitem[type='application/pdf']"); 270 Assert.ok(pdfItem, "pdfItem is present in handlersView."); 271 await selectStandardOptions(pdfItem); 272 }); 273 274 add_task(async function sortingCheck() { 275 let win = gBrowser.selectedBrowser.contentWindow; 276 const handlerView = win.document.getElementById("handlersView"); 277 const typeColumn = win.document.getElementById("typeColumn"); 278 Assert.ok(typeColumn, "typeColumn is present in handlersView."); 279 280 let expectedNumberOfItems = 281 handlerView.querySelectorAll("richlistitem").length; 282 283 // Test default sorting 284 assertSortByType("ascending"); 285 286 const oldDir = typeColumn.getAttribute("sortDirection"); 287 288 // click on an item and sort again: 289 let itemToUse = handlerView.querySelector("richlistitem[type=mailto]"); 290 itemToUse.scrollIntoView({ block: "center" }); 291 itemToUse.closest("richlistbox").selectItem(itemToUse); 292 293 // Test sorting on the type column 294 typeColumn.click(); 295 assertSortByType("descending"); 296 Assert.notEqual( 297 oldDir, 298 typeColumn.getAttribute("sortDirection"), 299 "Sort direction should change" 300 ); 301 302 typeColumn.click(); 303 assertSortByType("ascending"); 304 305 const actionColumn = win.document.getElementById("actionColumn"); 306 Assert.ok(actionColumn, "actionColumn is present in handlersView."); 307 308 // Test sorting on the action column 309 const oldActionDir = actionColumn.getAttribute("sortDirection"); 310 actionColumn.click(); 311 assertSortByAction("ascending"); 312 Assert.notEqual( 313 oldActionDir, 314 actionColumn.getAttribute("sortDirection"), 315 "Sort direction should change" 316 ); 317 318 actionColumn.click(); 319 assertSortByAction("descending"); 320 321 // Restore the default sort order 322 typeColumn.click(); 323 assertSortByType("ascending"); 324 325 function assertSortByAction(order) { 326 Assert.equal( 327 actionColumn.getAttribute("sortDirection"), 328 order, 329 `Sort direction should be ${order}` 330 ); 331 let siteItems = handlerView.getElementsByTagName("richlistitem"); 332 Assert.equal( 333 siteItems.length, 334 expectedNumberOfItems, 335 "Number of items should not change." 336 ); 337 for (let i = 0; i < siteItems.length - 1; ++i) { 338 let aType = ( 339 siteItems[i].getAttribute("actionDescription") || "" 340 ).toLowerCase(); 341 let bType = ( 342 siteItems[i + 1].getAttribute("actionDescription") || "" 343 ).toLowerCase(); 344 let result = 0; 345 if (aType > bType) { 346 result = 1; 347 } else if (bType > aType) { 348 result = -1; 349 } 350 if (order == "ascending") { 351 Assert.lessOrEqual( 352 result, 353 0, 354 "Should sort applications in the ascending order by action" 355 ); 356 } else { 357 Assert.greaterOrEqual( 358 result, 359 0, 360 "Should sort applications in the descending order by action" 361 ); 362 } 363 } 364 } 365 366 function assertSortByType(order) { 367 Assert.equal( 368 typeColumn.getAttribute("sortDirection"), 369 order, 370 `Sort direction should be ${order}` 371 ); 372 373 let siteItems = handlerView.getElementsByTagName("richlistitem"); 374 Assert.equal( 375 siteItems.length, 376 expectedNumberOfItems, 377 "Number of items should not change." 378 ); 379 for (let i = 0; i < siteItems.length - 1; ++i) { 380 let aType = ( 381 siteItems[i].getAttribute("typeDescription") || "" 382 ).toLowerCase(); 383 let bType = ( 384 siteItems[i + 1].getAttribute("typeDescription") || "" 385 ).toLowerCase(); 386 let result = 0; 387 if (aType > bType) { 388 result = 1; 389 } else if (bType > aType) { 390 result = -1; 391 } 392 if (order == "ascending") { 393 Assert.lessOrEqual( 394 result, 395 0, 396 "Should sort applications in the ascending order by type" 397 ); 398 } else { 399 Assert.greaterOrEqual( 400 result, 401 0, 402 "Should sort applications in the descending order by type" 403 ); 404 } 405 } 406 } 407 });