browser_https_only_exceptions.js (11244B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 /** 5 * First Test 6 * Checks if buttons are disabled/enabled and visible/hidden correctly. 7 */ 8 add_task(async function testButtons() { 9 // Let's make sure HTTPS-Only and HTTPS-First Mode is off. 10 await setHttpsOnlyPref("off"); 11 await setHttpsFirstPref("off"); 12 13 // Open the privacy-pane in about:preferences 14 await openPreferencesViaOpenPreferencesAPI("panePrivacy", { 15 leaveOpen: true, 16 }); 17 18 // Get button-element to open the exceptions-dialog 19 const exceptionButton = gBrowser.contentDocument.getElementById( 20 "httpsOnlyExceptionButton" 21 ); 22 23 is( 24 exceptionButton.disabled, 25 true, 26 "HTTPS-Only exception button should be disabled when HTTPS-Only Mode is disabled." 27 ); 28 29 await setHttpsOnlyPref("private"); 30 is( 31 exceptionButton.disabled, 32 false, 33 "HTTPS-Only exception button should be enabled when HTTPS-Only Mode is only enabled in private browsing." 34 ); 35 36 await setHttpsOnlyPref("everywhere"); 37 is( 38 exceptionButton.disabled, 39 false, 40 "HTTPS-Only exception button should be enabled when HTTPS-Only Mode enabled everywhere." 41 ); 42 43 await setHttpsOnlyPref("off"); 44 is( 45 exceptionButton.disabled, 46 true, 47 "Turning off HTTPS-Only should disable the exception button again." 48 ); 49 50 await setHttpsFirstPref("private"); 51 is( 52 exceptionButton.disabled, 53 false, 54 "HTTPS-Only exception button should be enabled when HTTPS-Only Mode is disabled and HTTPS-First Mode is only enabled in private browsing." 55 ); 56 57 await setHttpsFirstPref("everywhere"); 58 is( 59 exceptionButton.disabled, 60 false, 61 "HTTPS-Only exception button should be enabled when HTTPS-Only Mode is disabled and HTTPS-First Mode enabled everywhere." 62 ); 63 64 // Now that the button is clickable, we open the dialog 65 // to check if the correct buttons are visible 66 let promiseSubDialogLoaded = promiseLoadSubDialog( 67 "chrome://browser/content/preferences/dialogs/permissions.xhtml" 68 ); 69 exceptionButton.click(); 70 71 let win = await promiseSubDialogLoaded; 72 73 const dialogDoc = win.document; 74 75 is( 76 dialogDoc.getElementById("btnBlock").hidden, 77 true, 78 "Block button should not be visible in HTTPS-Only Dialog." 79 ); 80 is( 81 dialogDoc.getElementById("btnCookieSession").hidden, 82 true, 83 "Cookie specific allow button should not be visible in HTTPS-Only Dialog." 84 ); 85 is( 86 dialogDoc.getElementById("btnAllow").hidden, 87 true, 88 "Allow button should not be visible in HTTPS-Only Dialog." 89 ); 90 is( 91 dialogDoc.getElementById("btnHttpsOnlyOff").hidden, 92 false, 93 "HTTPS-Only off button should be visible in HTTPS-Only Dialog." 94 ); 95 is( 96 dialogDoc.getElementById("btnHttpsOnlyOffTmp").hidden, 97 false, 98 "HTTPS-Only temporary off button should be visible in HTTPS-Only Dialog." 99 ); 100 101 // Reset prefs and close the tab 102 await SpecialPowers.flushPrefEnv(); 103 BrowserTestUtils.removeTab(gBrowser.selectedTab); 104 }); 105 106 /** 107 * Second Test 108 * Checks permissions are added and removed correctly. 109 * 110 * Each test opens a new dialog, performs an action (second argument), 111 * then closes the dialog and checks if the changes were made (third argument). 112 */ 113 add_task(async function checkDialogFunctionality() { 114 // Enable HTTPS-Only Mode for every window, so the exceptions dialog is accessible. 115 await setHttpsOnlyPref("everywhere"); 116 117 // Open the privacy-pane in about:preferences 118 await openPreferencesViaOpenPreferencesAPI("panePrivacy", { 119 leaveOpen: true, 120 }); 121 const preferencesDoc = gBrowser.contentDocument; 122 123 // Test if we can add permanent exceptions 124 await runTest( 125 preferencesDoc, 126 elements => { 127 assertListContents(elements, []); 128 129 elements.url.value = "test.com"; 130 elements.btnAllow.doCommand(); 131 132 assertListContents(elements, [["http://test.com", elements.allowL10nId]]); 133 }, 134 () => [ 135 { 136 type: "https-only-load-insecure", 137 origin: "http://test.com", 138 data: "added", 139 capability: Ci.nsIPermissionManager.ALLOW_ACTION, 140 expireType: Ci.nsIPermissionManager.EXPIRE_NEVER, 141 }, 142 ] 143 ); 144 145 // Test if items are retained, and if temporary exceptions are added correctly 146 await runTest( 147 preferencesDoc, 148 elements => { 149 assertListContents(elements, [["http://test.com", elements.allowL10nId]]); 150 151 elements.url.value = "1.1.1.1:8080"; 152 elements.btnAllowSession.doCommand(); 153 154 assertListContents(elements, [ 155 ["http://test.com", elements.allowL10nId], 156 ["http://1.1.1.1:8080", elements.allowSessionL10nId], 157 ]); 158 }, 159 () => [ 160 { 161 type: "https-only-load-insecure", 162 origin: "http://1.1.1.1:8080", 163 data: "added", 164 capability: Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION, 165 expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, 166 }, 167 ] 168 ); 169 170 // Test if we can remove the permissions one-by-one 171 await runTest( 172 preferencesDoc, 173 elements => { 174 while (elements.richlistbox.itemCount) { 175 elements.richlistbox.selectedIndex = 0; 176 elements.btnRemove.doCommand(); 177 } 178 assertListContents(elements, []); 179 }, 180 elements => { 181 let richlistItems = elements.richlistbox.getElementsByAttribute( 182 "origin", 183 "*" 184 ); 185 let observances = []; 186 for (let item of richlistItems) { 187 observances.push({ 188 type: "https-only-load-insecure", 189 origin: item.getAttribute("origin"), 190 data: "deleted", 191 }); 192 } 193 return observances; 194 } 195 ); 196 197 // Test if all inputs with an https scheme are added with an http scheme, 198 // while other schemes are kept as they are. (Bug 1757297) 199 await runTest( 200 preferencesDoc, 201 elements => { 202 assertListContents(elements, []); 203 204 elements.url.value = "http://test.com"; 205 elements.btnAllow.doCommand(); 206 207 assertListContents(elements, [["http://test.com", elements.allowL10nId]]); 208 209 elements.url.value = "https://test.com"; 210 elements.btnAllow.doCommand(); 211 212 assertListContents(elements, [["http://test.com", elements.allowL10nId]]); 213 214 elements.url.value = "https://test.org"; 215 elements.btnAllow.doCommand(); 216 217 assertListContents(elements, [ 218 ["http://test.com", elements.allowL10nId], 219 ["http://test.org", elements.allowL10nId], 220 ]); 221 222 elements.url.value = "moz-extension://test"; 223 elements.btnAllow.doCommand(); 224 225 assertListContents(elements, [ 226 ["http://test.com", elements.allowL10nId], 227 ["http://test.org", elements.allowL10nId], 228 ["moz-extension://test", elements.allowL10nId], 229 ]); 230 }, 231 () => [ 232 { 233 type: "https-only-load-insecure", 234 origin: "http://test.com", 235 data: "added", 236 capability: Ci.nsIPermissionManager.ALLOW_ACTION, 237 expireType: Ci.nsIPermissionManager.EXPIRE_NEVER, 238 }, 239 { 240 type: "https-only-load-insecure", 241 origin: "http://test.org", 242 data: "added", 243 capability: Ci.nsIPermissionManager.ALLOW_ACTION, 244 expireType: Ci.nsIPermissionManager.EXPIRE_NEVER, 245 }, 246 { 247 type: "https-only-load-insecure", 248 origin: "moz-extension://test", 249 data: "added", 250 capability: Ci.nsIPermissionManager.ALLOW_ACTION, 251 expireType: Ci.nsIPermissionManager.EXPIRE_NEVER, 252 }, 253 ] 254 ); 255 256 // Test if we can remove all permissions at once 257 await runTest( 258 preferencesDoc, 259 elements => { 260 elements.btnRemoveAll.doCommand(); 261 assertListContents(elements, []); 262 }, 263 elements => { 264 let richlistItems = elements.richlistbox.getElementsByAttribute( 265 "origin", 266 "*" 267 ); 268 let observances = []; 269 for (let item of richlistItems) { 270 observances.push({ 271 type: "https-only-load-insecure", 272 origin: item.getAttribute("origin"), 273 data: "deleted", 274 }); 275 } 276 return observances; 277 } 278 ); 279 280 SpecialPowers.clearUserPref("dom.security.https_only_mode_ever_enabled_pbm"); 281 BrowserTestUtils.removeTab(gBrowser.selectedTab); 282 }); 283 284 /** 285 * Changes HTTPS-Only Mode pref 286 * 287 * @param {string} state "everywhere", "private", "off" 288 */ 289 async function setHttpsOnlyPref(state) { 290 await SpecialPowers.pushPrefEnv({ 291 set: [ 292 ["dom.security.https_only_mode", state === "everywhere"], 293 ["dom.security.https_only_mode_pbm", state === "private"], 294 ], 295 }); 296 } 297 298 /** 299 * Changes HTTPS-First Mode pref 300 * 301 * @param {string} state "everywhere", "private", "off" 302 */ 303 async function setHttpsFirstPref(state) { 304 await SpecialPowers.pushPrefEnv({ 305 set: [ 306 ["dom.security.https_first", state === "everywhere"], 307 ["dom.security.https_first_pbm", state === "private"], 308 ], 309 }); 310 } 311 312 /** 313 * Opens new exceptions dialog, runs test function 314 * 315 * @param {HTMLElement} preferencesDoc document of about:preferences tab 316 * @param {function} test function to call when dialog is open 317 * @param {Array} observances permission changes to observe (order is important) 318 */ 319 async function runTest(preferencesDoc, test, observancesFn) { 320 // Click on exception-button and wait for dialog to open 321 let promiseSubDialogLoaded = promiseLoadSubDialog( 322 "chrome://browser/content/preferences/dialogs/permissions.xhtml" 323 ); 324 preferencesDoc.getElementById("httpsOnlyExceptionButton").click(); 325 326 let win = await promiseSubDialogLoaded; 327 328 // Create a bunch of references to UI-elements for the test-function 329 const doc = win.document; 330 let elements = { 331 richlistbox: doc.getElementById("permissionsBox"), 332 url: doc.getElementById("url"), 333 btnAllow: doc.getElementById("btnHttpsOnlyOff"), 334 btnAllowSession: doc.getElementById("btnHttpsOnlyOffTmp"), 335 btnRemove: doc.getElementById("removePermission"), 336 btnRemoveAll: doc.getElementById("removeAllPermissions"), 337 allowL10nId: win.gPermissionManager._getCapabilityL10nId( 338 Ci.nsIPermissionManager.ALLOW_ACTION 339 ), 340 allowSessionL10nId: win.gPermissionManager._getCapabilityL10nId( 341 Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION 342 ), 343 }; 344 345 // Some observances need to be computed based on the current state. 346 const observances = observancesFn(elements); 347 348 // Run test function 349 await test(elements); 350 351 // Click on "Save changes" and wait for permission changes. 352 let btnApplyChanges = doc.querySelector("dialog").getButton("accept"); 353 let observeAllPromise = createObserveAllPromise(observances); 354 355 btnApplyChanges.doCommand(); 356 await observeAllPromise; 357 } 358 359 function assertListContents(elements, expected) { 360 is( 361 elements.richlistbox.itemCount, 362 expected.length, 363 "Richlistbox should match the expected amount of exceptions." 364 ); 365 366 for (let i = 0; i < expected.length; i++) { 367 let website = expected[i][0]; 368 let listItem = elements.richlistbox.getElementsByAttribute( 369 "origin", 370 website 371 ); 372 is(listItem.length, 1, "Each origin should be unique"); 373 is( 374 listItem[0] 375 .querySelector(".website-capability-value") 376 .getAttribute("data-l10n-id"), 377 expected[i][1], 378 "List item capability should match expected l10n-id" 379 ); 380 } 381 }