browser_closeCapPortalTabCanonicalURL.js (7543B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { HttpServer } = ChromeUtils.importESModule( 7 "resource://testing-common/httpd.sys.mjs" 8 ); 9 const LOGIN_LINK = `<html><body><a href="/unlock">login</a></body></html>`; 10 const LOGIN_URL = "http://localhost:8080/login"; 11 const CANONICAL_SUCCESS_URL = "http://localhost:8080/success"; 12 const CPS = Cc["@mozilla.org/network/captive-portal-service;1"].getService( 13 Ci.nsICaptivePortalService 14 ); 15 const META_CANONICAL_CONTENT = `<meta http-equiv=\"refresh\" content=\"0;url=http://support.mozilla.org:8080/sumo\"/>`; 16 17 const cps = Cc["@mozilla.org/network/captive-portal-service;1"].getService( 18 Ci.nsICaptivePortalService 19 ); 20 21 let server; 22 let loginPageShown = false; 23 let showSumo = false; 24 const SUMO_URL = "https://support.mozilla.org:8080/sumo"; 25 26 function redirectHandler(request, response) { 27 if (showSumo) { 28 response.setHeader("Content-Type", "text/html"); 29 response.bodyOutputStream.write( 30 META_CANONICAL_CONTENT, 31 META_CANONICAL_CONTENT.length 32 ); 33 return; 34 } 35 if (loginPageShown) { 36 return; 37 } 38 response.setStatusLine(request.httpVersion, 302, "captive"); 39 response.setHeader("Content-Type", "text/html"); 40 response.setHeader("Location", LOGIN_URL); 41 } 42 43 function sumoHandler(request, response) { 44 response.setHeader("Content-Type", "text/html"); 45 let content = ` 46 <!DOCTYPE html> 47 <html lang="en"> 48 <head> 49 <meta charset="utf-8"/> 50 </head> 51 <body> 52 Testing 53 </body> 54 </html> 55 `; 56 response.bodyOutputStream.write(content, content.length); 57 } 58 59 function loginHandler(request, response) { 60 response.setHeader("Content-Type", "text/html"); 61 response.bodyOutputStream.write(LOGIN_LINK, LOGIN_LINK.length); 62 loginPageShown = true; 63 } 64 65 function unlockHandler(request, response) { 66 response.setStatusLine(request.httpVersion, 302, "login complete"); 67 response.setHeader("Content-Type", "text/html"); 68 response.setHeader("Location", CANONICAL_SUCCESS_URL); 69 } 70 71 add_setup(async function () { 72 // Set up a mock server for handling captive portal redirect. 73 server = new HttpServer(); 74 server.identity.add("http", "support.mozilla.org", 8080); 75 server.registerPathHandler("/success", redirectHandler); 76 server.registerPathHandler("/login", loginHandler); 77 server.registerPathHandler("/unlock", unlockHandler); 78 server.registerPathHandler("/sumo", sumoHandler); 79 server.start(8080); 80 registerCleanupFunction(async () => await server.stop()); 81 info("Mock server is now set up for captive portal redirect"); 82 83 await SpecialPowers.pushPrefEnv({ 84 set: [ 85 ["captivedetect.canonicalURL", CANONICAL_SUCCESS_URL], 86 ["captivedetect.canonicalContent", META_CANONICAL_CONTENT], 87 ["network.dns.native-is-localhost", true], 88 ], 89 }); 90 }); 91 92 // This test checks if the captive portal tab is removed after the 93 // sucess/abort events are fired, assuming the tab has already redirected 94 // to the canonical URL before they are fired. 95 add_task(async function checkCaptivePortalTabCloseOnCanonicalURL_one() { 96 await portalDetected(); 97 let errorTab = await openCaptivePortalErrorTab(); 98 let tab = await openCaptivePortalLoginTab(errorTab, LOGIN_URL); 99 let browser = tab.linkedBrowser; 100 101 let redirectedToCanonicalURL = BrowserTestUtils.browserLoaded( 102 browser, 103 false, 104 CANONICAL_SUCCESS_URL 105 ); 106 let errorPageReloaded = BrowserTestUtils.waitForErrorPage( 107 errorTab.linkedBrowser 108 ); 109 110 await SpecialPowers.spawn(browser, [], async () => { 111 let doc = content.document; 112 let loginButton = doc.querySelector("a"); 113 await ContentTaskUtils.waitForCondition( 114 () => loginButton, 115 "Login button on the captive portal tab is visible" 116 ); 117 info("Clicking the login button on the captive portal tab page"); 118 await EventUtils.synthesizeMouseAtCenter(loginButton, {}, content); 119 }); 120 121 await redirectedToCanonicalURL; 122 info( 123 "Re-direct to canonical URL in the captive portal tab was succcessful after login" 124 ); 125 126 let tabClosed = BrowserTestUtils.waitForTabClosing(tab); 127 Services.obs.notifyObservers(null, "captive-portal-login-success"); 128 await tabClosed; 129 info( 130 "Captive portal tab was closed on re-direct to canonical URL after login as expected" 131 ); 132 133 await errorPageReloaded; 134 info("Captive portal error page was reloaded"); 135 gBrowser.removeTab(errorTab); 136 }); 137 138 // This test checks if the captive portal tab is removed on location change 139 // i.e. when it is re-directed to the canonical URL long after success/abort 140 // event handlers are executed. 141 add_task(async function checkCaptivePortalTabCloseOnCanonicalURL_two() { 142 loginPageShown = false; 143 await portalDetected(); 144 let errorTab = await openCaptivePortalErrorTab(); 145 let tab = await openCaptivePortalLoginTab(errorTab, LOGIN_URL); 146 let browser = tab.linkedBrowser; 147 148 let redirectedToCanonicalURL = BrowserTestUtils.waitForLocationChange( 149 gBrowser, 150 CANONICAL_SUCCESS_URL 151 ); 152 let errorPageReloaded = BrowserTestUtils.waitForErrorPage( 153 errorTab.linkedBrowser 154 ); 155 156 Services.obs.notifyObservers(null, "captive-portal-login-success"); 157 await TestUtils.waitForCondition( 158 () => CPS.state == CPS.UNLOCKED_PORTAL, 159 "Captive portal is released" 160 ); 161 162 let tabClosed = BrowserTestUtils.waitForTabClosing(tab); 163 await SpecialPowers.spawn(browser, [], async () => { 164 let doc = content.document; 165 let loginButton = doc.querySelector("a"); 166 await ContentTaskUtils.waitForCondition( 167 () => loginButton, 168 "Login button on the captive portal tab is visible" 169 ); 170 info("Clicking the login button on the captive portal tab page"); 171 await EventUtils.synthesizeMouseAtCenter(loginButton, {}, content); 172 }); 173 174 await redirectedToCanonicalURL; 175 info( 176 "Re-direct to canonical URL in the captive portal tab was succcessful after login" 177 ); 178 await tabClosed; 179 info( 180 "Captive portal tab was closed on re-direct to canonical URL after login as expected" 181 ); 182 183 await errorPageReloaded; 184 info("Captive portal error page was reloaded"); 185 gBrowser.removeTab(errorTab); 186 }); 187 188 // Test that the captive portal page is closed after a successful login 189 // even if it's self-refreshed to support.mozilla.org 190 add_task(async function checkCaptivePortalTabCloseOnCanonicalURL_three() { 191 loginPageShown = false; 192 await portalDetected(); 193 let errorTab = await openCaptivePortalErrorTab(); 194 let tab = await openCaptivePortalLoginTab(errorTab, LOGIN_URL); 195 let browser = tab.linkedBrowser; 196 197 let errorPageReloaded = BrowserTestUtils.waitForErrorPage( 198 errorTab.linkedBrowser 199 ); 200 201 let tabClosed = BrowserTestUtils.waitForTabClosing(tab); 202 showSumo = true; 203 await SpecialPowers.spawn(browser, [], async () => { 204 let doc = content.document; 205 let loginButton = doc.querySelector("a"); 206 await ContentTaskUtils.waitForCondition( 207 () => loginButton, 208 "Login button on the captive portal tab is visible" 209 ); 210 info("Clicking the login button on the captive portal tab page"); 211 await EventUtils.synthesizeMouseAtCenter(loginButton, {}, content); 212 }); 213 214 Services.obs.notifyObservers(null, "captive-portal-login-success"); 215 216 await TestUtils.waitForCondition( 217 () => CPS.state == CPS.UNLOCKED_PORTAL, 218 "Captive portal is released" 219 ); 220 221 await tabClosed; 222 info( 223 "Captive portal tab was closed on re-direct to canonical URL after login as expected" 224 ); 225 226 await errorPageReloaded; 227 info("Captive portal error page was reloaded"); 228 showSumo = false; 229 gBrowser.removeTab(errorTab); 230 });