tor-browser

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

monitor-card.mjs (14621B)


      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 const MONITOR_URL = RPMGetStringPref(
      6  "browser.contentblocking.report.monitor.url",
      7  ""
      8 );
      9 const MONITOR_SIGN_IN_URL = RPMGetStringPref(
     10  "browser.contentblocking.report.monitor.sign_in_url",
     11  ""
     12 );
     13 const HOW_IT_WORKS_URL_PREF = RPMGetFormatURLPref(
     14  "browser.contentblocking.report.monitor.how_it_works.url"
     15 );
     16 const MONITOR_PREFERENCES_URL = RPMGetFormatURLPref(
     17  "browser.contentblocking.report.monitor.preferences_url"
     18 );
     19 const MONITOR_HOME_PAGE_URL = RPMGetFormatURLPref(
     20  "browser.contentblocking.report.monitor.home_page_url"
     21 );
     22 
     23 export default class MonitorClass {
     24  constructor(doc) {
     25    this.doc = doc;
     26  }
     27 
     28  init() {
     29    // Wait for monitor data and display the card.
     30    this.getMonitorData();
     31 
     32    let monitorAboutLink = this.doc.getElementById("monitor-link");
     33    monitorAboutLink.addEventListener("click", () => {
     34      this.doc.sendTelemetryEvent("clickMtrAboutLink");
     35    });
     36 
     37    const storedEmailLink = this.doc.getElementById(
     38      "monitor-stored-emails-link"
     39    );
     40    storedEmailLink.href = MONITOR_PREFERENCES_URL;
     41    storedEmailLink.addEventListener(
     42      "click",
     43      this.onClickMonitorButton.bind(this)
     44    );
     45 
     46    const knownBreachesLink = this.doc.getElementById(
     47      "monitor-known-breaches-link"
     48    );
     49    knownBreachesLink.href = MONITOR_HOME_PAGE_URL;
     50    knownBreachesLink.addEventListener(
     51      "click",
     52      this.onClickMonitorButton.bind(this)
     53    );
     54 
     55    const exposedPasswordsLink = this.doc.getElementById(
     56      "monitor-exposed-passwords-link"
     57    );
     58    exposedPasswordsLink.href = MONITOR_HOME_PAGE_URL;
     59    exposedPasswordsLink.addEventListener(
     60      "click",
     61      this.onClickMonitorButton.bind(this)
     62    );
     63  }
     64 
     65  onClickMonitorButton(evt) {
     66    RPMSendAsyncMessage("ClearMonitorCache");
     67    switch (evt.currentTarget.id) {
     68      case "monitor-partial-breaches-link":
     69        this.doc.sendTelemetryEvent("clickMtrReportLink", "resolve_breaches");
     70        break;
     71      case "monitor-breaches-link":
     72        if (evt.currentTarget.classList.contains("no-breaches-resolved")) {
     73          this.doc.sendTelemetryEvent("clickMtrReportLink", "manage_breaches");
     74        } else {
     75          this.doc.sendTelemetryEvent("clickMtrReportLink", "view_report");
     76        }
     77        break;
     78      case "monitor-stored-emails-link":
     79        this.doc.sendTelemetryEvent("clickMtrReportLink", "stored_emails");
     80        break;
     81      case "monitor-known-breaches-link": {
     82        const knownBreaches = this.doc.querySelector(
     83          "span[data-type='known-breaches']"
     84        );
     85        if (knownBreaches.classList.contains("known-resolved-breaches")) {
     86          this.doc.sendTelemetryEvent(
     87            "clickMtrReportLink",
     88            "known_resolved_breaches"
     89          );
     90        } else if (
     91          knownBreaches.classList.contains("known-unresolved-breaches")
     92        ) {
     93          this.doc.sendTelemetryEvent(
     94            "clickMtrReportLink",
     95            "known_unresolved_breaches"
     96          );
     97        }
     98        break;
     99      }
    100      case "monitor-exposed-passwords-link": {
    101        const exposedPasswords = this.doc.querySelector(
    102          "span[data-type='exposed-passwords']"
    103        );
    104        if (
    105          exposedPasswords.classList.contains("passwords-exposed-all-breaches")
    106        ) {
    107          this.doc.sendTelemetryEvent(
    108            "clickMtrReportLink",
    109            "exposed_passwords_all_breaches"
    110          );
    111        } else if (
    112          exposedPasswords.classList.contains(
    113            "passwords-exposed-unresolved-breaches"
    114          )
    115        ) {
    116          this.doc.sendTelemetryEvent(
    117            "clickMtrReportLink",
    118            "exposed_passwords_unresolved_breaches"
    119          );
    120        }
    121        break;
    122      }
    123    }
    124  }
    125 
    126  /**
    127   * Retrieves the monitor data and displays this data in the card.
    128   */
    129  getMonitorData() {
    130    RPMSendQuery("FetchMonitorData", {}).then(monitorData => {
    131      // Once data for the user is retrieved, display the monitor card.
    132      this.buildContent(monitorData);
    133 
    134      // Show the Monitor card.
    135      const monitorUI = this.doc.querySelector(".card.monitor-card.loading");
    136      monitorUI.classList.remove("loading");
    137    });
    138  }
    139 
    140  buildContent(monitorData) {
    141    const headerContent = this.doc.querySelector(
    142      "#monitor-header-content span"
    143    );
    144    const monitorCard = this.doc.querySelector(".card.monitor-card");
    145    if (!monitorData.error) {
    146      monitorCard.classList.add("has-logins");
    147      this.doc.l10n.setAttributes(
    148        headerContent,
    149        "monitor-header-content-signed-in"
    150      );
    151      this.renderContentForUserWithAccount(monitorData);
    152    } else {
    153      monitorCard.classList.add("no-logins");
    154      const signUpForMonitorLink = this.doc.getElementById(
    155        "sign-up-for-monitor-link"
    156      );
    157      signUpForMonitorLink.href = this.buildMonitorUrl(monitorData.userEmail);
    158      this.doc.l10n.setAttributes(signUpForMonitorLink, "monitor-sign-up-link");
    159      this.doc.l10n.setAttributes(
    160        headerContent,
    161        "monitor-header-content-no-account"
    162      );
    163      signUpForMonitorLink.addEventListener("click", () => {
    164        this.doc.sendTelemetryEvent("clickMtrSignupButton");
    165      });
    166    }
    167  }
    168 
    169  /**
    170   * Builds the appropriate URL that takes the user to the Monitor website's
    171   * sign-up/sign-in page.
    172   *
    173   * @param {string | null} email
    174   *        Optional. The email used to direct the user to the Monitor website's OAuth
    175   *        sign-in flow. If null, then direct user to just the Monitor website.
    176   *
    177   * @returns {string} URL to Monitor website.
    178   */
    179  buildMonitorUrl(email = null) {
    180    return email
    181      ? `${MONITOR_SIGN_IN_URL}${encodeURIComponent(email)}`
    182      : MONITOR_URL;
    183  }
    184 
    185  renderContentForUserWithAccount(monitorData) {
    186    const {
    187      numBreaches,
    188      numBreachesResolved,
    189      passwords,
    190      passwordsResolved,
    191      monitoredEmails,
    192    } = monitorData;
    193    const monitorCardBody = this.doc.querySelector(
    194      ".card.monitor-card .card-body"
    195    );
    196    monitorCardBody.classList.remove("hidden");
    197 
    198    const howItWorksLink = this.doc.getElementById("monitor-link");
    199    howItWorksLink.href = HOW_IT_WORKS_URL_PREF;
    200 
    201    const storedEmail = this.doc.querySelector(
    202      "span[data-type='stored-emails']"
    203    );
    204    storedEmail.textContent = monitoredEmails;
    205    const infoMonitoredAddresses = this.doc.getElementById(
    206      "info-monitored-addresses"
    207    );
    208    this.doc.l10n.setAttributes(
    209      infoMonitoredAddresses,
    210      "info-monitored-emails",
    211      { count: monitoredEmails }
    212    );
    213 
    214    const knownBreaches = this.doc.querySelector(
    215      "span[data-type='known-breaches']"
    216    );
    217    const exposedPasswords = this.doc.querySelector(
    218      "span[data-type='exposed-passwords']"
    219    );
    220 
    221    const infoKnownBreaches = this.doc.getElementById("info-known-breaches");
    222    const infoExposedPasswords = this.doc.getElementById(
    223      "info-exposed-passwords"
    224    );
    225 
    226    const breachesWrapper = this.doc.querySelector(".monitor-breaches-wrapper");
    227    const partialBreachesWrapper = this.doc.querySelector(
    228      ".monitor-partial-breaches-wrapper"
    229    );
    230    const breachesTitle = this.doc.getElementById("monitor-breaches-title");
    231    const breachesIcon = this.doc.getElementById("monitor-breaches-icon");
    232    const breachesDesc = this.doc.getElementById(
    233      "monitor-breaches-description"
    234    );
    235    const breachesLink = this.doc.getElementById("monitor-breaches-link");
    236    if (numBreaches) {
    237      if (!numBreachesResolved) {
    238        partialBreachesWrapper.classList.add("hidden");
    239        knownBreaches.textContent = numBreaches;
    240        knownBreaches.classList.add("known-unresolved-breaches");
    241        knownBreaches.classList.remove("known-resolved-breaches");
    242        this.doc.l10n.setAttributes(
    243          infoKnownBreaches,
    244          "info-known-breaches-found",
    245          { count: numBreaches }
    246        );
    247        exposedPasswords.textContent = passwords;
    248        exposedPasswords.classList.add("passwords-exposed-all-breaches");
    249        exposedPasswords.classList.remove(
    250          "passwords-exposed-unresolved-breaches"
    251        );
    252        this.doc.l10n.setAttributes(
    253          infoExposedPasswords,
    254          "info-exposed-passwords-found",
    255          { count: passwords }
    256        );
    257 
    258        breachesIcon.setAttribute(
    259          "src",
    260          "chrome://browser/skin/protections/new-feature.svg"
    261        );
    262        this.doc.l10n.setAttributes(
    263          breachesTitle,
    264          "monitor-breaches-unresolved-title"
    265        );
    266        this.doc.l10n.setAttributes(
    267          breachesDesc,
    268          "monitor-breaches-unresolved-description"
    269        );
    270        this.doc.l10n.setAttributes(
    271          breachesLink,
    272          "monitor-manage-breaches-link"
    273        );
    274        breachesLink.classList.add("no-breaches-resolved");
    275      } else if (numBreaches == numBreachesResolved) {
    276        partialBreachesWrapper.classList.add("hidden");
    277        knownBreaches.textContent = numBreachesResolved;
    278        knownBreaches.classList.remove("known-unresolved-breaches");
    279        knownBreaches.classList.add("known-resolved-breaches");
    280        this.doc.l10n.setAttributes(
    281          infoKnownBreaches,
    282          "info-known-breaches-resolved",
    283          { count: numBreachesResolved }
    284        );
    285        let unresolvedPasswords = passwords - passwordsResolved;
    286        exposedPasswords.textContent = unresolvedPasswords;
    287        exposedPasswords.classList.remove("passwords-exposed-all-breaches");
    288        exposedPasswords.classList.add("passwords-exposed-unresolved-breaches");
    289        this.doc.l10n.setAttributes(
    290          infoExposedPasswords,
    291          "info-exposed-passwords-resolved",
    292          { count: unresolvedPasswords }
    293        );
    294 
    295        breachesIcon.setAttribute(
    296          "src",
    297          "chrome://browser/skin/protections/resolved-breach.svg"
    298        );
    299        this.doc.l10n.setAttributes(
    300          breachesTitle,
    301          "monitor-breaches-resolved-title"
    302        );
    303        this.doc.l10n.setAttributes(
    304          breachesDesc,
    305          "monitor-breaches-resolved-description"
    306        );
    307        this.doc.l10n.setAttributes(breachesLink, "monitor-view-report-link");
    308      } else {
    309        breachesWrapper.classList.add("hidden");
    310        knownBreaches.textContent = numBreachesResolved;
    311        knownBreaches.classList.remove("known-unresolved-breaches");
    312        knownBreaches.classList.add("known-resolved-breaches");
    313        this.doc.l10n.setAttributes(
    314          infoKnownBreaches,
    315          "info-known-breaches-resolved",
    316          { count: numBreachesResolved }
    317        );
    318        let unresolvedPasswords = passwords - passwordsResolved;
    319        exposedPasswords.textContent = unresolvedPasswords;
    320        exposedPasswords.classList.remove("passwords-exposed-all-breaches");
    321        exposedPasswords.classList.add("passwords-exposed-unresolved-breaches");
    322        this.doc.l10n.setAttributes(
    323          infoExposedPasswords,
    324          "info-exposed-passwords-resolved",
    325          { count: unresolvedPasswords }
    326        );
    327 
    328        const partialBreachesTitle = document.getElementById(
    329          "monitor-partial-breaches-title"
    330        );
    331        this.doc.l10n.setAttributes(
    332          partialBreachesTitle,
    333          "monitor-partial-breaches-title",
    334          {
    335            numBreaches,
    336            numBreachesResolved,
    337          }
    338        );
    339 
    340        const progressBar = this.doc.querySelector(".progress-bar");
    341        const partialBreachesMotivationTitle = document.getElementById(
    342          "monitor-partial-breaches-motivation-title"
    343        );
    344 
    345        let percentageResolved = Math.floor(
    346          (numBreachesResolved / numBreaches) * 100
    347        );
    348        progressBar.setAttribute("value", 100 - percentageResolved);
    349        switch (true) {
    350          case percentageResolved > 0 && percentageResolved < 25:
    351            this.doc.l10n.setAttributes(
    352              partialBreachesMotivationTitle,
    353              "monitor-partial-breaches-motivation-title-start"
    354            );
    355            break;
    356 
    357          case percentageResolved >= 25 && percentageResolved < 75:
    358            this.doc.l10n.setAttributes(
    359              partialBreachesMotivationTitle,
    360              "monitor-partial-breaches-motivation-title-middle"
    361            );
    362            break;
    363 
    364          case percentageResolved >= 75 && percentageResolved < 100:
    365            this.doc.l10n.setAttributes(
    366              partialBreachesMotivationTitle,
    367              "monitor-partial-breaches-motivation-title-end"
    368            );
    369            break;
    370        }
    371 
    372        const partialBreachesPercentage = document.getElementById(
    373          "monitor-partial-breaches-percentage"
    374        );
    375        this.doc.l10n.setAttributes(
    376          partialBreachesPercentage,
    377          "monitor-partial-breaches-percentage",
    378          { percentageResolved }
    379        );
    380 
    381        const partialBreachesLink = document.getElementById(
    382          "monitor-partial-breaches-link"
    383        );
    384        partialBreachesLink.setAttribute("href", MONITOR_HOME_PAGE_URL);
    385        partialBreachesLink.addEventListener(
    386          "click",
    387          this.onClickMonitorButton.bind(this)
    388        );
    389      }
    390    } else {
    391      partialBreachesWrapper.classList.add("hidden");
    392      knownBreaches.textContent = numBreaches;
    393      knownBreaches.classList.add("known-unresolved-breaches");
    394      knownBreaches.classList.remove("known-resolved-breaches");
    395      this.doc.l10n.setAttributes(
    396        infoKnownBreaches,
    397        "info-known-breaches-found",
    398        { count: numBreaches }
    399      );
    400      exposedPasswords.textContent = passwords;
    401      exposedPasswords.classList.add("passwords-exposed-all-breaches");
    402      exposedPasswords.classList.remove(
    403        "passwords-exposed-unresolved-breaches"
    404      );
    405      this.doc.l10n.setAttributes(
    406        infoExposedPasswords,
    407        "info-exposed-passwords-found",
    408        { count: passwords }
    409      );
    410 
    411      breachesIcon.setAttribute(
    412        "src",
    413        "chrome://browser/skin/protections/resolved-breach.svg"
    414      );
    415      this.doc.l10n.setAttributes(breachesTitle, "monitor-no-breaches-title");
    416      this.doc.l10n.setAttributes(
    417        breachesDesc,
    418        "monitor-no-breaches-description"
    419      );
    420      this.doc.l10n.setAttributes(breachesLink, "monitor-view-report-link");
    421    }
    422 
    423    breachesLink.setAttribute("href", MONITOR_HOME_PAGE_URL);
    424    breachesLink.addEventListener(
    425      "click",
    426      this.onClickMonitorButton.bind(this)
    427    );
    428  }
    429 }