mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	There are 3 types of changes in this commit:
- from `loadURI(foo.spec)` to `loadURI(foo)`, as there's already a URI
- from `loadURI("string")` to `loadURI(Services.io.newURI("string"))` as the URL is hardcoded
- one or two where there is perhaps an intermediate variable but the patch
  context should still make it trivial to ascertain the change is correct.
Depends on D168393
Differential Revision: https://phabricator.services.mozilla.com/D168394
		
	
			
		
			
				
	
	
		
			1341 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1341 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
/* eslint-env mozilla/browser-window */
 | 
						|
 | 
						|
/**
 | 
						|
 * Utility object to handle manipulations of the identity indicators in the UI
 | 
						|
 */
 | 
						|
var gIdentityHandler = {
 | 
						|
  /**
 | 
						|
   * nsIURI for which the identity UI is displayed. This has been already
 | 
						|
   * processed by createExposableURI.
 | 
						|
   */
 | 
						|
  _uri: null,
 | 
						|
 | 
						|
  /**
 | 
						|
   * We only know the connection type if this._uri has a defined "host" part.
 | 
						|
   *
 | 
						|
   * These URIs, like "about:", "file:" and "data:" URIs, will usually be treated as a
 | 
						|
   * an unknown connection.
 | 
						|
   */
 | 
						|
  _uriHasHost: false,
 | 
						|
 | 
						|
  /**
 | 
						|
   * If this tab belongs to a WebExtension, contains its WebExtensionPolicy.
 | 
						|
   */
 | 
						|
  _pageExtensionPolicy: null,
 | 
						|
 | 
						|
  /**
 | 
						|
   * Whether this._uri refers to an internally implemented browser page.
 | 
						|
   *
 | 
						|
   * Note that this is set for some "about:" pages, but general "chrome:" URIs
 | 
						|
   * are not included in this category by default.
 | 
						|
   */
 | 
						|
  _isSecureInternalUI: false,
 | 
						|
 | 
						|
  /**
 | 
						|
   * Whether the content window is considered a "secure context". This
 | 
						|
   * includes "potentially trustworthy" origins such as file:// URLs or localhost.
 | 
						|
   * https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
 | 
						|
   */
 | 
						|
  _isSecureContext: false,
 | 
						|
 | 
						|
  /**
 | 
						|
   * nsITransportSecurityInfo metadata provided by gBrowser.securityUI the last
 | 
						|
   * time the identity UI was updated, or null if the connection is not secure.
 | 
						|
   */
 | 
						|
  _secInfo: null,
 | 
						|
 | 
						|
  /**
 | 
						|
   * Bitmask provided by nsIWebProgressListener.onSecurityChange.
 | 
						|
   */
 | 
						|
  _state: 0,
 | 
						|
 | 
						|
  /**
 | 
						|
   * Whether the established HTTPS connection is considered "broken".
 | 
						|
   * This could have several reasons, such as mixed content or weak
 | 
						|
   * cryptography. If this is true, _isSecureConnection is false.
 | 
						|
   */
 | 
						|
  get _isBrokenConnection() {
 | 
						|
    return this._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Whether the connection to the current site was done via secure
 | 
						|
   * transport. Note that this attribute is not true in all cases that
 | 
						|
   * the site was accessed via HTTPS, i.e. _isSecureConnection will
 | 
						|
   * be false when _isBrokenConnection is true, even though the page
 | 
						|
   * was loaded over HTTPS.
 | 
						|
   */
 | 
						|
  get _isSecureConnection() {
 | 
						|
    // If a <browser> is included within a chrome document, then this._state
 | 
						|
    // will refer to the security state for the <browser> and not the top level
 | 
						|
    // document. In this case, don't upgrade the security state in the UI
 | 
						|
    // with the secure state of the embedded <browser>.
 | 
						|
    return (
 | 
						|
      !this._isURILoadedFromFile &&
 | 
						|
      this._state & Ci.nsIWebProgressListener.STATE_IS_SECURE
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isEV() {
 | 
						|
    // If a <browser> is included within a chrome document, then this._state
 | 
						|
    // will refer to the security state for the <browser> and not the top level
 | 
						|
    // document. In this case, don't upgrade the security state in the UI
 | 
						|
    // with the EV state of the embedded <browser>.
 | 
						|
    return (
 | 
						|
      !this._isURILoadedFromFile &&
 | 
						|
      this._state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isMixedActiveContentLoaded() {
 | 
						|
    return (
 | 
						|
      this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isMixedActiveContentBlocked() {
 | 
						|
    return (
 | 
						|
      this._state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isMixedPassiveContentLoaded() {
 | 
						|
    return (
 | 
						|
      this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isContentHttpsOnlyModeUpgraded() {
 | 
						|
    return (
 | 
						|
      this._state & Ci.nsIWebProgressListener.STATE_HTTPS_ONLY_MODE_UPGRADED
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isContentHttpsOnlyModeUpgradeFailed() {
 | 
						|
    return (
 | 
						|
      this._state &
 | 
						|
      Ci.nsIWebProgressListener.STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isCertUserOverridden() {
 | 
						|
    return this._state & Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN;
 | 
						|
  },
 | 
						|
 | 
						|
  get _isCertErrorPage() {
 | 
						|
    let { documentURI } = gBrowser.selectedBrowser;
 | 
						|
    if (documentURI?.scheme != "about") {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return (
 | 
						|
      documentURI.filePath == "certerror" ||
 | 
						|
      (documentURI.filePath == "neterror" &&
 | 
						|
        new URLSearchParams(documentURI.query).get("e") == "nssFailure2")
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isAboutNetErrorPage() {
 | 
						|
    let { documentURI } = gBrowser.selectedBrowser;
 | 
						|
    return documentURI?.scheme == "about" && documentURI.filePath == "neterror";
 | 
						|
  },
 | 
						|
 | 
						|
  get _isAboutHttpsOnlyErrorPage() {
 | 
						|
    let { documentURI } = gBrowser.selectedBrowser;
 | 
						|
    return (
 | 
						|
      documentURI?.scheme == "about" && documentURI.filePath == "httpsonlyerror"
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isPotentiallyTrustworthy() {
 | 
						|
    return (
 | 
						|
      !this._isBrokenConnection &&
 | 
						|
      (this._isSecureContext ||
 | 
						|
        gBrowser.selectedBrowser.documentURI?.scheme == "chrome")
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  get _isAboutBlockedPage() {
 | 
						|
    let { documentURI } = gBrowser.selectedBrowser;
 | 
						|
    return documentURI?.scheme == "about" && documentURI.filePath == "blocked";
 | 
						|
  },
 | 
						|
 | 
						|
  _popupInitialized: false,
 | 
						|
  _initializePopup() {
 | 
						|
    if (!this._popupInitialized) {
 | 
						|
      let wrapper = document.getElementById("template-identity-popup");
 | 
						|
      wrapper.replaceWith(wrapper.content);
 | 
						|
      this._popupInitialized = true;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  hidePopup() {
 | 
						|
    if (this._popupInitialized) {
 | 
						|
      PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  // smart getters
 | 
						|
  get _identityPopup() {
 | 
						|
    if (!this._popupInitialized) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    delete this._identityPopup;
 | 
						|
    return (this._identityPopup = document.getElementById("identity-popup"));
 | 
						|
  },
 | 
						|
  get _identityBox() {
 | 
						|
    delete this._identityBox;
 | 
						|
    return (this._identityBox = document.getElementById("identity-box"));
 | 
						|
  },
 | 
						|
  get _identityIconBox() {
 | 
						|
    delete this._identityIconBox;
 | 
						|
    return (this._identityIconBox = document.getElementById(
 | 
						|
      "identity-icon-box"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupMultiView() {
 | 
						|
    delete this._identityPopupMultiView;
 | 
						|
    return (this._identityPopupMultiView = document.getElementById(
 | 
						|
      "identity-popup-multiView"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupMainView() {
 | 
						|
    delete this._identityPopupMainView;
 | 
						|
    return (this._identityPopupMainView = document.getElementById(
 | 
						|
      "identity-popup-mainView"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupMainViewHeaderLabel() {
 | 
						|
    delete this._identityPopupMainViewHeaderLabel;
 | 
						|
    return (this._identityPopupMainViewHeaderLabel = document.getElementById(
 | 
						|
      "identity-popup-mainView-panel-header-span"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupSecurityView() {
 | 
						|
    delete this._identityPopupSecurityView;
 | 
						|
    return (this._identityPopupSecurityView = document.getElementById(
 | 
						|
      "identity-popup-securityView"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupHttpsOnlyModeMenuList() {
 | 
						|
    delete this._identityPopupHttpsOnlyModeMenuList;
 | 
						|
    return (this._identityPopupHttpsOnlyModeMenuList = document.getElementById(
 | 
						|
      "identity-popup-security-httpsonlymode-menulist"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupHttpsOnlyModeMenuListTempItem() {
 | 
						|
    delete this._identityPopupHttpsOnlyModeMenuListTempItem;
 | 
						|
    return (this._identityPopupHttpsOnlyModeMenuListTempItem = document.getElementById(
 | 
						|
      "identity-popup-security-menulist-tempitem"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupSecurityEVContentOwner() {
 | 
						|
    delete this._identityPopupSecurityEVContentOwner;
 | 
						|
    return (this._identityPopupSecurityEVContentOwner = document.getElementById(
 | 
						|
      "identity-popup-security-ev-content-owner"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupContentOwner() {
 | 
						|
    delete this._identityPopupContentOwner;
 | 
						|
    return (this._identityPopupContentOwner = document.getElementById(
 | 
						|
      "identity-popup-content-owner"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupContentSupp() {
 | 
						|
    delete this._identityPopupContentSupp;
 | 
						|
    return (this._identityPopupContentSupp = document.getElementById(
 | 
						|
      "identity-popup-content-supplemental"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupContentVerif() {
 | 
						|
    delete this._identityPopupContentVerif;
 | 
						|
    return (this._identityPopupContentVerif = document.getElementById(
 | 
						|
      "identity-popup-content-verifier"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupCustomRootLearnMore() {
 | 
						|
    delete this._identityPopupCustomRootLearnMore;
 | 
						|
    return (this._identityPopupCustomRootLearnMore = document.getElementById(
 | 
						|
      "identity-popup-custom-root-learn-more"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _identityPopupMixedContentLearnMore() {
 | 
						|
    delete this._identityPopupMixedContentLearnMore;
 | 
						|
    return (this._identityPopupMixedContentLearnMore = [
 | 
						|
      ...document.querySelectorAll(".identity-popup-mcb-learn-more"),
 | 
						|
    ]);
 | 
						|
  },
 | 
						|
 | 
						|
  get _identityIconLabel() {
 | 
						|
    delete this._identityIconLabel;
 | 
						|
    return (this._identityIconLabel = document.getElementById(
 | 
						|
      "identity-icon-label"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
  get _overrideService() {
 | 
						|
    delete this._overrideService;
 | 
						|
    return (this._overrideService = Cc[
 | 
						|
      "@mozilla.org/security/certoverride;1"
 | 
						|
    ].getService(Ci.nsICertOverrideService));
 | 
						|
  },
 | 
						|
  get _identityIcon() {
 | 
						|
    delete this._identityIcon;
 | 
						|
    return (this._identityIcon = document.getElementById("identity-icon"));
 | 
						|
  },
 | 
						|
  get _clearSiteDataFooter() {
 | 
						|
    delete this._clearSiteDataFooter;
 | 
						|
    return (this._clearSiteDataFooter = document.getElementById(
 | 
						|
      "identity-popup-clear-sitedata-footer"
 | 
						|
    ));
 | 
						|
  },
 | 
						|
 | 
						|
  get _insecureConnectionIconEnabled() {
 | 
						|
    delete this._insecureConnectionIconEnabled;
 | 
						|
    XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
      this,
 | 
						|
      "_insecureConnectionIconEnabled",
 | 
						|
      "security.insecure_connection_icon.enabled"
 | 
						|
    );
 | 
						|
    return this._insecureConnectionIconEnabled;
 | 
						|
  },
 | 
						|
  get _insecureConnectionIconPBModeEnabled() {
 | 
						|
    delete this._insecureConnectionIconPBModeEnabled;
 | 
						|
    XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
      this,
 | 
						|
      "_insecureConnectionIconPBModeEnabled",
 | 
						|
      "security.insecure_connection_icon.pbmode.enabled"
 | 
						|
    );
 | 
						|
    return this._insecureConnectionIconPBModeEnabled;
 | 
						|
  },
 | 
						|
  get _insecureConnectionTextEnabled() {
 | 
						|
    delete this._insecureConnectionTextEnabled;
 | 
						|
    XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
      this,
 | 
						|
      "_insecureConnectionTextEnabled",
 | 
						|
      "security.insecure_connection_text.enabled"
 | 
						|
    );
 | 
						|
    return this._insecureConnectionTextEnabled;
 | 
						|
  },
 | 
						|
  get _insecureConnectionTextPBModeEnabled() {
 | 
						|
    delete this._insecureConnectionTextPBModeEnabled;
 | 
						|
    XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
      this,
 | 
						|
      "_insecureConnectionTextPBModeEnabled",
 | 
						|
      "security.insecure_connection_text.pbmode.enabled"
 | 
						|
    );
 | 
						|
    return this._insecureConnectionTextPBModeEnabled;
 | 
						|
  },
 | 
						|
  get _protectionsPanelEnabled() {
 | 
						|
    delete this._protectionsPanelEnabled;
 | 
						|
    XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
      this,
 | 
						|
      "_protectionsPanelEnabled",
 | 
						|
      "browser.protections_panel.enabled",
 | 
						|
      false
 | 
						|
    );
 | 
						|
    return this._protectionsPanelEnabled;
 | 
						|
  },
 | 
						|
  get _httpsOnlyModeEnabled() {
 | 
						|
    delete this._httpsOnlyModeEnabled;
 | 
						|
    XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
      this,
 | 
						|
      "_httpsOnlyModeEnabled",
 | 
						|
      "dom.security.https_only_mode"
 | 
						|
    );
 | 
						|
    return this._httpsOnlyModeEnabled;
 | 
						|
  },
 | 
						|
  get _httpsOnlyModeEnabledPBM() {
 | 
						|
    delete this._httpsOnlyModeEnabledPBM;
 | 
						|
    XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
      this,
 | 
						|
      "_httpsOnlyModeEnabledPBM",
 | 
						|
      "dom.security.https_only_mode_pbm"
 | 
						|
    );
 | 
						|
    return this._httpsOnlyModeEnabledPBM;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Handles clicks on the "Clear Cookies and Site Data" button.
 | 
						|
   */
 | 
						|
  async clearSiteData(event) {
 | 
						|
    if (!this._uriHasHost) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Hide the popup before showing the removal prompt, to
 | 
						|
    // avoid a pretty ugly transition. Also hide it even
 | 
						|
    // if the update resulted in no site data, to keep the
 | 
						|
    // illusion that clicking the button had an effect.
 | 
						|
    let hidden = new Promise(c => {
 | 
						|
      this._identityPopup.addEventListener("popuphidden", c, { once: true });
 | 
						|
    });
 | 
						|
    PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
    await hidden;
 | 
						|
 | 
						|
    let baseDomain = SiteDataManager.getBaseDomainFromHost(this._uri.host);
 | 
						|
    if (SiteDataManager.promptSiteDataRemoval(window, [baseDomain])) {
 | 
						|
      SiteDataManager.remove(baseDomain);
 | 
						|
    }
 | 
						|
 | 
						|
    event.stopPropagation();
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Handler for mouseclicks on the "More Information" button in the
 | 
						|
   * "identity-popup" panel.
 | 
						|
   */
 | 
						|
  handleMoreInfoClick(event) {
 | 
						|
    displaySecurityInfo();
 | 
						|
    event.stopPropagation();
 | 
						|
    PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
  },
 | 
						|
 | 
						|
  showSecuritySubView() {
 | 
						|
    this._identityPopupMultiView.showSubView(
 | 
						|
      "identity-popup-securityView",
 | 
						|
      document.getElementById("identity-popup-security-button")
 | 
						|
    );
 | 
						|
 | 
						|
    // Elements of hidden views have -moz-user-focus:ignore but setting that
 | 
						|
    // per CSS selector doesn't blur a focused element in those hidden views.
 | 
						|
    Services.focus.clearFocus(window);
 | 
						|
  },
 | 
						|
 | 
						|
  disableMixedContentProtection() {
 | 
						|
    // Use telemetry to measure how often unblocking happens
 | 
						|
    const kMIXED_CONTENT_UNBLOCK_EVENT = 2;
 | 
						|
    let histogram = Services.telemetry.getHistogramById(
 | 
						|
      "MIXED_CONTENT_UNBLOCK_COUNTER"
 | 
						|
    );
 | 
						|
    histogram.add(kMIXED_CONTENT_UNBLOCK_EVENT);
 | 
						|
 | 
						|
    SitePermissions.setForPrincipal(
 | 
						|
      gBrowser.contentPrincipal,
 | 
						|
      "mixed-content",
 | 
						|
      SitePermissions.ALLOW,
 | 
						|
      SitePermissions.SCOPE_SESSION
 | 
						|
    );
 | 
						|
 | 
						|
    // Reload the page with the content unblocked
 | 
						|
    BrowserReloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
 | 
						|
    if (this._popupInitialized) {
 | 
						|
      PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  // This is needed for some tests which need the permission reset, but which
 | 
						|
  // then reuse the browser and would race between the reload and the next
 | 
						|
  // load.
 | 
						|
  enableMixedContentProtectionNoReload() {
 | 
						|
    this.enableMixedContentProtection(false);
 | 
						|
  },
 | 
						|
 | 
						|
  enableMixedContentProtection(reload = true) {
 | 
						|
    SitePermissions.removeFromPrincipal(
 | 
						|
      gBrowser.contentPrincipal,
 | 
						|
      "mixed-content"
 | 
						|
    );
 | 
						|
    if (reload) {
 | 
						|
      BrowserReload();
 | 
						|
    }
 | 
						|
    if (this._popupInitialized) {
 | 
						|
      PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  removeCertException() {
 | 
						|
    if (!this._uriHasHost) {
 | 
						|
      console.error(
 | 
						|
        "Trying to revoke a cert exception on a URI without a host?"
 | 
						|
      );
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    let host = this._uri.host;
 | 
						|
    let port = this._uri.port > 0 ? this._uri.port : 443;
 | 
						|
    this._overrideService.clearValidityOverride(
 | 
						|
      host,
 | 
						|
      port,
 | 
						|
      gBrowser.contentPrincipal.originAttributes
 | 
						|
    );
 | 
						|
    BrowserReloadSkipCache();
 | 
						|
    if (this._popupInitialized) {
 | 
						|
      PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Gets the current HTTPS-Only mode permission for the current page.
 | 
						|
   * Values are the same as in #identity-popup-security-httpsonlymode-menulist
 | 
						|
   */
 | 
						|
  _getHttpsOnlyPermission() {
 | 
						|
    const { state } = SitePermissions.getForPrincipal(
 | 
						|
      gBrowser.contentPrincipal,
 | 
						|
      "https-only-load-insecure"
 | 
						|
    );
 | 
						|
    switch (state) {
 | 
						|
      case Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION:
 | 
						|
        return 2; // Off temporarily
 | 
						|
      case Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW:
 | 
						|
        return 1; // Off
 | 
						|
      default:
 | 
						|
        return 0; // On
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets/removes HTTPS-Only Mode exception and possibly reloads the page.
 | 
						|
   */
 | 
						|
  changeHttpsOnlyPermission() {
 | 
						|
    // Get the new value from the menulist and the current value
 | 
						|
    // Note: value and permission association is laid out
 | 
						|
    //       in _getHttpsOnlyPermission
 | 
						|
    const oldValue = this._getHttpsOnlyPermission();
 | 
						|
    let newValue = parseInt(
 | 
						|
      this._identityPopupHttpsOnlyModeMenuList.selectedItem.value,
 | 
						|
      10
 | 
						|
    );
 | 
						|
 | 
						|
    // If nothing changed, just return here
 | 
						|
    if (newValue === oldValue) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Permissions set in PMB get deleted anyway, but to make sure, let's make
 | 
						|
    // the permission session-only.
 | 
						|
    if (newValue === 1 && PrivateBrowsingUtils.isWindowPrivate(window)) {
 | 
						|
      newValue = 2;
 | 
						|
    }
 | 
						|
 | 
						|
    // Usually we want to set the permission for the current site and therefore
 | 
						|
    // the current principal...
 | 
						|
    let principal = gBrowser.contentPrincipal;
 | 
						|
    // ...but if we're on the HTTPS-Only error page, the content-principal is
 | 
						|
    // for HTTPS but. We always want to set the exception for HTTP. (Code should
 | 
						|
    // be almost identical to the one in AboutHttpsOnlyErrorParent.sys.mjs)
 | 
						|
    let newURI;
 | 
						|
    if (this._isAboutHttpsOnlyErrorPage) {
 | 
						|
      newURI = gBrowser.currentURI
 | 
						|
        .mutate()
 | 
						|
        .setScheme("http")
 | 
						|
        .finalize();
 | 
						|
      principal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
        newURI,
 | 
						|
        gBrowser.contentPrincipal.originAttributes
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    // Set or remove the permission
 | 
						|
    if (newValue === 0) {
 | 
						|
      SitePermissions.removeFromPrincipal(
 | 
						|
        principal,
 | 
						|
        "https-only-load-insecure"
 | 
						|
      );
 | 
						|
    } else if (newValue === 1) {
 | 
						|
      SitePermissions.setForPrincipal(
 | 
						|
        principal,
 | 
						|
        "https-only-load-insecure",
 | 
						|
        Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW,
 | 
						|
        SitePermissions.SCOPE_PERSISTENT
 | 
						|
      );
 | 
						|
    } else {
 | 
						|
      SitePermissions.setForPrincipal(
 | 
						|
        principal,
 | 
						|
        "https-only-load-insecure",
 | 
						|
        Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION,
 | 
						|
        SitePermissions.SCOPE_SESSION
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    // If we're on the error-page, we have to redirect the user
 | 
						|
    // from HTTPS to HTTP. Otherwise we can just reload the page.
 | 
						|
    if (this._isAboutHttpsOnlyErrorPage) {
 | 
						|
      gBrowser.loadURI(newURI, {
 | 
						|
        triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
 | 
						|
        loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY,
 | 
						|
      });
 | 
						|
      if (this._popupInitialized) {
 | 
						|
        PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    // The page only needs to reload if we switch between allow and block
 | 
						|
    // Because "off" is 1 and "off temporarily" is 2, we can just check if the
 | 
						|
    // sum of newValue and oldValue is 3.
 | 
						|
    if (newValue + oldValue !== 3) {
 | 
						|
      BrowserReloadSkipCache();
 | 
						|
      if (this._popupInitialized) {
 | 
						|
        PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    // Otherwise we just refresh the interface
 | 
						|
    this.refreshIdentityPopup();
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Helper to parse out the important parts of _secInfo (of the SSL cert in
 | 
						|
   * particular) for use in constructing identity UI strings
 | 
						|
   */
 | 
						|
  getIdentityData() {
 | 
						|
    var result = {};
 | 
						|
    var cert = this._secInfo.serverCert;
 | 
						|
 | 
						|
    // Human readable name of Subject
 | 
						|
    result.subjectOrg = cert.organization;
 | 
						|
 | 
						|
    // SubjectName fields, broken up for individual access
 | 
						|
    if (cert.subjectName) {
 | 
						|
      result.subjectNameFields = {};
 | 
						|
      cert.subjectName.split(",").forEach(function(v) {
 | 
						|
        var field = v.split("=");
 | 
						|
        this[field[0]] = field[1];
 | 
						|
      }, result.subjectNameFields);
 | 
						|
 | 
						|
      // Call out city, state, and country specifically
 | 
						|
      result.city = result.subjectNameFields.L;
 | 
						|
      result.state = result.subjectNameFields.ST;
 | 
						|
      result.country = result.subjectNameFields.C;
 | 
						|
    }
 | 
						|
 | 
						|
    // Human readable name of Certificate Authority
 | 
						|
    result.caOrg = cert.issuerOrganization || cert.issuerCommonName;
 | 
						|
    result.cert = cert;
 | 
						|
 | 
						|
    return result;
 | 
						|
  },
 | 
						|
 | 
						|
  _getIsSecureContext() {
 | 
						|
    if (gBrowser.contentPrincipal?.originNoSuffix != "resource://pdf.js") {
 | 
						|
      return gBrowser.securityUI.isSecureContext;
 | 
						|
    }
 | 
						|
 | 
						|
    // For PDF viewer pages (pdf.js) we can't rely on the isSecureContext field.
 | 
						|
    // The backend will return isSecureContext = true, because the content
 | 
						|
    // principal has a resource:// URI. Instead use the URI of the selected
 | 
						|
    // browser to perform the isPotentiallyTrustWorthy check.
 | 
						|
 | 
						|
    let principal;
 | 
						|
    try {
 | 
						|
      principal = Services.scriptSecurityManager.createContentPrincipal(
 | 
						|
        gBrowser.selectedBrowser.documentURI,
 | 
						|
        {}
 | 
						|
      );
 | 
						|
      return principal.isOriginPotentiallyTrustworthy;
 | 
						|
    } catch (error) {
 | 
						|
      console.error(
 | 
						|
        "Error while computing isPotentiallyTrustWorthy for pdf viewer page: ",
 | 
						|
        error
 | 
						|
      );
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Update the identity user interface for the page currently being displayed.
 | 
						|
   *
 | 
						|
   * This examines the SSL certificate metadata, if available, as well as the
 | 
						|
   * connection type and other security-related state information for the page.
 | 
						|
   *
 | 
						|
   * @param state
 | 
						|
   *        Bitmask provided by nsIWebProgressListener.onSecurityChange.
 | 
						|
   * @param uri
 | 
						|
   *        nsIURI for which the identity UI should be displayed, already
 | 
						|
   *        processed by createExposableURI.
 | 
						|
   */
 | 
						|
  updateIdentity(state, uri) {
 | 
						|
    let shouldHidePopup = this._uri && this._uri.spec != uri.spec;
 | 
						|
    this._state = state;
 | 
						|
 | 
						|
    // Firstly, populate the state properties required to display the UI. See
 | 
						|
    // the documentation of the individual properties for details.
 | 
						|
    this.setURI(uri);
 | 
						|
    this._secInfo = gBrowser.securityUI.secInfo;
 | 
						|
    this._isSecureContext = this._getIsSecureContext();
 | 
						|
 | 
						|
    // Then, update the user interface with the available data.
 | 
						|
    this.refreshIdentityBlock();
 | 
						|
    // Handle a location change while the Control Center is focused
 | 
						|
    // by closing the popup (bug 1207542)
 | 
						|
    if (shouldHidePopup) {
 | 
						|
      this.hidePopup();
 | 
						|
      gPermissionPanel.hidePopup();
 | 
						|
    }
 | 
						|
 | 
						|
    // NOTE: We do NOT update the identity popup (the control center) when
 | 
						|
    // we receive a new security state on the existing page (i.e. from a
 | 
						|
    // subframe). If the user opened the popup and looks at the provided
 | 
						|
    // information we don't want to suddenly change the panel contents.
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Attempt to provide proper IDN treatment for host names
 | 
						|
   */
 | 
						|
  getEffectiveHost() {
 | 
						|
    if (!this._IDNService) {
 | 
						|
      this._IDNService = Cc["@mozilla.org/network/idn-service;1"].getService(
 | 
						|
        Ci.nsIIDNService
 | 
						|
      );
 | 
						|
    }
 | 
						|
    try {
 | 
						|
      return this._IDNService.convertToDisplayIDN(this._uri.host, {});
 | 
						|
    } catch (e) {
 | 
						|
      // If something goes wrong (e.g. host is an IP address) just fail back
 | 
						|
      // to the full domain.
 | 
						|
      return this._uri.host;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  getHostForDisplay() {
 | 
						|
    let host = "";
 | 
						|
 | 
						|
    try {
 | 
						|
      host = this.getEffectiveHost();
 | 
						|
    } catch (e) {
 | 
						|
      // Some URIs might have no hosts.
 | 
						|
    }
 | 
						|
 | 
						|
    if (this._uri.schemeIs("about")) {
 | 
						|
      // For example in about:certificate the original URL is
 | 
						|
      // about:certificate?cert=<large base64 encoded data>&cert=<large base64 encoded data>&cert=...
 | 
						|
      // So, instead of showing that large string in the identity panel header, we are just showing
 | 
						|
      // about:certificate now. For the other about pages we are just showing about:<page>
 | 
						|
      host = "about:" + this._uri.filePath;
 | 
						|
    }
 | 
						|
 | 
						|
    if (this._uri.schemeIs("chrome")) {
 | 
						|
      host = this._uri.spec;
 | 
						|
    }
 | 
						|
 | 
						|
    let readerStrippedURI = ReaderMode.getOriginalUrlObjectForDisplay(
 | 
						|
      this._uri.displaySpec
 | 
						|
    );
 | 
						|
    if (readerStrippedURI) {
 | 
						|
      host = readerStrippedURI.host;
 | 
						|
    }
 | 
						|
 | 
						|
    if (this._pageExtensionPolicy) {
 | 
						|
      host = this._pageExtensionPolicy.name;
 | 
						|
    }
 | 
						|
 | 
						|
    // Fallback for special protocols.
 | 
						|
    if (!host) {
 | 
						|
      host = this._uri.specIgnoringRef;
 | 
						|
    }
 | 
						|
 | 
						|
    return host;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Return the CSS class name to set on the "fullscreen-warning" element to
 | 
						|
   * display information about connection security in the notification shown
 | 
						|
   * when a site enters the fullscreen mode.
 | 
						|
   */
 | 
						|
  get pointerlockFsWarningClassName() {
 | 
						|
    // Note that the fullscreen warning does not handle _isSecureInternalUI.
 | 
						|
    if (this._uriHasHost && this._isSecureConnection) {
 | 
						|
      return "verifiedDomain";
 | 
						|
    }
 | 
						|
    return "unknownIdentity";
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns whether the issuer of the current certificate chain is
 | 
						|
   * built-in (returns false) or imported (returns true).
 | 
						|
   */
 | 
						|
  _hasCustomRoot() {
 | 
						|
    let issuerCert = null;
 | 
						|
    issuerCert = this._secInfo.succeededCertChain[
 | 
						|
      this._secInfo.succeededCertChain.length - 1
 | 
						|
    ];
 | 
						|
 | 
						|
    return !issuerCert.isBuiltInRoot;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns whether the current URI results in an "invalid"
 | 
						|
   * URL bar state, which effectively means hidden security
 | 
						|
   * indicators.
 | 
						|
   */
 | 
						|
  _hasInvalidPageProxyState() {
 | 
						|
    return (
 | 
						|
      !this._uriHasHost &&
 | 
						|
      this._uri &&
 | 
						|
      isBlankPageURL(this._uri.spec) &&
 | 
						|
      !this._uri.schemeIs("moz-extension")
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Updates the security identity in the identity block.
 | 
						|
   */
 | 
						|
  _refreshIdentityIcons() {
 | 
						|
    let icon_label = "";
 | 
						|
    let tooltip = "";
 | 
						|
 | 
						|
    if (this._isSecureInternalUI) {
 | 
						|
      // This is a secure internal Firefox page.
 | 
						|
      this._identityBox.className = "chromeUI";
 | 
						|
      let brandBundle = document.getElementById("bundle_brand");
 | 
						|
      icon_label = brandBundle.getString("brandShorterName");
 | 
						|
    } else if (this._pageExtensionPolicy) {
 | 
						|
      // This is a WebExtension page.
 | 
						|
      this._identityBox.className = "extensionPage";
 | 
						|
      let extensionName = this._pageExtensionPolicy.name;
 | 
						|
      icon_label = gNavigatorBundle.getFormattedString(
 | 
						|
        "identity.extension.label",
 | 
						|
        [extensionName]
 | 
						|
      );
 | 
						|
    } else if (this._uriHasHost && this._isSecureConnection) {
 | 
						|
      // This is a secure connection.
 | 
						|
      this._identityBox.className = "verifiedDomain";
 | 
						|
      if (this._isMixedActiveContentBlocked) {
 | 
						|
        this._identityBox.classList.add("mixedActiveBlocked");
 | 
						|
      }
 | 
						|
      if (!this._isCertUserOverridden) {
 | 
						|
        // It's a normal cert, verifier is the CA Org.
 | 
						|
        tooltip = gNavigatorBundle.getFormattedString(
 | 
						|
          "identity.identified.verifier",
 | 
						|
          [this.getIdentityData().caOrg]
 | 
						|
        );
 | 
						|
      }
 | 
						|
    } else if (this._isBrokenConnection) {
 | 
						|
      // This is a secure connection, but something is wrong.
 | 
						|
      this._identityBox.className = "unknownIdentity";
 | 
						|
 | 
						|
      if (this._isMixedActiveContentLoaded) {
 | 
						|
        this._identityBox.classList.add("mixedActiveContent");
 | 
						|
      } else if (this._isMixedActiveContentBlocked) {
 | 
						|
        this._identityBox.classList.add(
 | 
						|
          "mixedDisplayContentLoadedActiveBlocked"
 | 
						|
        );
 | 
						|
      } else if (this._isMixedPassiveContentLoaded) {
 | 
						|
        this._identityBox.classList.add("mixedDisplayContent");
 | 
						|
      } else {
 | 
						|
        this._identityBox.classList.add("weakCipher");
 | 
						|
      }
 | 
						|
    } else if (this._isCertErrorPage) {
 | 
						|
      // We show a warning lock icon for certificate errors, and
 | 
						|
      // show the "Not Secure" text.
 | 
						|
      this._identityBox.className = "certErrorPage notSecureText";
 | 
						|
      icon_label = gNavigatorBundle.getString("identity.notSecure.label");
 | 
						|
      tooltip = gNavigatorBundle.getString("identity.notSecure.tooltip");
 | 
						|
    } else if (this._isAboutHttpsOnlyErrorPage) {
 | 
						|
      // We show a not secure lock icon for 'about:httpsonlyerror' page.
 | 
						|
      this._identityBox.className = "httpsOnlyErrorPage";
 | 
						|
    } else if (this._isAboutNetErrorPage || this._isAboutBlockedPage) {
 | 
						|
      // Network errors and blocked pages get a more neutral icon
 | 
						|
      this._identityBox.className = "unknownIdentity";
 | 
						|
    } else if (this._isPotentiallyTrustworthy) {
 | 
						|
      // This is a local resource (and shouldn't be marked insecure).
 | 
						|
      this._identityBox.className = "localResource";
 | 
						|
    } else {
 | 
						|
      // This is an insecure connection.
 | 
						|
      let warnOnInsecure =
 | 
						|
        this._insecureConnectionIconEnabled ||
 | 
						|
        (this._insecureConnectionIconPBModeEnabled &&
 | 
						|
          PrivateBrowsingUtils.isWindowPrivate(window));
 | 
						|
      let className = warnOnInsecure ? "notSecure" : "unknownIdentity";
 | 
						|
      this._identityBox.className = className;
 | 
						|
      tooltip = warnOnInsecure
 | 
						|
        ? gNavigatorBundle.getString("identity.notSecure.tooltip")
 | 
						|
        : "";
 | 
						|
 | 
						|
      let warnTextOnInsecure =
 | 
						|
        this._insecureConnectionTextEnabled ||
 | 
						|
        (this._insecureConnectionTextPBModeEnabled &&
 | 
						|
          PrivateBrowsingUtils.isWindowPrivate(window));
 | 
						|
      if (warnTextOnInsecure) {
 | 
						|
        icon_label = gNavigatorBundle.getString("identity.notSecure.label");
 | 
						|
        this._identityBox.classList.add("notSecureText");
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (this._isCertUserOverridden) {
 | 
						|
      this._identityBox.classList.add("certUserOverridden");
 | 
						|
      // Cert is trusted because of a security exception, verifier is a special string.
 | 
						|
      tooltip = gNavigatorBundle.getString(
 | 
						|
        "identity.identified.verified_by_you"
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    // Push the appropriate strings out to the UI
 | 
						|
    this._identityIcon.setAttribute("tooltiptext", tooltip);
 | 
						|
 | 
						|
    if (this._pageExtensionPolicy) {
 | 
						|
      let extensionName = this._pageExtensionPolicy.name;
 | 
						|
      this._identityIcon.setAttribute(
 | 
						|
        "tooltiptext",
 | 
						|
        gNavigatorBundle.getFormattedString("identity.extension.tooltip", [
 | 
						|
          extensionName,
 | 
						|
        ])
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    this._identityIconLabel.setAttribute("tooltiptext", tooltip);
 | 
						|
    this._identityIconLabel.setAttribute("value", icon_label);
 | 
						|
    this._identityIconLabel.collapsed = !icon_label;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Updates the identity block user interface with the data from this object.
 | 
						|
   */
 | 
						|
  refreshIdentityBlock() {
 | 
						|
    if (!this._identityBox) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this._refreshIdentityIcons();
 | 
						|
 | 
						|
    // If this condition is true, the URL bar will have an "invalid"
 | 
						|
    // pageproxystate, so we should hide the permission icons.
 | 
						|
    if (this._hasInvalidPageProxyState()) {
 | 
						|
      gPermissionPanel.hidePermissionIcons();
 | 
						|
    } else {
 | 
						|
      gPermissionPanel.refreshPermissionIcons();
 | 
						|
    }
 | 
						|
 | 
						|
    // Hide the shield icon if it is a chrome page.
 | 
						|
    gProtectionsHandler._trackingProtectionIconContainer.classList.toggle(
 | 
						|
      "chromeUI",
 | 
						|
      this._isSecureInternalUI
 | 
						|
    );
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Set up the title and content messages for the identity message popup,
 | 
						|
   * based on the specified mode, and the details of the SSL cert, where
 | 
						|
   * applicable
 | 
						|
   */
 | 
						|
  refreshIdentityPopup() {
 | 
						|
    // Update cookies and site data information and show the
 | 
						|
    // "Clear Site Data" button if the site is storing local data, and
 | 
						|
    // if the page is not controlled by a WebExtension.
 | 
						|
    this._clearSiteDataFooter.hidden = true;
 | 
						|
    let identityPopupPanelView = document.getElementById(
 | 
						|
      "identity-popup-mainView"
 | 
						|
    );
 | 
						|
    identityPopupPanelView.removeAttribute("footerVisible");
 | 
						|
    if (this._uriHasHost && !this._pageExtensionPolicy) {
 | 
						|
      SiteDataManager.hasSiteData(this._uri.asciiHost).then(hasData => {
 | 
						|
        this._clearSiteDataFooter.hidden = !hasData;
 | 
						|
        identityPopupPanelView.setAttribute("footerVisible", hasData);
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    // Update "Learn More" for Mixed Content Blocking and Insecure Login Forms.
 | 
						|
    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
 | 
						|
    this._identityPopupMixedContentLearnMore.forEach(e =>
 | 
						|
      e.setAttribute("href", baseURL + "mixed-content")
 | 
						|
    );
 | 
						|
 | 
						|
    this._identityPopupCustomRootLearnMore.setAttribute(
 | 
						|
      "href",
 | 
						|
      baseURL + "enterprise-roots"
 | 
						|
    );
 | 
						|
 | 
						|
    let customRoot = false;
 | 
						|
 | 
						|
    // Determine connection security information.
 | 
						|
    let connection = "not-secure";
 | 
						|
    if (this._isSecureInternalUI) {
 | 
						|
      connection = "chrome";
 | 
						|
    } else if (this._pageExtensionPolicy) {
 | 
						|
      connection = "extension";
 | 
						|
    } else if (this._isURILoadedFromFile) {
 | 
						|
      connection = "file";
 | 
						|
    } else if (this._isEV) {
 | 
						|
      connection = "secure-ev";
 | 
						|
    } else if (this._isCertUserOverridden) {
 | 
						|
      connection = "secure-cert-user-overridden";
 | 
						|
    } else if (this._isSecureConnection) {
 | 
						|
      connection = "secure";
 | 
						|
      customRoot = this._hasCustomRoot();
 | 
						|
    } else if (this._isCertErrorPage) {
 | 
						|
      connection = "cert-error-page";
 | 
						|
    } else if (this._isAboutHttpsOnlyErrorPage) {
 | 
						|
      connection = "https-only-error-page";
 | 
						|
    } else if (this._isAboutBlockedPage) {
 | 
						|
      connection = "not-secure";
 | 
						|
    } else if (this._isAboutNetErrorPage) {
 | 
						|
      connection = "net-error-page";
 | 
						|
    } else if (this._isPotentiallyTrustworthy) {
 | 
						|
      connection = "file";
 | 
						|
    }
 | 
						|
 | 
						|
    let securityButtonNode = document.getElementById(
 | 
						|
      "identity-popup-security-button"
 | 
						|
    );
 | 
						|
 | 
						|
    let disableSecurityButton = ![
 | 
						|
      "not-secure",
 | 
						|
      "secure",
 | 
						|
      "secure-ev",
 | 
						|
      "secure-cert-user-overridden",
 | 
						|
      "cert-error-page",
 | 
						|
      "net-error-page",
 | 
						|
      "https-only-error-page",
 | 
						|
    ].includes(connection);
 | 
						|
    if (disableSecurityButton) {
 | 
						|
      securityButtonNode.disabled = true;
 | 
						|
      securityButtonNode.classList.remove("subviewbutton-nav");
 | 
						|
    } else {
 | 
						|
      securityButtonNode.disabled = false;
 | 
						|
      securityButtonNode.classList.add("subviewbutton-nav");
 | 
						|
    }
 | 
						|
 | 
						|
    // Determine the mixed content state.
 | 
						|
    let mixedcontent = [];
 | 
						|
    if (this._isMixedPassiveContentLoaded) {
 | 
						|
      mixedcontent.push("passive-loaded");
 | 
						|
    }
 | 
						|
    if (this._isMixedActiveContentLoaded) {
 | 
						|
      mixedcontent.push("active-loaded");
 | 
						|
    } else if (this._isMixedActiveContentBlocked) {
 | 
						|
      mixedcontent.push("active-blocked");
 | 
						|
    }
 | 
						|
    mixedcontent = mixedcontent.join(" ");
 | 
						|
 | 
						|
    // We have no specific flags for weak ciphers (yet). If a connection is
 | 
						|
    // broken and we can't detect any mixed content loaded then it's a weak
 | 
						|
    // cipher.
 | 
						|
    let ciphers = "";
 | 
						|
    if (
 | 
						|
      this._isBrokenConnection &&
 | 
						|
      !this._isMixedActiveContentLoaded &&
 | 
						|
      !this._isMixedPassiveContentLoaded
 | 
						|
    ) {
 | 
						|
      ciphers = "weak";
 | 
						|
    }
 | 
						|
 | 
						|
    // If HTTPS-Only Mode is enabled, check the permission status
 | 
						|
    const privateBrowsingWindow = PrivateBrowsingUtils.isWindowPrivate(window);
 | 
						|
    let httpsOnlyStatus = "";
 | 
						|
    if (
 | 
						|
      this._httpsOnlyModeEnabled ||
 | 
						|
      (privateBrowsingWindow && this._httpsOnlyModeEnabledPBM)
 | 
						|
    ) {
 | 
						|
      // Note: value and permission association is laid out
 | 
						|
      //       in _getHttpsOnlyPermission
 | 
						|
      let value = this._getHttpsOnlyPermission();
 | 
						|
 | 
						|
      // Because everything in PBM is temporary anyway, we don't need to make the distinction
 | 
						|
      if (privateBrowsingWindow) {
 | 
						|
        if (value === 2) {
 | 
						|
          value = 1;
 | 
						|
        }
 | 
						|
        // Hide "off temporarily" option
 | 
						|
        this._identityPopupHttpsOnlyModeMenuListTempItem.style.display = "none";
 | 
						|
      } else {
 | 
						|
        this._identityPopupHttpsOnlyModeMenuListTempItem.style.display = "";
 | 
						|
      }
 | 
						|
 | 
						|
      this._identityPopupHttpsOnlyModeMenuList.value = value;
 | 
						|
 | 
						|
      if (value > 0) {
 | 
						|
        httpsOnlyStatus = "exception";
 | 
						|
      } else if (this._isAboutHttpsOnlyErrorPage) {
 | 
						|
        httpsOnlyStatus = "failed-top";
 | 
						|
      } else if (this._isContentHttpsOnlyModeUpgradeFailed) {
 | 
						|
        httpsOnlyStatus = "failed-sub";
 | 
						|
      } else if (this._isContentHttpsOnlyModeUpgraded) {
 | 
						|
        httpsOnlyStatus = "upgraded";
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Update all elements.
 | 
						|
    let elementIDs = [
 | 
						|
      "identity-popup",
 | 
						|
      "identity-popup-securityView-extended-info",
 | 
						|
    ];
 | 
						|
 | 
						|
    for (let id of elementIDs) {
 | 
						|
      let element = document.getElementById(id);
 | 
						|
      this._updateAttribute(element, "connection", connection);
 | 
						|
      this._updateAttribute(element, "ciphers", ciphers);
 | 
						|
      this._updateAttribute(element, "mixedcontent", mixedcontent);
 | 
						|
      this._updateAttribute(element, "isbroken", this._isBrokenConnection);
 | 
						|
      this._updateAttribute(element, "customroot", customRoot);
 | 
						|
      this._updateAttribute(element, "httpsonlystatus", httpsOnlyStatus);
 | 
						|
    }
 | 
						|
 | 
						|
    // Initialize the optional strings to empty values
 | 
						|
    let supplemental = "";
 | 
						|
    let verifier = "";
 | 
						|
    let host = this.getHostForDisplay();
 | 
						|
    let owner = "";
 | 
						|
 | 
						|
    // Fill in the CA name if we have a valid TLS certificate.
 | 
						|
    if (this._isSecureConnection || this._isCertUserOverridden) {
 | 
						|
      verifier = this._identityIconLabel.tooltipText;
 | 
						|
    }
 | 
						|
 | 
						|
    // Fill in organization information if we have a valid EV certificate.
 | 
						|
    if (this._isEV) {
 | 
						|
      let iData = this.getIdentityData();
 | 
						|
      owner = iData.subjectOrg;
 | 
						|
      verifier = this._identityIconLabel.tooltipText;
 | 
						|
 | 
						|
      // Build an appropriate supplemental block out of whatever location data we have
 | 
						|
      if (iData.city) {
 | 
						|
        supplemental += iData.city + "\n";
 | 
						|
      }
 | 
						|
      if (iData.state && iData.country) {
 | 
						|
        supplemental += gNavigatorBundle.getFormattedString(
 | 
						|
          "identity.identified.state_and_country",
 | 
						|
          [iData.state, iData.country]
 | 
						|
        );
 | 
						|
      } else if (iData.state) {
 | 
						|
        // State only
 | 
						|
        supplemental += iData.state;
 | 
						|
      } else if (iData.country) {
 | 
						|
        // Country only
 | 
						|
        supplemental += iData.country;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Push the appropriate strings out to the UI.
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      this._identityPopupMainViewHeaderLabel,
 | 
						|
      "identity-site-information",
 | 
						|
      {
 | 
						|
        host,
 | 
						|
      }
 | 
						|
    );
 | 
						|
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      this._identityPopupSecurityView,
 | 
						|
      "identity-header-security-with-host",
 | 
						|
      {
 | 
						|
        host,
 | 
						|
      }
 | 
						|
    );
 | 
						|
 | 
						|
    this._identityPopupSecurityEVContentOwner.textContent = gNavigatorBundle.getFormattedString(
 | 
						|
      "identity.ev.contentOwner2",
 | 
						|
      [owner]
 | 
						|
    );
 | 
						|
 | 
						|
    this._identityPopupContentOwner.textContent = owner;
 | 
						|
    this._identityPopupContentSupp.textContent = supplemental;
 | 
						|
    this._identityPopupContentVerif.textContent = verifier;
 | 
						|
  },
 | 
						|
 | 
						|
  setURI(uri) {
 | 
						|
    if (uri.schemeIs("view-source")) {
 | 
						|
      uri = Services.io.newURI(uri.spec.replace(/^view-source:/i, ""));
 | 
						|
    }
 | 
						|
    this._uri = uri;
 | 
						|
 | 
						|
    try {
 | 
						|
      // Account for file: urls and catch when "" is the value
 | 
						|
      this._uriHasHost = !!this._uri.host;
 | 
						|
    } catch (ex) {
 | 
						|
      this._uriHasHost = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (uri.schemeIs("about")) {
 | 
						|
      let module = E10SUtils.getAboutModule(uri);
 | 
						|
      if (module) {
 | 
						|
        let flags = module.getURIFlags(uri);
 | 
						|
        this._isSecureInternalUI = !!(
 | 
						|
          flags & Ci.nsIAboutModule.IS_SECURE_CHROME_UI
 | 
						|
        );
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      this._isSecureInternalUI = false;
 | 
						|
    }
 | 
						|
    this._pageExtensionPolicy = WebExtensionPolicy.getByURI(uri);
 | 
						|
 | 
						|
    // Create a channel for the sole purpose of getting the resolved URI
 | 
						|
    // of the request to determine if it's loaded from the file system.
 | 
						|
    this._isURILoadedFromFile = false;
 | 
						|
    let chanOptions = { uri: this._uri, loadUsingSystemPrincipal: true };
 | 
						|
    let resolvedURI;
 | 
						|
    try {
 | 
						|
      resolvedURI = NetUtil.newChannel(chanOptions).URI;
 | 
						|
      if (resolvedURI.schemeIs("jar")) {
 | 
						|
        // Given a URI "jar:<jar-file-uri>!/<jar-entry>"
 | 
						|
        // create a new URI using <jar-file-uri>!/<jar-entry>
 | 
						|
        resolvedURI = NetUtil.newURI(resolvedURI.pathQueryRef);
 | 
						|
      }
 | 
						|
      // Check the URI again after resolving.
 | 
						|
      this._isURILoadedFromFile = resolvedURI.schemeIs("file");
 | 
						|
    } catch (ex) {
 | 
						|
      // NetUtil's methods will throw for malformed URIs and the like
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Click handler for the identity-box element in primary chrome.
 | 
						|
   */
 | 
						|
  handleIdentityButtonEvent(event) {
 | 
						|
    event.stopPropagation();
 | 
						|
 | 
						|
    if (
 | 
						|
      (event.type == "click" && event.button != 0) ||
 | 
						|
      (event.type == "keypress" &&
 | 
						|
        event.charCode != KeyEvent.DOM_VK_SPACE &&
 | 
						|
        event.keyCode != KeyEvent.DOM_VK_RETURN)
 | 
						|
    ) {
 | 
						|
      return; // Left click, space or enter only
 | 
						|
    }
 | 
						|
 | 
						|
    // Don't allow left click, space or enter if the location has been modified.
 | 
						|
    if (gURLBar.getAttribute("pageproxystate") != "valid") {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this._openPopup(event);
 | 
						|
  },
 | 
						|
 | 
						|
  _openPopup(event) {
 | 
						|
    // Make the popup available.
 | 
						|
    this._initializePopup();
 | 
						|
 | 
						|
    // Update the popup strings
 | 
						|
    this.refreshIdentityPopup();
 | 
						|
 | 
						|
    // Check the panel state of other panels. Hide them if needed.
 | 
						|
    let openPanels = Array.from(document.querySelectorAll("panel[openpanel]"));
 | 
						|
    for (let panel of openPanels) {
 | 
						|
      PanelMultiView.hidePopup(panel);
 | 
						|
    }
 | 
						|
 | 
						|
    // Now open the popup, anchored off the primary chrome element
 | 
						|
    PanelMultiView.openPopup(this._identityPopup, this._identityIconBox, {
 | 
						|
      position: "bottomleft topleft",
 | 
						|
      triggerEvent: event,
 | 
						|
    }).catch(console.error);
 | 
						|
  },
 | 
						|
 | 
						|
  onPopupShown(event) {
 | 
						|
    if (event.target == this._identityPopup) {
 | 
						|
      PopupNotifications.suppressWhileOpen(this._identityPopup);
 | 
						|
      window.addEventListener("focus", this, true);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  onPopupHidden(event) {
 | 
						|
    if (event.target == this._identityPopup) {
 | 
						|
      window.removeEventListener("focus", this, true);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  handleEvent(event) {
 | 
						|
    let elem = document.activeElement;
 | 
						|
    let position = elem.compareDocumentPosition(this._identityPopup);
 | 
						|
 | 
						|
    if (
 | 
						|
      !(
 | 
						|
        position &
 | 
						|
        (Node.DOCUMENT_POSITION_CONTAINS | Node.DOCUMENT_POSITION_CONTAINED_BY)
 | 
						|
      ) &&
 | 
						|
      !this._identityPopup.hasAttribute("noautohide")
 | 
						|
    ) {
 | 
						|
      // Hide the panel when focusing an element that is
 | 
						|
      // neither an ancestor nor descendant unless the panel has
 | 
						|
      // @noautohide (e.g. for a tour).
 | 
						|
      PanelMultiView.hidePopup(this._identityPopup);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  observe(subject, topic, data) {
 | 
						|
    switch (topic) {
 | 
						|
      case "perm-changed": {
 | 
						|
        // Exclude permissions which do not appear in the UI in order to avoid
 | 
						|
        // doing extra work here.
 | 
						|
        if (!subject) {
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        let { type } = subject.QueryInterface(Ci.nsIPermission);
 | 
						|
        if (SitePermissions.isSitePermission(type)) {
 | 
						|
          this.refreshIdentityBlock();
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  onDragStart(event) {
 | 
						|
    const TEXT_SIZE = 14;
 | 
						|
    const IMAGE_SIZE = 16;
 | 
						|
    const SPACING = 5;
 | 
						|
 | 
						|
    if (gURLBar.getAttribute("pageproxystate") != "valid") {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let value = gBrowser.currentURI.displaySpec;
 | 
						|
    let urlString = value + "\n" + gBrowser.contentTitle;
 | 
						|
    let htmlString = '<a href="' + value + '">' + value + "</a>";
 | 
						|
 | 
						|
    let scale = window.devicePixelRatio;
 | 
						|
    let canvas = document.createElementNS(
 | 
						|
      "http://www.w3.org/1999/xhtml",
 | 
						|
      "canvas"
 | 
						|
    );
 | 
						|
    canvas.width = 550 * scale;
 | 
						|
    let ctx = canvas.getContext("2d");
 | 
						|
    ctx.font = `${TEXT_SIZE * scale}px sans-serif`;
 | 
						|
    let tabIcon = gBrowser.selectedTab.iconImage;
 | 
						|
    let image = new Image();
 | 
						|
    image.src = tabIcon.src;
 | 
						|
    let textWidth = ctx.measureText(value).width / scale;
 | 
						|
    let textHeight = parseInt(ctx.font, 10) / scale;
 | 
						|
    let imageHorizontalOffset, imageVerticalOffset;
 | 
						|
    imageHorizontalOffset = imageVerticalOffset = SPACING;
 | 
						|
    let textHorizontalOffset = image.width ? IMAGE_SIZE + SPACING * 2 : SPACING;
 | 
						|
    let textVerticalOffset = textHeight + SPACING - 1;
 | 
						|
    let backgroundColor = "white";
 | 
						|
    let textColor = "black";
 | 
						|
    let totalWidth = image.width
 | 
						|
      ? textWidth + IMAGE_SIZE + 3 * SPACING
 | 
						|
      : textWidth + 2 * SPACING;
 | 
						|
    let totalHeight = image.width
 | 
						|
      ? IMAGE_SIZE + 2 * SPACING
 | 
						|
      : textHeight + 2 * SPACING;
 | 
						|
    ctx.fillStyle = backgroundColor;
 | 
						|
    ctx.fillRect(0, 0, totalWidth * scale, totalHeight * scale);
 | 
						|
    ctx.fillStyle = textColor;
 | 
						|
    ctx.fillText(
 | 
						|
      `${value}`,
 | 
						|
      textHorizontalOffset * scale,
 | 
						|
      textVerticalOffset * scale
 | 
						|
    );
 | 
						|
    try {
 | 
						|
      ctx.drawImage(
 | 
						|
        image,
 | 
						|
        imageHorizontalOffset * scale,
 | 
						|
        imageVerticalOffset * scale,
 | 
						|
        IMAGE_SIZE * scale,
 | 
						|
        IMAGE_SIZE * scale
 | 
						|
      );
 | 
						|
    } catch (e) {
 | 
						|
      // Sites might specify invalid data URIs favicons that
 | 
						|
      // will result in errors when trying to draw, we can
 | 
						|
      // just ignore this case and not paint any favicon.
 | 
						|
    }
 | 
						|
 | 
						|
    let dt = event.dataTransfer;
 | 
						|
    dt.setData("text/x-moz-url", urlString);
 | 
						|
    dt.setData("text/uri-list", value);
 | 
						|
    dt.setData("text/plain", value);
 | 
						|
    dt.setData("text/html", htmlString);
 | 
						|
    dt.setDragImage(canvas, 16, 16);
 | 
						|
 | 
						|
    // Don't cover potential drop targets on the toolbars or in content.
 | 
						|
    gURLBar.view.close();
 | 
						|
  },
 | 
						|
 | 
						|
  _updateAttribute(elem, attr, value) {
 | 
						|
    if (value) {
 | 
						|
      elem.setAttribute(attr, value);
 | 
						|
    } else {
 | 
						|
      elem.removeAttribute(attr);
 | 
						|
    }
 | 
						|
  },
 | 
						|
};
 |