tor-browser

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

browser_protectionsUI_cookie_banner.js (14194B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * Tests the cookie banner handling section in the protections panel.
      8 */
      9 
     10 const { TelemetryTestUtils } = ChromeUtils.importESModule(
     11  "resource://testing-common/TelemetryTestUtils.sys.mjs"
     12 );
     13 const { sinon } = ChromeUtils.importESModule(
     14  "resource://testing-common/Sinon.sys.mjs"
     15 );
     16 
     17 const { MODE_DISABLED, MODE_REJECT, MODE_REJECT_OR_ACCEPT, MODE_UNSET } =
     18  Ci.nsICookieBannerService;
     19 
     20 const exampleRules = JSON.stringify([
     21  {
     22    id: "4b18afb0-76db-4f9e-a818-ed9a783fae6a",
     23    cookies: {},
     24    click: {
     25      optIn: "#foo",
     26      presence: "#bar",
     27    },
     28    domains: ["example.com"],
     29  },
     30 ]);
     31 
     32 /**
     33 * Determines whether the cookie banner section in the protections panel should
     34 * be visible with the given configuration.
     35 *
     36 * @param {*} options - Configuration to test.
     37 * @param {number} options.featureMode - nsICookieBannerService::Modes value for
     38 * normal browsing.
     39 * @param {number} options.featureModePBM - nsICookieBannerService::Modes value
     40 * for private browsing.
     41 * @param {boolean} options.visibilityPref - State of the cookie banner UI
     42 * visibility pref.
     43 * @param {boolean} options.testPBM - Whether the window is in private browsing
     44 * mode (true) or not (false).
     45 * @returns {boolean} Whether the section should be visible for the given
     46 * config.
     47 */
     48 function cookieBannerSectionIsVisible({
     49  featureMode,
     50  featureModePBM,
     51  detectOnly,
     52  visibilityPref,
     53  testPBM,
     54 }) {
     55  if (!visibilityPref) {
     56    return false;
     57  }
     58  if (detectOnly) {
     59    return false;
     60  }
     61 
     62  return (
     63    (testPBM && featureModePBM != MODE_DISABLED) ||
     64    (!testPBM && featureMode != MODE_DISABLED)
     65  );
     66 }
     67 
     68 /**
     69 * Runs a visibility test of the cookie banner section in the protections panel.
     70 *
     71 * @param {*} options - Test options.
     72 * @param {Window} options.win - Browser window to use for testing. It's
     73 * browsing mode should match the testPBM variable.
     74 * @param {number} options.featureMode - nsICookieBannerService::Modes value for
     75 * normal browsing.
     76 * @param {number} options.featureModePBM - nsICookieBannerService::Modes value
     77 * for private browsing.
     78 * @param {boolean} options.visibilityPref - State of the cookie banner UI
     79 * visibility pref.
     80 * @param {boolean} options.testPBM - Whether the window is in private browsing
     81 * mode (true) or not (false).
     82 * @returns {Promise} Resolves once the test is complete.
     83 */
     84 async function testSectionVisibility({
     85  win,
     86  featureMode,
     87  featureModePBM,
     88  visibilityPref,
     89  testPBM,
     90 }) {
     91  info(
     92    "testSectionVisibility " +
     93      JSON.stringify({ featureMode, featureModePBM, visibilityPref, testPBM })
     94  );
     95  // initialize the pref environment
     96  await SpecialPowers.pushPrefEnv({
     97    set: [
     98      ["cookiebanners.service.mode", featureMode],
     99      ["cookiebanners.service.mode.privateBrowsing", featureModePBM],
    100      ["cookiebanners.ui.desktop.enabled", visibilityPref],
    101    ],
    102  });
    103 
    104  // Open a tab with example.com so the protections panel can be opened.
    105  await BrowserTestUtils.withNewTab(
    106    { gBrowser: win.gBrowser, url: "https://example.com" },
    107    async () => {
    108      await openProtectionsPanel(null, win);
    109 
    110      // Get panel elements to test
    111      let el = {
    112        section: win.document.getElementById(
    113          "protections-popup-cookie-banner-section"
    114        ),
    115        sectionSeparator: win.document.getElementById(
    116          "protections-popup-cookie-banner-section-separator"
    117        ),
    118        switch: win.document.getElementById(
    119          "protections-popup-cookie-banner-switch"
    120        ),
    121      };
    122 
    123      let expectVisible = cookieBannerSectionIsVisible({
    124        featureMode,
    125        featureModePBM,
    126        visibilityPref,
    127        testPBM,
    128      });
    129      is(
    130        BrowserTestUtils.isVisible(el.section),
    131        expectVisible,
    132        `Cookie banner section should be ${
    133          expectVisible ? "visible" : "not visible"
    134        }.`
    135      );
    136      is(
    137        BrowserTestUtils.isVisible(el.sectionSeparator),
    138        expectVisible,
    139        `Cookie banner section separator should be ${
    140          expectVisible ? "visible" : "not visible"
    141        }.`
    142      );
    143      is(
    144        BrowserTestUtils.isVisible(el.switch),
    145        expectVisible,
    146        `Cookie banner switch should be ${
    147          expectVisible ? "visible" : "not visible"
    148        }.`
    149      );
    150    }
    151  );
    152 
    153  await SpecialPowers.popPrefEnv();
    154 }
    155 
    156 /**
    157 * Tests cookie banner section visibility state in different configurations.
    158 */
    159 add_task(async function test_section_visibility() {
    160  // Test all combinations of cookie banner service modes and normal and
    161  // private browsing.
    162 
    163  for (let testPBM of [false, true]) {
    164    let win = window;
    165    // Open a new private window to test the panel in for testing PBM, otherwise
    166    // reuse the existing window.
    167    if (testPBM) {
    168      win = await BrowserTestUtils.openNewBrowserWindow({
    169        private: true,
    170      });
    171      win.focus();
    172    }
    173 
    174    for (let featureMode of [
    175      MODE_DISABLED,
    176      MODE_REJECT,
    177      MODE_REJECT_OR_ACCEPT,
    178    ]) {
    179      for (let featureModePBM of [
    180        MODE_DISABLED,
    181        MODE_REJECT,
    182        MODE_REJECT_OR_ACCEPT,
    183      ]) {
    184        for (let detectOnly of [false, true]) {
    185          // Testing detect only mode for normal browsing is sufficient.
    186          if (detectOnly && featureModePBM != MODE_DISABLED) {
    187            continue;
    188          }
    189          await testSectionVisibility({
    190            win,
    191            featureMode,
    192            featureModePBM,
    193            detectOnly,
    194            testPBM,
    195            visibilityPref: true,
    196          });
    197        }
    198      }
    199    }
    200 
    201    if (testPBM) {
    202      await BrowserTestUtils.closeWindow(win);
    203    }
    204  }
    205 });
    206 
    207 /**
    208 * Tests that the cookie banner section is only visible if enabled by UI pref.
    209 */
    210 add_task(async function test_section_visibility_pref() {
    211  for (let visibilityPref of [false, true]) {
    212    await testSectionVisibility({
    213      win: window,
    214      featureMode: MODE_REJECT,
    215      featureModePBM: MODE_DISABLED,
    216      testPBM: false,
    217      visibilityPref,
    218    });
    219  }
    220 });
    221 
    222 /**
    223 * Test the state of the per-site exception switch in the cookie banner section
    224 * and whether a matching per-site exception is set.
    225 *
    226 * @param {*} options
    227 * @param {Window} options.win - Chrome window to test exception for (selected
    228 * tab).
    229 * @param {boolean} options.isPBM - Whether the given window is in private
    230 * browsing mode.
    231 * @param {string} options.expectedSwitchState - Whether the switch is expected to be
    232 * "on" (CBH enabled), "off" (user added exception), or "unsupported" (no rules for site).
    233 */
    234 function assertSwitchAndPrefState({ win, isPBM, expectedSwitchState }) {
    235  let el = {
    236    section: win.document.getElementById(
    237      "protections-popup-cookie-banner-section"
    238    ),
    239    switch: win.document.getElementById(
    240      "protections-popup-cookie-banner-switch"
    241    ),
    242    labelON: win.document.querySelector(
    243      "#protections-popup-cookie-banner-detected"
    244    ),
    245    labelOFF: win.document.querySelector(
    246      "#protections-popup-cookie-banner-site-disabled"
    247    ),
    248    labelUNDETECTED: win.document.querySelector(
    249      "#protections-popup-cookie-banner-undetected"
    250    ),
    251  };
    252 
    253  let currentURI = win.gBrowser.currentURI;
    254  let pref = Services.cookieBanners.getDomainPref(currentURI, isPBM);
    255  if (expectedSwitchState == "on") {
    256    Assert.equal(
    257      el.section.dataset.state,
    258      "detected",
    259      "CBH switch is set to ON"
    260    );
    261 
    262    ok(BrowserTestUtils.isVisible(el.labelON), "ON label should be visible");
    263    ok(
    264      !BrowserTestUtils.isVisible(el.labelOFF),
    265      "OFF label should not be visible"
    266    );
    267    ok(
    268      !BrowserTestUtils.isVisible(el.labelUNDETECTED),
    269      "UNDETECTED label should not be visible"
    270    );
    271 
    272    is(
    273      pref,
    274      MODE_UNSET,
    275      `There should be no per-site exception for ${currentURI.spec}.`
    276    );
    277  } else if (expectedSwitchState === "off") {
    278    Assert.equal(
    279      el.section.dataset.state,
    280      "site-disabled",
    281      "CBH switch is set to OFF"
    282    );
    283 
    284    ok(
    285      !BrowserTestUtils.isVisible(el.labelON),
    286      "ON label should not be visible"
    287    );
    288    ok(BrowserTestUtils.isVisible(el.labelOFF), "OFF label should be visible");
    289    ok(
    290      !BrowserTestUtils.isVisible(el.labelUNDETECTED),
    291      "UNDETECTED label should not be visible"
    292    );
    293 
    294    is(
    295      pref,
    296      MODE_DISABLED,
    297      `There should be a per-site exception for ${currentURI.spec}.`
    298    );
    299  } else {
    300    Assert.equal(
    301      el.section.dataset.state,
    302      "undetected",
    303      "CBH not supported for site"
    304    );
    305 
    306    ok(
    307      !BrowserTestUtils.isVisible(el.labelON),
    308      "ON label should not be visible"
    309    );
    310    ok(
    311      !BrowserTestUtils.isVisible(el.labelOFF),
    312      "OFF label should not be visible"
    313    );
    314    ok(
    315      BrowserTestUtils.isVisible(el.labelUNDETECTED),
    316      "UNDETECTED label should be visible"
    317    );
    318  }
    319 }
    320 
    321 /**
    322 * Test the telemetry associated with the cookie banner toggle. To be called
    323 * after interacting with the toggle.
    324 *
    325 * @param {*} options
    326 * @param {boolean|null} - Expected telemetry state matching the button state.
    327 * button on = true = cookieb_toggle_on event. Pass null to expect no event
    328 * recorded.
    329 */
    330 function assertTelemetryState({ expectEnabled = null } = {}) {
    331  info("Test telemetry state.");
    332 
    333  let events = [];
    334  const CATEGORY = "security.ui.protectionspopup";
    335  const METHOD = "click";
    336 
    337  if (expectEnabled != null) {
    338    events.push({
    339      category: CATEGORY,
    340      method: METHOD,
    341      object: expectEnabled ? "cookieb_toggle_on" : "cookieb_toggle_off",
    342    });
    343  }
    344 
    345  // Assert event state and clear event list.
    346  TelemetryTestUtils.assertEvents(events, {
    347    category: CATEGORY,
    348    method: METHOD,
    349  });
    350 }
    351 
    352 /**
    353 * Test the cookie banner enable / disable by clicking the switch, then
    354 * clicking the on/off button in the cookie banner subview. Assumes the
    355 * protections panel is already open.
    356 *
    357 * @param {boolean} enable - Whether we want to enable or disable.
    358 * @param {Window} win - Current chrome window under test.
    359 */
    360 async function toggleCookieBannerHandling(enable, win) {
    361  let switchEl = win.document.getElementById(
    362    "protections-popup-cookie-banner-switch"
    363  );
    364  let enableButton = win.document.getElementById(
    365    "protections-popup-cookieBannerView-enable-button"
    366  );
    367  let disableButton = win.document.getElementById(
    368    "protections-popup-cookieBannerView-disable-button"
    369  );
    370  let subView = win.document.getElementById(
    371    "protections-popup-cookieBannerView"
    372  );
    373 
    374  let subViewShownPromise = BrowserTestUtils.waitForEvent(subView, "ViewShown");
    375  switchEl.click();
    376  await subViewShownPromise;
    377 
    378  if (enable) {
    379    ok(BrowserTestUtils.isVisible(enableButton), "Enable button is visible");
    380    enableButton.click();
    381  } else {
    382    ok(BrowserTestUtils.isVisible(disableButton), "Disable button is visible");
    383    disableButton.click();
    384  }
    385 }
    386 
    387 function waitForProtectionsPopupHide(win = window) {
    388  return BrowserTestUtils.waitForEvent(
    389    win.document.getElementById("protections-popup"),
    390    "popuphidden"
    391  );
    392 }
    393 
    394 /**
    395 * Tests the cookie banner section per-site preference toggle.
    396 */
    397 add_task(async function test_section_toggle() {
    398  requestLongerTimeout(3);
    399 
    400  // initialize the pref environment
    401  await SpecialPowers.pushPrefEnv({
    402    set: [
    403      ["cookiebanners.service.mode", MODE_REJECT_OR_ACCEPT],
    404      ["cookiebanners.service.mode.privateBrowsing", MODE_REJECT_OR_ACCEPT],
    405      ["cookiebanners.ui.desktop.enabled", true],
    406      ["cookiebanners.listService.testRules", exampleRules],
    407      ["cookiebanners.listService.testSkipRemoteSettings", true],
    408    ],
    409  });
    410 
    411  Services.cookieBanners.resetRules(false);
    412  await BrowserTestUtils.waitForCondition(
    413    () => !!Services.cookieBanners.rules.length,
    414    "waiting for Services.cookieBanners.rules.length to be greater than 0"
    415  );
    416 
    417  // Test both normal and private browsing windows. For normal windows we reuse
    418  // the existing one, for private windows we need to open a new window.
    419  for (let testPBM of [false, true]) {
    420    let win = window;
    421    if (testPBM) {
    422      win = await BrowserTestUtils.openNewBrowserWindow({
    423        private: true,
    424      });
    425    }
    426 
    427    await BrowserTestUtils.withNewTab(
    428      { gBrowser: win.gBrowser, url: "https://example.com" },
    429      async () => {
    430        let clearSiteDataSpy = sinon.spy(window.SiteDataManager, "remove");
    431 
    432        await openProtectionsPanel(null, win);
    433        let switchEl = win.document.getElementById(
    434          "protections-popup-cookie-banner-switch"
    435        );
    436        info("Testing initial switch ON state.");
    437        assertSwitchAndPrefState({
    438          win,
    439          isPBM: testPBM,
    440          switchEl,
    441          expectedSwitchState: "on",
    442        });
    443        assertTelemetryState();
    444 
    445        info("Testing switch state after toggle OFF");
    446        let closePromise = waitForProtectionsPopupHide(win);
    447        await toggleCookieBannerHandling(false, win);
    448        await closePromise;
    449        if (testPBM) {
    450          Assert.ok(
    451            clearSiteDataSpy.notCalled,
    452            "clearSiteData should not be called in private browsing mode"
    453          );
    454        } else {
    455          Assert.ok(
    456            clearSiteDataSpy.calledOnce,
    457            "clearSiteData should be called in regular browsing mode"
    458          );
    459        }
    460        clearSiteDataSpy.restore();
    461 
    462        await openProtectionsPanel(null, win);
    463        assertSwitchAndPrefState({
    464          win,
    465          isPBM: testPBM,
    466          switchEl,
    467          expectedSwitchState: "off",
    468        });
    469        assertTelemetryState({ expectEnabled: false });
    470 
    471        info("Testing switch state after toggle ON.");
    472        closePromise = waitForProtectionsPopupHide(win);
    473        await toggleCookieBannerHandling(true, win);
    474        await closePromise;
    475 
    476        await openProtectionsPanel(null, win);
    477        assertSwitchAndPrefState({
    478          win,
    479          isPBM: testPBM,
    480          switchEl,
    481          expectedSwitchState: "on",
    482        });
    483        assertTelemetryState({ expectEnabled: true });
    484      }
    485    );
    486 
    487    if (testPBM) {
    488      await BrowserTestUtils.closeWindow(win);
    489    }
    490  }
    491 
    492  await SpecialPowers.popPrefEnv();
    493 });