tor-browser

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

securityLevel.js (10486B)


      1 "use strict";
      2 
      3 /* global AppConstants, Services, openPreferences, XPCOMUtils, gSubDialog */
      4 
      5 ChromeUtils.defineESModuleGetters(this, {
      6  SecurityLevelPrefs: "resource://gre/modules/SecurityLevel.sys.mjs",
      7  SecurityLevelUIUtils: "resource:///modules/SecurityLevelUIUtils.sys.mjs",
      8 });
      9 
     10 /*
     11  Security Level Button Code
     12 
     13  Controls init and update of the security level toolbar button
     14 */
     15 
     16 var SecurityLevelButton = {
     17  _securityPrefsBranch: null,
     18  /**
     19   * Whether we have added popup listeners to the panel.
     20   *
     21   * @type {boolean}
     22   */
     23  _panelPopupListenersSetup: false,
     24  /**
     25   * The toolbar button element.
     26   *
     27   * @type {Element}
     28   */
     29  _button: null,
     30  /**
     31   * The button that the panel should point to. Either the toolbar button or the
     32   * overflow button.
     33   *
     34   * @type {Element}
     35   */
     36  _anchorButton: null,
     37 
     38  _configUIFromPrefs() {
     39    const level = SecurityLevelPrefs.securityLevelSummary;
     40    this._button.setAttribute("level", level);
     41 
     42    let l10nIdLevel;
     43    switch (level) {
     44      case "standard":
     45        l10nIdLevel = "security-level-toolbar-button-standard";
     46        break;
     47      case "safer":
     48        l10nIdLevel = "security-level-toolbar-button-safer";
     49        break;
     50      case "safest":
     51        l10nIdLevel = "security-level-toolbar-button-safest";
     52        break;
     53      case "custom":
     54        l10nIdLevel = "security-level-toolbar-button-custom";
     55        break;
     56      default:
     57        throw Error(`Unhandled level: ${level}`);
     58    }
     59    document.l10n.setAttributes(this._button, l10nIdLevel);
     60  },
     61 
     62  /**
     63   * Open the panel popup for the button.
     64   */
     65  openPopup() {
     66    const overflowPanel = document.getElementById("widget-overflow");
     67    if (overflowPanel.contains(this._button)) {
     68      // We are in the overflow panel.
     69      // We first close the overflow panel, otherwise focus will not return to
     70      // the nav-bar-overflow-button if the security level panel is closed with
     71      // "Escape" (the navigation toolbar does not track focus when a panel is
     72      // opened whilst another is already open).
     73      // NOTE: In principle, using PanelMultiView would allow us to open panels
     74      // from within another panel. However, when using panelmultiview for the
     75      // security level panel, tab navigation was broken within the security
     76      // level panel. PanelMultiView may be set up to work with a menu-like
     77      // panel rather than our dialog-like panel.
     78      overflowPanel.hidePopup();
     79      this._anchorButton = document.getElementById("nav-bar-overflow-button");
     80    } else {
     81      this._anchorButton = this._button;
     82    }
     83 
     84    const panel = SecurityLevelPanel.panel;
     85    if (!this._panelPopupListenersSetup) {
     86      this._panelPopupListenersSetup = true;
     87      // NOTE: We expect the _anchorButton to not change whilst the popup is
     88      // open.
     89      panel.addEventListener("popupshown", () => {
     90        this._anchorButton.setAttribute("open", "true");
     91      });
     92      panel.addEventListener("popuphidden", () => {
     93        this._anchorButton.removeAttribute("open");
     94      });
     95    }
     96 
     97    panel.openPopup(
     98      this._anchorButton.icon,
     99      "bottomright topright",
    100      0,
    101      0,
    102      false
    103    );
    104  },
    105 
    106  init() {
    107    // We first search in the DOM for the security level button. If it does not
    108    // exist it may be in the toolbox palette. We still want to return the
    109    // button in the latter case to allow it to be initialized or adjusted in
    110    // case it is added back through customization.
    111    this._button =
    112      document.getElementById("security-level-button") ||
    113      window.gNavToolbox.palette.querySelector("#security-level-button");
    114    // Set a label to be be used as the accessible name, and to be shown in the
    115    // overflow menu and during customization.
    116    this._button.addEventListener("command", () => this.openPopup());
    117    // set the initial class based off of the current pref
    118    this._configUIFromPrefs();
    119 
    120    this._securityPrefsBranch = Services.prefs.getBranch(
    121      "browser.security_level."
    122    );
    123    this._securityPrefsBranch.addObserver("", this);
    124 
    125    SecurityLevelPanel.init();
    126  },
    127 
    128  uninit() {
    129    this._securityPrefsBranch.removeObserver("", this);
    130    this._securityPrefsBranch = null;
    131 
    132    SecurityLevelPanel.uninit();
    133  },
    134 
    135  observe(subject, topic, data) {
    136    switch (topic) {
    137      case "nsPref:changed":
    138        if (data === "security_slider" || data === "security_custom") {
    139          this._configUIFromPrefs();
    140        }
    141        break;
    142    }
    143  },
    144 }; /* SecurityLevelButton */
    145 
    146 /*
    147  Security Level Panel Code
    148 
    149  Controls init and update of the panel in the security level hanger
    150 */
    151 
    152 var SecurityLevelPanel = {
    153  _securityPrefsBranch: null,
    154  _populated: false,
    155 
    156  _populateXUL() {
    157    this._elements = {
    158      panel: document.getElementById("securityLevel-panel"),
    159      levelName: document.getElementById("securityLevel-level"),
    160      summary: document.getElementById("securityLevel-summary"),
    161    };
    162 
    163    const learnMoreEl = document.getElementById("securityLevel-learnMore");
    164    learnMoreEl.addEventListener("click", () => {
    165      this.hide();
    166    });
    167 
    168    document
    169      .getElementById("securityLevel-settings")
    170      .addEventListener("command", () => {
    171        this.openSecuritySettings();
    172      });
    173 
    174    this._elements.panel.addEventListener("popupshown", () => {
    175      // Bring focus into the panel by focusing the default button.
    176      this._elements.panel.querySelector('button[default="true"]').focus();
    177    });
    178 
    179    this._populated = true;
    180    this._configUIFromPrefs();
    181  },
    182 
    183  _configUIFromPrefs() {
    184    if (!this._populated) {
    185      return;
    186    }
    187 
    188    // get security prefs
    189    const level = SecurityLevelPrefs.securityLevelSummary;
    190 
    191    // Descriptions change based on security level
    192    this._elements.panel.setAttribute("level", level);
    193    let l10nIdLevel;
    194    let l10nIdSummary;
    195    switch (level) {
    196      case "standard":
    197        l10nIdLevel = "security-level-panel-level-standard";
    198        l10nIdSummary = "security-level-summary-standard";
    199        break;
    200      case "safer":
    201        l10nIdLevel = "security-level-panel-level-safer";
    202        l10nIdSummary = "security-level-summary-safer";
    203        break;
    204      case "safest":
    205        l10nIdLevel = "security-level-panel-level-safest";
    206        l10nIdSummary = "security-level-summary-safest";
    207        break;
    208      case "custom":
    209        l10nIdLevel = "security-level-panel-level-custom";
    210        l10nIdSummary = "security-level-summary-custom";
    211        break;
    212      default:
    213        throw Error(`Unhandled level: ${level}`);
    214    }
    215 
    216    document.l10n.setAttributes(this._elements.levelName, l10nIdLevel);
    217    document.l10n.setAttributes(this._elements.summary, l10nIdSummary);
    218  },
    219 
    220  /**
    221   * The popup element.
    222   *
    223   * @type {MozPanel}
    224   */
    225  get panel() {
    226    if (!this._populated) {
    227      this._populateXUL();
    228    }
    229    return this._elements.panel;
    230  },
    231 
    232  init() {
    233    this._securityPrefsBranch = Services.prefs.getBranch(
    234      "browser.security_level."
    235    );
    236    this._securityPrefsBranch.addObserver("", this);
    237  },
    238 
    239  uninit() {
    240    this._securityPrefsBranch.removeObserver("", this);
    241    this._securityPrefsBranch = null;
    242  },
    243 
    244  hide() {
    245    this._elements.panel.hidePopup();
    246  },
    247 
    248  openSecuritySettings() {
    249    openPreferences("privacy-securitylevel");
    250    this.hide();
    251  },
    252 
    253  // callback when prefs change
    254  observe(subject, topic, data) {
    255    switch (topic) {
    256      case "nsPref:changed":
    257        if (data == "security_slider" || data == "security_custom") {
    258          this._configUIFromPrefs();
    259        }
    260        break;
    261    }
    262  },
    263 }; /* SecurityLevelPanel */
    264 
    265 /*
    266  Security Level Preferences Code
    267 
    268  Code to handle init and update of security level section in about:preferences#privacy
    269 */
    270 
    271 var SecurityLevelPreferences = {
    272  _securityPrefsBranch: null,
    273 
    274  /**
    275   * The element that shows the current security level.
    276   *
    277   * @type {?Element}
    278   */
    279  _currentEl: null,
    280 
    281  _populateXUL() {
    282    this._currentEl = document.getElementById("security-level-current");
    283    const changeButton = document.getElementById("security-level-change");
    284    const badgeEl = this._currentEl.querySelector(
    285      ".security-level-current-badge"
    286    );
    287 
    288    for (const { level, nameId } of [
    289      { level: "standard", nameId: "security-level-panel-level-standard" },
    290      { level: "safer", nameId: "security-level-panel-level-safer" },
    291      { level: "safest", nameId: "security-level-panel-level-safest" },
    292      { level: "custom", nameId: "security-level-panel-level-custom" },
    293    ]) {
    294      // Classes that control visibility:
    295      // security-level-current-standard
    296      // security-level-current-safer
    297      // security-level-current-safest
    298      // security-level-current-custom
    299      const visibilityClass = `security-level-current-${level}`;
    300      const nameEl = document.createElement("div");
    301      nameEl.classList.add("security-level-name", visibilityClass);
    302      document.l10n.setAttributes(nameEl, nameId);
    303 
    304      const descriptionEl = SecurityLevelUIUtils.createDescriptionElement(
    305        level,
    306        document
    307      );
    308      descriptionEl.classList.add(visibilityClass);
    309 
    310      this._currentEl.insertBefore(nameEl, badgeEl);
    311      this._currentEl.insertBefore(descriptionEl, changeButton);
    312    }
    313 
    314    changeButton.addEventListener("click", () => {
    315      this._openDialog();
    316    });
    317  },
    318 
    319  _openDialog() {
    320    gSubDialog.open(
    321      "chrome://browser/content/securitylevel/securityLevelDialog.xhtml",
    322      { features: "resizable=yes" }
    323    );
    324  },
    325 
    326  _configUIFromPrefs() {
    327    // Set a data-current-level attribute for showing the current security
    328    // level, and hiding the rest.
    329    this._currentEl.dataset.currentLevel =
    330      SecurityLevelPrefs.securityLevelSummary;
    331  },
    332 
    333  init() {
    334    // populate XUL with localized strings
    335    this._populateXUL();
    336 
    337    // read prefs and populate UI
    338    this._configUIFromPrefs();
    339 
    340    // register for pref chagnes
    341    this._securityPrefsBranch = Services.prefs.getBranch(
    342      "browser.security_level."
    343    );
    344    this._securityPrefsBranch.addObserver("", this);
    345  },
    346 
    347  uninit() {
    348    // unregister for pref change events
    349    this._securityPrefsBranch.removeObserver("", this);
    350    this._securityPrefsBranch = null;
    351  },
    352 
    353  // callback for when prefs change
    354  observe(subject, topic, data) {
    355    switch (topic) {
    356      case "nsPref:changed":
    357        if (data == "security_slider" || data == "security_custom") {
    358          this._configUIFromPrefs();
    359        }
    360        break;
    361    }
    362  },
    363 }; /* SecurityLevelPreferences */