tor-browser

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

aboutLogins.mjs (9875B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import {
      6  recordTelemetryEvent,
      7  setKeyboardAccessForNonDialogElements,
      8 } from "./aboutLoginsUtils.mjs";
      9 
     10 // The init code isn't wrapped in a DOMContentLoaded/load event listener so the
     11 // page works properly when restored from session restore.
     12 const gElements = {
     13  fxAccountsButton: document.querySelector("fxaccounts-button"),
     14  loginList: document.querySelector("login-list"),
     15  loginIntro: document.querySelector("login-intro"),
     16  loginItem: document.querySelector("login-item"),
     17  loginFilter: document
     18    .querySelector("login-list")
     19    .shadowRoot.querySelector("login-filter"),
     20  menuButton: document.querySelector("menu-button"),
     21  get exportButton() {
     22    return this.menuButton.shadowRoot.querySelector(".menuitem-export");
     23  },
     24  // removeAllLogins button is nested inside of menuButton
     25  get removeAllButton() {
     26    return this.menuButton.shadowRoot.querySelector(
     27      ".menuitem-remove-all-logins"
     28    );
     29  },
     30 };
     31 
     32 let numberOfLogins = 0;
     33 
     34 function updateNoLogins() {
     35  document.documentElement.classList.toggle("no-logins", numberOfLogins == 0);
     36  gElements.loginList.classList.toggle("no-logins", numberOfLogins == 0);
     37  gElements.loginItem.classList.toggle("no-logins", numberOfLogins == 0);
     38  gElements.exportButton.disabled = numberOfLogins == 0;
     39  gElements.removeAllButton.disabled = numberOfLogins == 0;
     40 }
     41 
     42 function handleAllLogins(logins) {
     43  gElements.loginList.setLogins(logins);
     44  numberOfLogins = logins.length;
     45  updateNoLogins();
     46 }
     47 
     48 let fxaLoggedIn = null;
     49 let passwordSyncEnabled = null;
     50 
     51 function handleSyncState(syncState) {
     52  gElements.fxAccountsButton.updateState(syncState);
     53  gElements.loginIntro.updateState(syncState);
     54  fxaLoggedIn = syncState.loggedIn;
     55  passwordSyncEnabled = syncState.passwordSyncEnabled;
     56 }
     57 
     58 window.addEventListener("AboutLoginsChromeToContent", event => {
     59  switch (event.detail.messageType) {
     60    case "AllLogins": {
     61      document.documentElement.classList.remove(
     62        "primary-password-auth-required"
     63      );
     64      setKeyboardAccessForNonDialogElements(true);
     65      handleAllLogins(event.detail.value);
     66      break;
     67    }
     68    case "ImportPasswordsDialog": {
     69      let dialog = document.querySelector("import-summary-dialog");
     70      let options = {
     71        logins: event.detail.value,
     72      };
     73      dialog.show(options);
     74      break;
     75    }
     76    case "ImportPasswordsErrorDialog": {
     77      let dialog = document.querySelector("import-error-dialog");
     78      dialog.show(event.detail.value);
     79      break;
     80    }
     81    case "LoginAdded": {
     82      gElements.loginList.loginAdded(event.detail.value);
     83      gElements.loginItem.loginAdded(event.detail.value);
     84      numberOfLogins++;
     85      updateNoLogins();
     86      break;
     87    }
     88    case "LoginModified": {
     89      gElements.loginList.loginModified(event.detail.value);
     90      gElements.loginItem.loginModified(event.detail.value);
     91      break;
     92    }
     93    case "LoginRemoved": {
     94      // The loginRemoved function of loginItem needs to be called before
     95      // the one in loginList since it will remove the editing. So that the
     96      // discard dialog won't show up if we delete a login after edit it.
     97      gElements.loginItem.loginRemoved(event.detail.value);
     98      gElements.loginList.loginRemoved(event.detail.value);
     99      numberOfLogins--;
    100      updateNoLogins();
    101      break;
    102    }
    103    case "PrimaryPasswordAuthRequired": {
    104      document.documentElement.classList.add("primary-password-auth-required");
    105      setKeyboardAccessForNonDialogElements(false);
    106      break;
    107    }
    108    case "RemaskPassword": {
    109      window.dispatchEvent(new CustomEvent("AboutLoginsRemaskPassword"));
    110      break;
    111    }
    112    case "RemoveAllLogins": {
    113      handleAllLogins(event.detail.value);
    114      document.documentElement.classList.remove("login-selected");
    115      break;
    116    }
    117    case "SetBreaches": {
    118      gElements.loginList.setBreaches(event.detail.value);
    119      gElements.loginItem.setBreaches(event.detail.value);
    120      break;
    121    }
    122    case "SetVulnerableLogins": {
    123      gElements.loginList.setVulnerableLogins(event.detail.value);
    124      gElements.loginItem.setVulnerableLogins(event.detail.value);
    125      break;
    126    }
    127    case "Setup": {
    128      gElements.loginList.selectLoginByDomainOrGuid(
    129        event.detail.value.preselectedLogin
    130      );
    131      handleAllLogins(event.detail.value.logins);
    132      handleSyncState(event.detail.value.syncState);
    133      gElements.loginList.setSortDirection(event.detail.value.selectedSort);
    134      document.documentElement.classList.add("initialized");
    135      gElements.loginList.classList.add("initialized");
    136      gElements.loginList.canCreateLogins = event.detail.value.canCreateLogins;
    137      break;
    138    }
    139    case "ShowLoginItemError": {
    140      gElements.loginItem.showLoginItemError(event.detail.value);
    141      break;
    142    }
    143    case "SyncState": {
    144      handleSyncState(event.detail.value);
    145      break;
    146    }
    147    case "UpdateBreaches": {
    148      gElements.loginList.updateBreaches(event.detail.value);
    149      gElements.loginItem.updateBreaches(event.detail.value);
    150      break;
    151    }
    152    case "UpdateVulnerableLogins": {
    153      gElements.loginList.updateVulnerableLogins(event.detail.value);
    154      gElements.loginItem.updateVulnerableLogins(event.detail.value);
    155      break;
    156    }
    157  }
    158 });
    159 
    160 window.addEventListener("AboutLoginsRemoveAllLoginsDialog", () => {
    161  let loginItem = document.querySelector("login-item");
    162  let options = {};
    163  if (fxaLoggedIn && passwordSyncEnabled) {
    164    options.title = "about-logins-confirm-remove-all-sync-dialog-title2";
    165    options.message = "about-logins-confirm-remove-all-sync-dialog-message3";
    166  } else {
    167    options.title = "about-logins-confirm-remove-all-dialog-title2";
    168    options.message = "about-logins-confirm-remove-all-dialog-message2";
    169  }
    170  options.confirmCheckboxLabel =
    171    "about-logins-confirm-remove-all-dialog-checkbox-label2";
    172  options.confirmButtonLabel =
    173    "about-logins-confirm-remove-all-dialog-confirm-button-label";
    174  options.count = numberOfLogins;
    175 
    176  let dialog = document.querySelector("remove-logins-dialog");
    177  let dialogPromise = dialog.show(options);
    178  try {
    179    dialogPromise.then(
    180      () => {
    181        if (loginItem.dataset.isNewLogin) {
    182          // Bug 1681042 - Resetting the form prevents a double confirmation dialog since there
    183          // may be pending changes in the new login.
    184          loginItem.resetForm();
    185          window.dispatchEvent(new CustomEvent("AboutLoginsClearSelection"));
    186        } else if (loginItem.dataset.editing) {
    187          loginItem._toggleEditing();
    188        }
    189        window.document.documentElement.classList.remove("login-selected");
    190        let removeAllEvt = new CustomEvent("AboutLoginsRemoveAllLogins", {
    191          bubbles: true,
    192        });
    193        window.dispatchEvent(removeAllEvt);
    194      },
    195      () => {}
    196    );
    197  } catch (e) {
    198    if (e != undefined) {
    199      throw e;
    200    }
    201  }
    202 });
    203 
    204 window.addEventListener("AboutLoginsExportPasswordsDialog", async () => {
    205  recordTelemetryEvent({
    206    name: "mgmtMenuItemUsedExport",
    207  });
    208  let dialog = document.querySelector("confirmation-dialog");
    209  let options = {
    210    title: "about-logins-confirm-export-dialog-title2",
    211    message: "about-logins-confirm-export-dialog-message2",
    212    confirmButtonLabel: "about-logins-confirm-export-dialog-confirm-button2",
    213  };
    214  try {
    215    await dialog.show(options);
    216    document.dispatchEvent(
    217      new CustomEvent("AboutLoginsExportPasswords", { bubbles: true })
    218    );
    219  } catch (ex) {
    220    // The user cancelled the dialog.
    221  }
    222 });
    223 
    224 async function interceptFocusKey() {
    225  // Intercept Ctrl+F on the page to focus login filter box
    226  const [findKey] = await document.l10n.formatMessages([
    227    { id: "about-logins-login-filter2" },
    228  ]);
    229  const focusKey = findKey.attributes
    230    .find(a => a.name == "key")
    231    .value.toLowerCase();
    232  document.addEventListener("keydown", event => {
    233    if (event.key == focusKey && event.getModifierState("Accel")) {
    234      event.preventDefault();
    235      document
    236        .querySelector("login-list")
    237        .shadowRoot.querySelector("login-filter")
    238        .shadowRoot.querySelector("input")
    239        .focus();
    240    }
    241  });
    242 }
    243 
    244 await interceptFocusKey();
    245 
    246 // Begin code that executes on page load.
    247 
    248 let searchParamsChanged = false;
    249 let { protocol, pathname, searchParams } = new URL(document.location);
    250 
    251 recordTelemetryEvent({
    252  name: "openManagement" + (searchParams.get("entryPoint") || "Direct"),
    253 });
    254 
    255 if (searchParams.has("entryPoint")) {
    256  // Remove this parameter from the URL (after recording above) to make it
    257  // cleaner for bookmarking and switch-to-tab and so that bookmarked values
    258  // don't skew telemetry.
    259  searchParams.delete("entryPoint");
    260  searchParamsChanged = true;
    261 }
    262 
    263 if (searchParams.has("filter")) {
    264  let filter = searchParams.get("filter");
    265  if (!filter) {
    266    // Remove empty `filter` params to give a cleaner URL for bookmarking and
    267    // switch-to-tab
    268    searchParams.delete("filter");
    269    searchParamsChanged = true;
    270  }
    271 }
    272 
    273 if (searchParamsChanged) {
    274  const paramsPart = searchParams.toString() ? `?${searchParams}` : "";
    275  const newURL = protocol + pathname + paramsPart + document.location.hash;
    276  // This redirect doesn't stop this script from running so ensure you guard
    277  // later code if it shouldn't run before and after the redirect.
    278  window.location.replace(newURL);
    279 } else if (searchParams.has("filter")) {
    280  // This must be after the `location.replace` so it doesn't cause telemetry to
    281  // record a filter event before the navigation to clean the URL.
    282  gElements.loginFilter.value = searchParams.get("filter");
    283 }
    284 
    285 if (!searchParamsChanged) {
    286  gElements.loginFilter.focus();
    287  document.dispatchEvent(new CustomEvent("AboutLoginsInit", { bubbles: true }));
    288 }