tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });