browser_captivePortal_lna.js (6976B)
1 "use strict"; 2 3 const { HttpServer } = ChromeUtils.importESModule( 4 "resource://testing-common/httpd.sys.mjs" 5 ); 6 7 const PUBLIC_PAGE_URL = 8 "https://example.com/browser/browser/base/content/test/captivePortal/file_captivePortal_lna.html"; 9 let SERVER_RESPONSE = ""; 10 const CANONICAL_HTML = "<!DOCTYPE html><html><body>hello</body></html>"; 11 let gHttpServer; 12 let privateServer; 13 let localServer; 14 15 add_setup(async function setup() { 16 // Set up local HTTP server 17 gHttpServer = new HttpServer(); 18 gHttpServer.start(); 19 gHttpServer.registerPathHandler("/", (request, response) => { 20 response.setHeader("Access-Control-Allow-Origin", "*", false); 21 response.setHeader("Content-Type", "text/html", false); 22 response.setStatusLine(request.httpVersion, 200, "OK"); 23 response.write(SERVER_RESPONSE); 24 }); 25 26 privateServer = new HttpServer(); 27 privateServer.start(); 28 privateServer.registerPathHandler("/", (request, response) => { 29 response.setHeader("Access-Control-Allow-Origin", "*", false); 30 response.setHeader("Content-Type", "text/plain", false); 31 response.setStatusLine(request.httpVersion, 200, "OK"); 32 response.write("hello"); 33 }); 34 35 localServer = new HttpServer(); 36 localServer.start(); 37 localServer.registerPathHandler("/", (request, response) => { 38 response.setHeader("Access-Control-Allow-Origin", "*", false); 39 response.setHeader("Content-Type", "text/plain", false); 40 response.setStatusLine(request.httpVersion, 200, "OK"); 41 response.write("hello"); 42 }); 43 44 SERVER_RESPONSE = `<!DOCTYPE html><html><body><script>fetch('http://localhost:${privateServer.identity.primaryPort}/').then(r => r.text()).then(t => document.body.textContent = t);</script></body></html>`; 45 46 await SpecialPowers.pushPrefEnv({ 47 set: [ 48 ["network.lna.blocking", true], 49 ["network.http.rcwn.enabled", false], 50 [ 51 "captivedetect.canonicalURL", 52 `http://127.0.0.1:${gHttpServer.identity.primaryPort}/`, 53 ], 54 ["captivedetect.canonicalContent", CANONICAL_HTML], 55 // Set up address space override to this page appear as public 56 [ 57 "network.lna.address_space.public.override", 58 `127.0.0.1:${gHttpServer.identity.primaryPort}`, 59 ], 60 [ 61 "network.lna.address_space.private.override", 62 `127.0.0.1:${privateServer.identity.primaryPort}`, 63 ], 64 ], 65 }); 66 67 registerCleanupFunction(async () => { 68 await gHttpServer.stop(); 69 await privateServer.stop(); 70 await localServer.stop(); 71 }); 72 }); 73 74 function observeRequest(url) { 75 return new Promise(resolve => { 76 const observer = { 77 observe(subject, topic) { 78 if (topic !== "http-on-stop-request") { 79 return; 80 } 81 82 let channel = subject.QueryInterface(Ci.nsIHttpChannel); 83 if (!channel || channel.URI.spec !== url) { 84 return; 85 } 86 87 Services.obs.removeObserver(observer, "http-on-stop-request"); 88 resolve(channel.status); 89 }, 90 }; 91 Services.obs.addObserver(observer, "http-on-stop-request"); 92 }); 93 } 94 95 // Tests that a captive portal tab making a request to a local network 96 // resource does not trigger an LNA permission prompt. 97 add_task(async function test_captivePortalTab_noLnaPrompt() { 98 // Simulate portal detection 99 await portalDetected(); 100 101 let canonicalURL = `http://127.0.0.1:${gHttpServer.identity.primaryPort}/`; 102 let portalTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, canonicalURL); 103 104 // Wait for the captive portal notification 105 let notification = await ensurePortalNotification(window); 106 107 // Click the notification button to open the captive portal tab 108 let button = notification.querySelector("button.notification-button"); 109 button.click(); 110 111 let portalTab = await portalTabPromise; 112 113 // Verify the tab has the isCaptivePortalTab flag set 114 ok( 115 portalTab.linkedBrowser.browsingContext.isCaptivePortalTab, 116 "Captive portal tab should have isCaptivePortalTab flag set" 117 ); 118 119 // Wait for the fetch to complete and the page content to be updated 120 await BrowserTestUtils.waitForCondition( 121 () => 122 SpecialPowers.spawn(portalTab.linkedBrowser, [], () => { 123 return content.document.body.textContent === "hello"; 124 }), 125 "Waiting for fetch response to be displayed on the page" 126 ); 127 128 // Verify the page content contains "hello" from the fetch response 129 let bodyText = await SpecialPowers.spawn(portalTab.linkedBrowser, [], () => { 130 return content.document.body.textContent; 131 }); 132 is(bodyText, "hello", "Page should display the fetch response"); 133 134 // Verify that no LNA permission prompt appeared 135 let lnaPrompt = PopupNotifications.getNotification( 136 "local-network", 137 portalTab.linkedBrowser 138 ); 139 ok( 140 !lnaPrompt, 141 "Should not show LNA prompt for captive portal tab accessing local network" 142 ); 143 144 await SpecialPowers.spawn( 145 portalTab.linkedBrowser, 146 [localServer.identity.primaryPort], 147 port => { 148 content.console.log("url", `http://localhost:${port}/`); 149 content.fetch(`http://localhost:${port}/`); 150 } 151 ); 152 await BrowserTestUtils.waitForCondition( 153 () => 154 PopupNotifications.getNotification("localhost", portalTab.linkedBrowser), 155 "Waiting for localhost prompt" 156 ); 157 158 // Clean up 159 BrowserTestUtils.removeTab(portalTab); 160 await freePortal(true); 161 }); 162 163 // Tests that a regular tab (non-captive portal) does NOT trigger an LNA 164 // permission prompt when accessing local network resources during captive portal. 165 add_task(async function test_regularTab_noLnaPrompt_duringCaptivePortal() { 166 await portalDetected(); 167 168 let canonicalURL = `http://127.0.0.1:${gHttpServer.identity.primaryPort}/`; 169 170 // Wait for the captive portal notification 171 await ensurePortalNotification(window); 172 173 const tab = await BrowserTestUtils.openNewForegroundTab( 174 gBrowser, 175 canonicalURL 176 ); 177 178 // Verify the tab does not have the isCaptivePortalTab flag set 179 ok( 180 !tab.linkedBrowser.browsingContext.isCaptivePortalTab, 181 "Regular tab should not have isCaptivePortalTab flag set" 182 ); 183 184 // Wait for the fetch to complete and the page content to be updated 185 await BrowserTestUtils.waitForCondition( 186 () => 187 SpecialPowers.spawn(tab.linkedBrowser, [], () => { 188 return content.document.body.textContent === "hello"; 189 }), 190 "Waiting for fetch response to be displayed on the page" 191 ); 192 193 // Verify the page content contains "hello" from the fetch response 194 let bodyText = await SpecialPowers.spawn(tab.linkedBrowser, [], () => { 195 return content.document.body.textContent; 196 }); 197 is(bodyText, "hello", "Page should display the fetch response"); 198 199 // Verify that no local network LNA permission prompt appeared 200 let lnaPrompt = PopupNotifications.getNotification( 201 "local-network", 202 tab.linkedBrowser 203 ); 204 ok( 205 !lnaPrompt, 206 "Should not show local network LNA prompt for regular tab during captive portal" 207 ); 208 209 // Clean up 210 BrowserTestUtils.removeTab(tab); 211 await freePortal(true); 212 });