tor-browser

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

security-privacy-card.mjs (10732B)


      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 { html } from "chrome://global/content/vendor/lit.all.mjs";
      6 import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
      7 
      8 const lazy = {};
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  AppUpdater: "resource://gre/modules/AppUpdater.sys.mjs",
     11 });
     12 
     13 const L10N_IDS = {
     14  okHeader: "security-privacy-status-ok-header",
     15  problemHeader: "security-privacy-status-problem-header",
     16  okLabel: "security-privacy-status-ok-label",
     17  problemLabel: "security-privacy-status-problem-label",
     18  problemHelperLabel: "security-privacy-status-problem-helper-label",
     19  trackersPendingLabel: "security-privacy-status-pending-trackers-label",
     20  trackersLabel: "security-privacy-status-trackers-label",
     21  strictEnabledLabel: "security-privacy-status-strict-enabled-label",
     22  upToDateLabel: "security-privacy-status-up-to-date-label",
     23  updateNeededLabel: "security-privacy-status-update-needed-label",
     24  updateErrorLabel: "security-privacy-status-update-error-label",
     25  updateCheckingLabel: "security-privacy-status-update-checking-label",
     26  updateNeededDescription: "security-privacy-status-update-needed-description",
     27  updateButtonLabel: "security-privacy-status-update-button-label",
     28 };
     29 
     30 /**
     31 * Custom Element for a card holding configuration issues from the user settings
     32 */
     33 export default class SecurityPrivacyCard extends MozLitElement {
     34  /**
     35   * Private member to check the App Updater status
     36   *
     37   * @returns {boolean} should we NOT warn the user about their app update status
     38   */
     39  #okUpdateStatus() {
     40    const okStatuses = [
     41      lazy.AppUpdater.STATUS.NO_UPDATES_FOUND,
     42      lazy.AppUpdater.STATUS.CHECKING,
     43      lazy.AppUpdater.STATUS.NO_UPDATER,
     44      lazy.AppUpdater.STATUS.UPDATE_DISABLED_BY_POLICY,
     45      lazy.AppUpdater.STATUS.OTHER_INSTANCE_HANDLING_UPDATES,
     46      undefined,
     47    ];
     48 
     49    return okStatuses.includes(this.appUpdateStatus);
     50  }
     51 
     52  get strictEnabled() {
     53    return this.setting.deps.etpStrictEnabled.value;
     54  }
     55 
     56  get trackersBlocked() {
     57    return this.setting.deps.trackerCount.value;
     58  }
     59 
     60  get appUpdateStatus() {
     61    return this.setting.deps.appUpdateStatus.value;
     62  }
     63 
     64  // This should really only be used for testing, as it
     65  // overrides the reported app updater state
     66  set appUpdateStatus(value) {
     67    this.requestUpdate();
     68    this.setting.deps.appUpdateStatus.value = value;
     69  }
     70 
     71  get configIssueCount() {
     72    let filteredWarnings = [
     73      "etpStrictEnabled",
     74      "trackerCount",
     75      "appUpdateStatus",
     76    ];
     77    return Object.values(this.setting.deps).filter(
     78      warning => !filteredWarnings.includes(warning.id) && warning.visible
     79    ).length;
     80  }
     81 
     82  /**
     83   * Scrolling to an element in about:preferences is non-trivial because the fragment is controlled
     84   * by the panel manager. So we need this logic.
     85   *
     86   * @param {string} panelHash - the ID of the panel the element we want to scroll to lives on
     87   * @param {string} targetId - the ID of the element to scroll to
     88   * @returns {Function} a callback that will perform the scroll
     89   */
     90  #scrollToTargetOnPanel(panelHash, targetId) {
     91    return function () {
     92      // This actually scrolls to the target ID, if it exists.
     93      // It looks in the document first, then the shadowRoot for that ID.
     94      const scrollIntoView = () => {
     95        let target = document.getElementById(targetId);
     96        if (!target) {
     97          target = this.shadowRoot.getElementById(targetId);
     98        }
     99        if (target) {
    100          target.scrollIntoView({ behavior: "smooth" });
    101        }
    102      };
    103      if (panelHash !== undefined && document.location.hash != panelHash) {
    104        // If we are given a panel to go to, and we aren't already there,
    105        // switch to that panel and when it is shown, scrollIntoView.
    106        document.addEventListener("paneshown", scrollIntoView, { once: true });
    107        document.location.hash = panelHash;
    108      } else {
    109        // Here we are already on the panel, so we can just scroll straight to it.
    110        scrollIntoView();
    111      }
    112    };
    113  }
    114 
    115  #openWarningCardAndScroll() {
    116    let accordion = document.getElementById("warningCard");
    117    if (!accordion) {
    118      return;
    119    }
    120    accordion.expanded = true;
    121    this.#scrollToTargetOnPanel("#privacy", "warningCard")();
    122  }
    123 
    124  getStatusImage() {
    125    if (this.configIssueCount > 0) {
    126      return html`<img
    127        class="status-image"
    128        src="chrome://global/skin/illustrations/kit-looking-left.svg"
    129        data-l10n-id="security-privacy-image-warning"
    130      />`;
    131    }
    132    return html`<img
    133      class="status-image"
    134      src="chrome://global/skin/illustrations/kit-looking-forward.svg"
    135      data-l10n-id="security-privacy-image-ok"
    136    />`;
    137  }
    138 
    139  /**
    140   * Create the bullet point for the current count of "issues" in the user profile.
    141   * Really only depends on `this.configIssueCount`
    142   *
    143   * @returns {TemplateResult} the HTML for the "issues" bullet of the custom element
    144   */
    145  buildIssuesElement() {
    146    if (this.configIssueCount == 0) {
    147      return html`<li class="status-ok">
    148        <p data-l10n-id=${L10N_IDS.okLabel}></p>
    149      </li>`;
    150    }
    151    return html`<li class="status-alert">
    152      <div>
    153        <p data-l10n-id=${L10N_IDS.problemLabel}></p>
    154        <p>
    155          <small
    156            ><a
    157              href=""
    158              @click=${() => this.#openWarningCardAndScroll()}
    159              data-l10n-id=${L10N_IDS.problemHelperLabel}
    160            ></a
    161          ></small>
    162        </p>
    163      </div>
    164    </li>`;
    165  }
    166 
    167  /**
    168   * Create the bullet point for the current count of trackers blocked in the past week.
    169   * Really only depends on `this.trackersBlocked`and `this.strictEnabled`
    170   *
    171   * @returns {TemplateResult} the HTML for the "trackers" bullet of the custom element
    172   */
    173  buildTrackersElement() {
    174    let trackerData = {
    175      trackerCount: this.trackersBlocked,
    176    };
    177    let trackerLabelElement =
    178      this.trackersBlocked != null
    179        ? html`<p
    180            data-l10n-id=${L10N_IDS.trackersLabel}
    181            data-l10n-args=${JSON.stringify(trackerData)}
    182          ></p>`
    183        : html`<p data-l10n-id=${L10N_IDS.trackersPendingLabel}></p>`;
    184 
    185    if (this.strictEnabled) {
    186      return html`<li class="status-ok">
    187        <div>
    188          ${trackerLabelElement}
    189          <p>
    190            <small
    191              data-l10n-id=${L10N_IDS.strictEnabledLabel}
    192              id="strictEnabled"
    193              @click=${this.#scrollToTargetOnPanel("#privacy", "trackingGroup")}
    194            >
    195              <a data-l10n-name="strict-tracking-protection" href=""></a
    196            ></small>
    197          </p>
    198        </div>
    199      </li>`;
    200    }
    201    return html`<li class="status-ok">${trackerLabelElement}</li>`;
    202  }
    203 
    204  /**
    205   * Create the bullet point for the current update status bullet
    206   * Really only depends on `this.appUpdateStatus`
    207   *
    208   * @returns {TemplateResult} the HTML for the "update" bullet of the custom element
    209   */
    210  buildUpdateElement() {
    211    switch (this.appUpdateStatus) {
    212      case lazy.AppUpdater.STATUS.NO_UPDATES_FOUND:
    213        return html`<li class="status-ok">
    214          <p data-l10n-id=${L10N_IDS.upToDateLabel}></p>
    215        </li>`;
    216      case lazy.AppUpdater.STATUS.MANUAL_UPDATE:
    217      case lazy.AppUpdater.STATUS.DOWNLOADING:
    218      case lazy.AppUpdater.STATUS.DOWNLOAD_AND_INSTALL:
    219      case lazy.AppUpdater.STATUS.STAGING:
    220      case lazy.AppUpdater.STATUS.READY_FOR_RESTART:
    221        return html`<li class="status-alert">
    222          <div>
    223            <p data-l10n-id=${L10N_IDS.updateNeededLabel}></p>
    224            <p>
    225              <small
    226                ><span data-l10n-id=${L10N_IDS.updateNeededDescription}></span
    227              ></small>
    228            </p>
    229            <moz-box-link
    230              @click=${this.#scrollToTargetOnPanel("#general", "updateApp")}
    231              data-l10n-id=${L10N_IDS.updateButtonLabel}
    232            ></moz-box-link>
    233          </div>
    234        </li>`;
    235      case lazy.AppUpdater.STATUS.NEVER_CHECKED:
    236      case lazy.AppUpdater.STATUS.UNSUPPORTED_SYSTEM:
    237      case lazy.AppUpdater.STATUS.DOWNLOAD_FAILED:
    238      case lazy.AppUpdater.STATUS.INTERNAL_ERROR:
    239      case lazy.AppUpdater.STATUS.CHECKING_FAILED:
    240        return html`<li class="status-alert">
    241          <div>
    242            <p data-l10n-id=${L10N_IDS.updateErrorLabel}></p>
    243            <p>
    244              <small
    245                ><span data-l10n-id=${L10N_IDS.updateNeededDescription}></span
    246              ></small>
    247            </p>
    248            <moz-box-link
    249              href="javascript:void(0)"
    250              @click=${this.#scrollToTargetOnPanel("#general", "updateApp")}
    251              data-l10n-id=${L10N_IDS.updateButtonLabel}
    252            ></moz-box-link>
    253          </div>
    254        </li>`;
    255      case lazy.AppUpdater.STATUS.CHECKING:
    256        return html`<li class="status-loading">
    257          <p data-l10n-id=${L10N_IDS.updateCheckingLabel}></p>
    258        </li>`;
    259      case lazy.AppUpdater.STATUS.NO_UPDATER:
    260      case lazy.AppUpdater.STATUS.UPDATE_DISABLED_BY_POLICY:
    261      case lazy.AppUpdater.STATUS.OTHER_INSTANCE_HANDLING_UPDATES:
    262      case undefined:
    263      default:
    264        return html``;
    265    }
    266  }
    267 
    268  /**
    269   * Lit invoked callback to render a template for this component.
    270   * This creates a card for itself, populates it with bullets and headings,
    271   * and nests a <configuration-issue-card>.
    272   *
    273   * @returns {TemplateResult} the full HTML of this panel, CSS <link> and <moz-card>s included
    274   */
    275  render() {
    276    // Create l10n fields for the card's header
    277    let headerL10nId = L10N_IDS.okHeader;
    278    let headerL10nData = { problemCount: 0 };
    279    let trueIssueCount =
    280      this.configIssueCount + (this.#okUpdateStatus() ? 0 : 1);
    281    if (trueIssueCount > 0) {
    282      headerL10nId = L10N_IDS.problemHeader;
    283      headerL10nData.problemCount = trueIssueCount;
    284    }
    285 
    286    // And render this template!
    287    return html`
    288      <link
    289        rel="stylesheet"
    290        href="chrome://browser/content/preferences/widgets/security-privacy-card.css"
    291      />
    292      <moz-card aria-labelledby="heading">
    293        <div class="card-contents">
    294          <div class="status-text-container">
    295            <h3
    296              id="heading"
    297              data-l10n-id=${headerL10nId}
    298              data-l10n-args=${JSON.stringify(headerL10nData)}
    299            ></h3>
    300            <ul>
    301              ${this.buildIssuesElement()} ${this.buildTrackersElement()}
    302              ${this.buildUpdateElement()}
    303            </ul>
    304          </div>
    305          ${this.getStatusImage()}
    306        </div>
    307      </moz-card>
    308    `;
    309  }
    310 }
    311 customElements.define("security-privacy-card", SecurityPrivacyCard);