tor-browser

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

browser_https_only_exceptions.js (11244B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 /**
      5 * First Test
      6 * Checks if buttons are disabled/enabled and visible/hidden correctly.
      7 */
      8 add_task(async function testButtons() {
      9  // Let's make sure HTTPS-Only and HTTPS-First Mode is off.
     10  await setHttpsOnlyPref("off");
     11  await setHttpsFirstPref("off");
     12 
     13  // Open the privacy-pane in about:preferences
     14  await openPreferencesViaOpenPreferencesAPI("panePrivacy", {
     15    leaveOpen: true,
     16  });
     17 
     18  // Get button-element to open the exceptions-dialog
     19  const exceptionButton = gBrowser.contentDocument.getElementById(
     20    "httpsOnlyExceptionButton"
     21  );
     22 
     23  is(
     24    exceptionButton.disabled,
     25    true,
     26    "HTTPS-Only exception button should be disabled when HTTPS-Only Mode is disabled."
     27  );
     28 
     29  await setHttpsOnlyPref("private");
     30  is(
     31    exceptionButton.disabled,
     32    false,
     33    "HTTPS-Only exception button should be enabled when HTTPS-Only Mode is only enabled in private browsing."
     34  );
     35 
     36  await setHttpsOnlyPref("everywhere");
     37  is(
     38    exceptionButton.disabled,
     39    false,
     40    "HTTPS-Only exception button should be enabled when HTTPS-Only Mode enabled everywhere."
     41  );
     42 
     43  await setHttpsOnlyPref("off");
     44  is(
     45    exceptionButton.disabled,
     46    true,
     47    "Turning off HTTPS-Only should disable the exception button again."
     48  );
     49 
     50  await setHttpsFirstPref("private");
     51  is(
     52    exceptionButton.disabled,
     53    false,
     54    "HTTPS-Only exception button should be enabled when HTTPS-Only Mode is disabled and HTTPS-First Mode is only enabled in private browsing."
     55  );
     56 
     57  await setHttpsFirstPref("everywhere");
     58  is(
     59    exceptionButton.disabled,
     60    false,
     61    "HTTPS-Only exception button should be enabled when HTTPS-Only Mode is disabled and HTTPS-First Mode enabled everywhere."
     62  );
     63 
     64  // Now that the button is clickable, we open the dialog
     65  // to check if the correct buttons are visible
     66  let promiseSubDialogLoaded = promiseLoadSubDialog(
     67    "chrome://browser/content/preferences/dialogs/permissions.xhtml"
     68  );
     69  exceptionButton.click();
     70 
     71  let win = await promiseSubDialogLoaded;
     72 
     73  const dialogDoc = win.document;
     74 
     75  is(
     76    dialogDoc.getElementById("btnBlock").hidden,
     77    true,
     78    "Block button should not be visible in HTTPS-Only Dialog."
     79  );
     80  is(
     81    dialogDoc.getElementById("btnCookieSession").hidden,
     82    true,
     83    "Cookie specific allow button should not be visible in HTTPS-Only Dialog."
     84  );
     85  is(
     86    dialogDoc.getElementById("btnAllow").hidden,
     87    true,
     88    "Allow button should not be visible in HTTPS-Only Dialog."
     89  );
     90  is(
     91    dialogDoc.getElementById("btnHttpsOnlyOff").hidden,
     92    false,
     93    "HTTPS-Only off button should be visible in HTTPS-Only Dialog."
     94  );
     95  is(
     96    dialogDoc.getElementById("btnHttpsOnlyOffTmp").hidden,
     97    false,
     98    "HTTPS-Only temporary off button should be visible in HTTPS-Only Dialog."
     99  );
    100 
    101  // Reset prefs and close the tab
    102  await SpecialPowers.flushPrefEnv();
    103  BrowserTestUtils.removeTab(gBrowser.selectedTab);
    104 });
    105 
    106 /**
    107 * Second Test
    108 * Checks permissions are added and removed correctly.
    109 *
    110 * Each test opens a new dialog, performs an action (second argument),
    111 * then closes the dialog and checks if the changes were made (third argument).
    112 */
    113 add_task(async function checkDialogFunctionality() {
    114  // Enable HTTPS-Only Mode for every window, so the exceptions dialog is accessible.
    115  await setHttpsOnlyPref("everywhere");
    116 
    117  // Open the privacy-pane in about:preferences
    118  await openPreferencesViaOpenPreferencesAPI("panePrivacy", {
    119    leaveOpen: true,
    120  });
    121  const preferencesDoc = gBrowser.contentDocument;
    122 
    123  // Test if we can add permanent exceptions
    124  await runTest(
    125    preferencesDoc,
    126    elements => {
    127      assertListContents(elements, []);
    128 
    129      elements.url.value = "test.com";
    130      elements.btnAllow.doCommand();
    131 
    132      assertListContents(elements, [["http://test.com", elements.allowL10nId]]);
    133    },
    134    () => [
    135      {
    136        type: "https-only-load-insecure",
    137        origin: "http://test.com",
    138        data: "added",
    139        capability: Ci.nsIPermissionManager.ALLOW_ACTION,
    140        expireType: Ci.nsIPermissionManager.EXPIRE_NEVER,
    141      },
    142    ]
    143  );
    144 
    145  // Test if items are retained, and if temporary exceptions are added correctly
    146  await runTest(
    147    preferencesDoc,
    148    elements => {
    149      assertListContents(elements, [["http://test.com", elements.allowL10nId]]);
    150 
    151      elements.url.value = "1.1.1.1:8080";
    152      elements.btnAllowSession.doCommand();
    153 
    154      assertListContents(elements, [
    155        ["http://test.com", elements.allowL10nId],
    156        ["http://1.1.1.1:8080", elements.allowSessionL10nId],
    157      ]);
    158    },
    159    () => [
    160      {
    161        type: "https-only-load-insecure",
    162        origin: "http://1.1.1.1:8080",
    163        data: "added",
    164        capability: Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION,
    165        expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
    166      },
    167    ]
    168  );
    169 
    170  // Test if we can remove the permissions one-by-one
    171  await runTest(
    172    preferencesDoc,
    173    elements => {
    174      while (elements.richlistbox.itemCount) {
    175        elements.richlistbox.selectedIndex = 0;
    176        elements.btnRemove.doCommand();
    177      }
    178      assertListContents(elements, []);
    179    },
    180    elements => {
    181      let richlistItems = elements.richlistbox.getElementsByAttribute(
    182        "origin",
    183        "*"
    184      );
    185      let observances = [];
    186      for (let item of richlistItems) {
    187        observances.push({
    188          type: "https-only-load-insecure",
    189          origin: item.getAttribute("origin"),
    190          data: "deleted",
    191        });
    192      }
    193      return observances;
    194    }
    195  );
    196 
    197  // Test if all inputs with an https scheme are added with an http scheme,
    198  // while other schemes are kept as they are. (Bug 1757297)
    199  await runTest(
    200    preferencesDoc,
    201    elements => {
    202      assertListContents(elements, []);
    203 
    204      elements.url.value = "http://test.com";
    205      elements.btnAllow.doCommand();
    206 
    207      assertListContents(elements, [["http://test.com", elements.allowL10nId]]);
    208 
    209      elements.url.value = "https://test.com";
    210      elements.btnAllow.doCommand();
    211 
    212      assertListContents(elements, [["http://test.com", elements.allowL10nId]]);
    213 
    214      elements.url.value = "https://test.org";
    215      elements.btnAllow.doCommand();
    216 
    217      assertListContents(elements, [
    218        ["http://test.com", elements.allowL10nId],
    219        ["http://test.org", elements.allowL10nId],
    220      ]);
    221 
    222      elements.url.value = "moz-extension://test";
    223      elements.btnAllow.doCommand();
    224 
    225      assertListContents(elements, [
    226        ["http://test.com", elements.allowL10nId],
    227        ["http://test.org", elements.allowL10nId],
    228        ["moz-extension://test", elements.allowL10nId],
    229      ]);
    230    },
    231    () => [
    232      {
    233        type: "https-only-load-insecure",
    234        origin: "http://test.com",
    235        data: "added",
    236        capability: Ci.nsIPermissionManager.ALLOW_ACTION,
    237        expireType: Ci.nsIPermissionManager.EXPIRE_NEVER,
    238      },
    239      {
    240        type: "https-only-load-insecure",
    241        origin: "http://test.org",
    242        data: "added",
    243        capability: Ci.nsIPermissionManager.ALLOW_ACTION,
    244        expireType: Ci.nsIPermissionManager.EXPIRE_NEVER,
    245      },
    246      {
    247        type: "https-only-load-insecure",
    248        origin: "moz-extension://test",
    249        data: "added",
    250        capability: Ci.nsIPermissionManager.ALLOW_ACTION,
    251        expireType: Ci.nsIPermissionManager.EXPIRE_NEVER,
    252      },
    253    ]
    254  );
    255 
    256  // Test if we can remove all permissions at once
    257  await runTest(
    258    preferencesDoc,
    259    elements => {
    260      elements.btnRemoveAll.doCommand();
    261      assertListContents(elements, []);
    262    },
    263    elements => {
    264      let richlistItems = elements.richlistbox.getElementsByAttribute(
    265        "origin",
    266        "*"
    267      );
    268      let observances = [];
    269      for (let item of richlistItems) {
    270        observances.push({
    271          type: "https-only-load-insecure",
    272          origin: item.getAttribute("origin"),
    273          data: "deleted",
    274        });
    275      }
    276      return observances;
    277    }
    278  );
    279 
    280  SpecialPowers.clearUserPref("dom.security.https_only_mode_ever_enabled_pbm");
    281  BrowserTestUtils.removeTab(gBrowser.selectedTab);
    282 });
    283 
    284 /**
    285 * Changes HTTPS-Only Mode pref
    286 *
    287 * @param {string} state "everywhere", "private", "off"
    288 */
    289 async function setHttpsOnlyPref(state) {
    290  await SpecialPowers.pushPrefEnv({
    291    set: [
    292      ["dom.security.https_only_mode", state === "everywhere"],
    293      ["dom.security.https_only_mode_pbm", state === "private"],
    294    ],
    295  });
    296 }
    297 
    298 /**
    299 * Changes HTTPS-First Mode pref
    300 *
    301 * @param {string} state "everywhere", "private", "off"
    302 */
    303 async function setHttpsFirstPref(state) {
    304  await SpecialPowers.pushPrefEnv({
    305    set: [
    306      ["dom.security.https_first", state === "everywhere"],
    307      ["dom.security.https_first_pbm", state === "private"],
    308    ],
    309  });
    310 }
    311 
    312 /**
    313 * Opens new exceptions dialog, runs test function
    314 *
    315 * @param {HTMLElement} preferencesDoc document of about:preferences tab
    316 * @param {function} test function to call when dialog is open
    317 * @param {Array} observances permission changes to observe (order is important)
    318 */
    319 async function runTest(preferencesDoc, test, observancesFn) {
    320  // Click on exception-button and wait for dialog to open
    321  let promiseSubDialogLoaded = promiseLoadSubDialog(
    322    "chrome://browser/content/preferences/dialogs/permissions.xhtml"
    323  );
    324  preferencesDoc.getElementById("httpsOnlyExceptionButton").click();
    325 
    326  let win = await promiseSubDialogLoaded;
    327 
    328  // Create a bunch of references to UI-elements for the test-function
    329  const doc = win.document;
    330  let elements = {
    331    richlistbox: doc.getElementById("permissionsBox"),
    332    url: doc.getElementById("url"),
    333    btnAllow: doc.getElementById("btnHttpsOnlyOff"),
    334    btnAllowSession: doc.getElementById("btnHttpsOnlyOffTmp"),
    335    btnRemove: doc.getElementById("removePermission"),
    336    btnRemoveAll: doc.getElementById("removeAllPermissions"),
    337    allowL10nId: win.gPermissionManager._getCapabilityL10nId(
    338      Ci.nsIPermissionManager.ALLOW_ACTION
    339    ),
    340    allowSessionL10nId: win.gPermissionManager._getCapabilityL10nId(
    341      Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION
    342    ),
    343  };
    344 
    345  // Some observances need to be computed based on the current state.
    346  const observances = observancesFn(elements);
    347 
    348  // Run test function
    349  await test(elements);
    350 
    351  // Click on "Save changes" and wait for permission changes.
    352  let btnApplyChanges = doc.querySelector("dialog").getButton("accept");
    353  let observeAllPromise = createObserveAllPromise(observances);
    354 
    355  btnApplyChanges.doCommand();
    356  await observeAllPromise;
    357 }
    358 
    359 function assertListContents(elements, expected) {
    360  is(
    361    elements.richlistbox.itemCount,
    362    expected.length,
    363    "Richlistbox should match the expected amount of exceptions."
    364  );
    365 
    366  for (let i = 0; i < expected.length; i++) {
    367    let website = expected[i][0];
    368    let listItem = elements.richlistbox.getElementsByAttribute(
    369      "origin",
    370      website
    371    );
    372    is(listItem.length, 1, "Each origin should be unique");
    373    is(
    374      listItem[0]
    375        .querySelector(".website-capability-value")
    376        .getAttribute("data-l10n-id"),
    377      expected[i][1],
    378      "List item capability should match expected l10n-id"
    379    );
    380  }
    381 }