browser_anti_tracking_panel.js (12723B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Load anti-tracking test utilities 7 Services.scriptloader.loadSubScript( 8 "chrome://mochitests/content/browser/toolkit/components/antitracking/test/browser/head.js", 9 this 10 ); 11 12 const BLOCKED_STATE = "Blocked 🛑"; 13 const NOT_BLOCKED_STATE = "Not Blocked"; 14 15 /** 16 * Creates and loads a tracker iframe in the current page. 17 * Similar to the approach used in browser_siteSpecificWorkAroundsComplex.js 18 */ 19 async function loadTracker( 20 browser, 21 trackerUrl = "https://tracking.example.org" 22 ) { 23 const blockedPromise = waitForContentBlockingEvent(window).then( 24 () => "blocked" 25 ); 26 27 const loadPromise = SpecialPowers.spawn( 28 browser, 29 [trackerUrl], 30 async function (url) { 31 // Create a unique iframe ID to avoid conflicts 32 const iframe = content.document.createElement("iframe"); 33 34 const iframeLoadPromise = ContentTaskUtils.waitForEvent( 35 iframe, 36 "load" 37 ).then(() => "loaded"); 38 39 iframe.src = url; 40 content.document.body.appendChild(iframe); 41 return iframeLoadPromise; 42 } 43 ); 44 45 return Promise.race([loadPromise, blockedPromise]); 46 } 47 48 add_setup(async function () { 49 await SpecialPowers.pushPrefEnv({ 50 set: [ 51 ["browser.contentblocking.category", "strict"], 52 ["privacy.trackingprotection.enabled", true], 53 ["privacy.trackingprotection.allow_list.baseline.enabled", false], 54 ["privacy.trackingprotection.allow_list.convenience.enabled", false], 55 [ 56 "urlclassifier.trackingTable.testEntries", 57 "tracking.example.org,social-tracking.example.org", 58 ], 59 [ 60 "urlclassifier.trackingAnnotationTable.testEntries", 61 "tracking.example.org,social-tracking.example.org", 62 ], 63 ], 64 }); 65 await UrlClassifierTestUtils.addTestTrackers(); 66 registerCleanupFunction(async () => { 67 UrlClassifierTestUtils.cleanupTestTrackers(); 68 }); 69 }); 70 71 async function cleanup(toolbox) { 72 await closeToolboxAndTab(toolbox); 73 } 74 75 /** 76 * Test that a single blocked request is shown in the panel 77 */ 78 add_task(async function test_single_blocked_request_shown() { 79 const tab = await addTab("https://example.com/"); 80 const toolbox = await openToolboxForTab(tab, "antitracking"); 81 const panel = toolbox.getCurrentPanel(); 82 const panelWindow = panel.panelWin; 83 84 const webcompatDebugger = panelWindow.AntiTracking.debugger; 85 86 const result = await loadTracker(tab.linkedBrowser); 87 is(result, "blocked", "Tracker should be blocked"); 88 89 const hasTrackers = !!Object.keys(webcompatDebugger.allTrackers).length; 90 91 ok(hasTrackers, "There should be at least one tracker in the debugger"); 92 93 const trackerRows = panelWindow.document.querySelectorAll( 94 "#tracker-table tbody tr" 95 ); 96 Assert.equal( 97 trackerRows.length, 98 1, 99 "There should be one tracker shown in the panel" 100 ); 101 102 // Check that the blocked tracker is displayed 103 const blockedColumn = trackerRows[0].querySelector("td:nth-child(2)"); 104 is( 105 blockedColumn.textContent, 106 BLOCKED_STATE, 107 "The tracker should be marked as blocked" 108 ); 109 110 // Check hostname is displayed 111 const hostnameColumn = trackerRows[0].querySelector("td:nth-child(3)"); 112 ok( 113 hostnameColumn.textContent.includes("tracking.example.org"), 114 "Tracker hostname should be displayed" 115 ); 116 117 await cleanup(toolbox); 118 }); 119 120 /** 121 * Test that when no requests are blocked, nothing is shown in the panel 122 */ 123 add_task(async function test_no_blocked_requests_shown() { 124 const tab = await addTab("https://example.com/"); 125 const toolbox = await openToolboxForTab(tab, "antitracking"); 126 const panel = toolbox.getCurrentPanel(); 127 const panelWindow = panel.panelWin; 128 129 const table = panelWindow.document.getElementById("tracker-table"); 130 const trackerRows = table.querySelectorAll("tbody tr"); 131 132 is( 133 trackerRows.length, 134 0, 135 "No trackers should be shown when none are blocked" 136 ); 137 138 // Should show the "no content" message 139 const noContentMessage = panelWindow.document.querySelector( 140 ".no-content-message" 141 ); 142 ok( 143 noContentMessage && 144 noContentMessage.textContent.includes("No blocked resources"), 145 "Should show no blocked resources message" 146 ); 147 148 await cleanup(toolbox); 149 }); 150 151 /** 152 * Test that unblocking a tracker works correctly 153 */ 154 add_task(async function test_unblock_single_request() { 155 const tab = await addTab("https://example.com/"); 156 const toolbox = await openToolboxForTab(tab, "antitracking"); 157 const panel = toolbox.getCurrentPanel(); 158 const panelWindow = panel.panelWin; 159 const webcompatDebugger = panelWindow.AntiTracking.debugger; 160 161 let result = await loadTracker(tab.linkedBrowser); 162 is(result, "blocked", "Tracker should be blocked initially"); 163 164 const trackerRows = panelWindow.document.querySelectorAll( 165 "#tracker-table tbody tr" 166 ); 167 Assert.equal( 168 trackerRows.length, 169 1, 170 "There should be one tracker shown in the panel" 171 ); 172 173 const firstRow = trackerRows[0]; 174 175 const blockedColumn = firstRow.querySelector("td:nth-child(2)"); 176 is( 177 blockedColumn.textContent, 178 BLOCKED_STATE, 179 "Tracker should initially be blocked" 180 ); 181 182 const unblockButton = firstRow.querySelector("td:last-child button"); 183 is(unblockButton.textContent, "Unblock", "Button should say 'Unblock'"); 184 185 const browserLoadedPromise = BrowserTestUtils.browserLoaded( 186 tab.linkedBrowser 187 ); 188 189 unblockButton.click(); 190 await browserLoadedPromise; 191 192 const updatedTrackerRows = panelWindow.document.querySelectorAll( 193 "#tracker-table tbody tr" 194 ); 195 const updatedFirstRow = updatedTrackerRows[0]; 196 197 const updatedBlockedColumn = updatedFirstRow.querySelector("td:nth-child(2)"); 198 is( 199 updatedBlockedColumn.textContent, 200 NOT_BLOCKED_STATE, 201 "Tracker should now be unblocked in UI" 202 ); 203 204 const updatedButton = updatedFirstRow.querySelector("td:last-child button"); 205 is(updatedButton.textContent, "Block", "Button should now say 'Block'"); 206 207 ok( 208 webcompatDebugger.unblockedChannels.has("tracking.example.org"), 209 "Tracker should now be unblocked in debugger state" 210 ); 211 212 await reloadBrowser(tab.linkedBrowser); 213 214 // Test that the tracker would now load 215 result = await loadTracker(tab.linkedBrowser); 216 is(result, "loaded", "Tracker should load successfully after unblocking"); 217 218 await cleanup(toolbox); 219 }); 220 221 /** 222 * Test that clicking the "block" button for a request blocks it 223 */ 224 add_task(async function test_block_unblocked_channel() { 225 const tab = await addTab("https://example.com/"); 226 const toolbox = await openToolboxForTab(tab, "antitracking"); 227 const panel = toolbox.getCurrentPanel(); 228 const panelWindow = panel.panelWin; 229 const webcompatDebugger = panelWindow.AntiTracking.debugger; 230 231 // Set up the debugger state to simulate an unblocked tracker 232 webcompatDebugger.unblockedChannels = new Set(["tracking.example.org"]); 233 webcompatDebugger.allTrackers = { 234 "tracking.example.org": "Tracking Protection", 235 }; 236 237 webcompatDebugger.populateTrackerTable(); 238 239 const firstRow = panelWindow.document.querySelectorAll( 240 "#tracker-table tbody tr" 241 )[0]; 242 243 const blockButton = firstRow.querySelector("td:last-child button"); 244 ok(blockButton, "Block button should be present"); 245 is(blockButton.textContent, "Block", "Button should say 'Block'"); 246 247 const browserLoadedPromise = BrowserTestUtils.browserLoaded( 248 tab.linkedBrowser 249 ); 250 blockButton.click(); 251 await browserLoadedPromise; 252 253 const updatedTrackerRows = panelWindow.document.querySelectorAll( 254 "#tracker-table tbody tr" 255 ); 256 const updatedFirstRow = updatedTrackerRows[0]; 257 258 const updatedBlockedColumn = updatedFirstRow.querySelector("td:nth-child(2)"); 259 is( 260 updatedBlockedColumn.textContent, 261 BLOCKED_STATE, 262 "Tracker should now be blocked in UI" 263 ); 264 265 const updatedButton = updatedFirstRow.querySelector("td:last-child button"); 266 is(updatedButton.textContent, "Unblock", "Button should now say 'Unblock'"); 267 268 ok( 269 !webcompatDebugger.unblockedChannels.has("tracking.example.org"), 270 "Tracker should now be blocked (removed from unblockedChannels)" 271 ); 272 273 const result = await loadTracker(tab.linkedBrowser); 274 is( 275 result, 276 "blocked", 277 "Tracker should be blocked after clicking block button" 278 ); 279 280 await cleanup(toolbox); 281 }); 282 283 /** 284 * Test when there are multiple trackers and one is unblocked, the others are still blocked 285 */ 286 add_task(async function test_multiple_trackers_one_unblocked() { 287 const tab = await addTab("https://example.com/"); 288 const toolbox = await openToolboxForTab(tab, "antitracking"); 289 const panel = toolbox.getCurrentPanel(); 290 const panelWindow = panel.panelWin; 291 292 // Load multiple trackers 293 let result = await loadTracker( 294 tab.linkedBrowser, 295 "https://tracking.example.org" 296 ); 297 is(result, "blocked", "First tracker should be blocked"); 298 299 result = await loadTracker( 300 tab.linkedBrowser, 301 "https://social-tracking.example.org" 302 ); 303 is(result, "blocked", "Second tracker should also be blocked"); 304 305 const trackerRows = panelWindow.document.querySelectorAll( 306 "#tracker-table tbody tr" 307 ); 308 Assert.equal( 309 trackerRows.length, 310 2, 311 "Two trackers should be shown in the panel" 312 ); 313 314 const firstRow = trackerRows[0]; 315 const unblockButton = firstRow.querySelector("td:last-child button"); 316 317 const browserLoadedPromise = BrowserTestUtils.browserLoaded( 318 tab.linkedBrowser 319 ); 320 unblockButton.click(); 321 322 await browserLoadedPromise; 323 324 const updatedTrackerRows = panelWindow.document.querySelectorAll( 325 "#tracker-table tbody tr" 326 ); 327 const updatedFirstRow = updatedTrackerRows[0]; 328 329 const updatedBlockedColumn = updatedFirstRow.querySelector("td:nth-child(2)"); 330 is( 331 updatedBlockedColumn.textContent, 332 NOT_BLOCKED_STATE, 333 "First tracker should now be unblocked in UI" 334 ); 335 336 result = await loadTracker(tab.linkedBrowser, "https://tracking.example.org"); 337 is(result, "loaded", "First tracker should be loaded"); 338 339 await cleanup(toolbox); 340 }); 341 342 /** 343 * Test the "unblock selected" button functionality with multiple selected trackers 344 */ 345 add_task(async function test_unblock_selected_button() { 346 const tab = await addTab("https://example.com/"); 347 const toolbox = await openToolboxForTab(tab, "antitracking"); 348 const panel = toolbox.getCurrentPanel(); 349 const panelWindow = panel.panelWin; 350 const webcompatDebugger = panelWindow.AntiTracking.debugger; 351 352 // Load two trackers 353 let result = await loadTracker( 354 tab.linkedBrowser, 355 "https://tracking.example.org" 356 ); 357 is(result, "blocked", "First tracker should be blocked"); 358 359 result = await loadTracker( 360 tab.linkedBrowser, 361 "https://social-tracking.example.org" 362 ); 363 is(result, "blocked", "Second tracker should also be blocked"); 364 365 const rowCheckboxes = panelWindow.document.querySelectorAll( 366 "#tracker-table tbody .row-checkbox" 367 ); 368 is(rowCheckboxes.length, 2, "Two row checkboxes should be present"); 369 370 rowCheckboxes[0].click(); 371 rowCheckboxes[1].click(); 372 373 const unblockSelectedButton = 374 panelWindow.document.getElementById("unblock-selected"); 375 ok(unblockSelectedButton, "Unblock selected button should be present"); 376 377 const browserLoadedPromise = BrowserTestUtils.browserLoaded( 378 tab.linkedBrowser 379 ); 380 381 unblockSelectedButton.click(); 382 383 await browserLoadedPromise; 384 385 // Verify both trackers are now unblocked in the debugger state 386 ok( 387 webcompatDebugger.unblockedChannels.has("tracking.example.org"), 388 "First tracker should be unblocked in debugger state" 389 ); 390 ok( 391 webcompatDebugger.unblockedChannels.has("social-tracking.example.org"), 392 "Second tracker should be unblocked in debugger state" 393 ); 394 395 // Verify UI shows both trackers as unblocked 396 const updatedTrackerRows = panelWindow.document.querySelectorAll( 397 "#tracker-table tbody tr" 398 ); 399 // Verify buttons changed to "Block" 400 const firstRowButton = updatedTrackerRows[0].querySelector( 401 "td:last-child button" 402 ); 403 const secondRowButton = updatedTrackerRows[1].querySelector( 404 "td:last-child button" 405 ); 406 407 is( 408 firstRowButton.textContent, 409 "Block", 410 "First tracker button should say 'Block'" 411 ); 412 is( 413 secondRowButton.textContent, 414 "Block", 415 "Second tracker button should say 'Block'" 416 ); 417 418 // Test that both trackers now load successfully 419 await reloadBrowser(tab.linkedBrowser); 420 421 result = await loadTracker(tab.linkedBrowser, "https://tracking.example.org"); 422 is(result, "loaded", "First tracker should load after bulk unblock"); 423 424 result = await loadTracker( 425 tab.linkedBrowser, 426 "https://social-tracking.example.org" 427 ); 428 is(result, "loaded", "Second tracker should load after bulk unblock"); 429 430 await cleanup(toolbox); 431 });