--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7324,1123 +7324,16 @@ function ReportFalseDeceptiveSite() {
* Currently supported built-ins are LOCALE, APP, and any value from nsIXULAppInfo, uppercased.
*/
function formatURL(aFormat, aIsPref) {
return aIsPref ? Services.urlFormatter.formatURLPref(aFormat) :
Services.urlFormatter.formatURL(aFormat);
}
/**
- * 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 nsIURIFixup.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,
-
- /**
- * nsISSLStatus metadata provided by gBrowser.securityUI the last time the
- * identity UI was updated, or null if the connection is not secure.
- */
- _sslStatus: null,
-
- /**
- * Bitmask provided by nsIWebProgressListener.onSecurityChange.
- */
- _state: 0,
-
- /**
- * This flag gets set if the identity popup was opened by a keypress,
- * to be able to focus it on the popupshown event.
- */
- _popupTriggeredByKeyboard: false,
-
- /**
- * RegExp used to decide if an about url should be shown as being part of
- * the browser UI.
- */
- _secureInternalUIWhitelist: /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|license|permissions|preferences|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i,
-
- get _isBroken() {
- return this._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
- },
-
- get _isSecure() {
- // 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 _isCertUserOverridden() {
- return this._state & Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN;
- },
-
- get _isCertDistrustImminent() {
- return this._state & Ci.nsIWebProgressListener.STATE_CERT_DISTRUST_IMMINENT;
- },
-
- get _hasInsecureLoginForms() {
- // checks if the page has been flagged for an insecure login. Also checks
- // if the pref to degrade the UI is set to true
- return LoginManagerParent.hasInsecureLoginForms(gBrowser.selectedBrowser) &&
- Services.prefs.getBoolPref("security.insecure_password.ui.enabled");
- },
-
- // smart getters
- get _identityPopup() {
- delete this._identityPopup;
- return this._identityPopup = document.getElementById("identity-popup");
- },
- get _identityBox() {
- delete this._identityBox;
- return this._identityBox = document.getElementById("identity-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 _identityPopupContentHosts() {
- delete this._identityPopupContentHosts;
- return this._identityPopupContentHosts =
- [...document.querySelectorAll(".identity-popup-host")];
- },
- get _identityPopupContentHostless() {
- delete this._identityPopupContentHostless;
- return this._identityPopupContentHostless =
- [...document.querySelectorAll(".identity-popup-hostless")];
- },
- 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 _identityPopupMixedContentLearnMore() {
- delete this._identityPopupMixedContentLearnMore;
- return this._identityPopupMixedContentLearnMore =
- document.getElementById("identity-popup-mcb-learn-more");
- },
- get _identityPopupInsecureLoginFormsLearnMore() {
- delete this._identityPopupInsecureLoginFormsLearnMore;
- return this._identityPopupInsecureLoginFormsLearnMore =
- document.getElementById("identity-popup-insecure-login-forms-learn-more");
- },
- get _identityIconLabels() {
- delete this._identityIconLabels;
- return this._identityIconLabels = document.getElementById("identity-icon-labels");
- },
- get _identityIconLabel() {
- delete this._identityIconLabel;
- return this._identityIconLabel = document.getElementById("identity-icon-label");
- },
- get _connectionIcon() {
- delete this._connectionIcon;
- return this._connectionIcon = document.getElementById("connection-icon");
- },
- get _extensionIcon() {
- delete this._extensionIcon;
- return this._extensionIcon = document.getElementById("extension-icon");
- },
- get _overrideService() {
- delete this._overrideService;
- return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
- .getService(Ci.nsICertOverrideService);
- },
- get _identityIconCountryLabel() {
- delete this._identityIconCountryLabel;
- return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
- },
- get _identityIcon() {
- delete this._identityIcon;
- return this._identityIcon = document.getElementById("identity-icon");
- },
- get _permissionList() {
- delete this._permissionList;
- return this._permissionList = document.getElementById("identity-popup-permission-list");
- },
- get _permissionEmptyHint() {
- delete this._permissionEmptyHint;
- return this._permissionEmptyHint = document.getElementById("identity-popup-permission-empty-hint");
- },
- get _permissionReloadHint() {
- delete this._permissionReloadHint;
- return this._permissionReloadHint = document.getElementById("identity-popup-permission-reload-hint");
- },
- get _popupExpander() {
- delete this._popupExpander;
- return this._popupExpander = document.getElementById("identity-popup-security-expander");
- },
- get _permissionAnchors() {
- delete this._permissionAnchors;
- let permissionAnchors = {};
- for (let anchor of document.getElementById("blocked-permissions-container").children) {
- permissionAnchors[anchor.getAttribute("data-permission-id")] = anchor;
- }
- return this._permissionAnchors = permissionAnchors;
- },
-
- /**
- * 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",
- this._popupExpander);
-
- // 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);
- // Reload the page with the content unblocked
- BrowserReloadWithFlags(
- Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT);
- PanelMultiView.hidePopup(this._identityPopup);
- },
-
- enableMixedContentProtection() {
- gBrowser.selectedBrowser.messageManager.sendAsyncMessage(
- "MixedContent:ReenableProtection", {});
- BrowserReload();
- PanelMultiView.hidePopup(this._identityPopup);
- },
-
- removeCertException() {
- if (!this._uriHasHost) {
- Cu.reportError("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);
- BrowserReloadSkipCache();
- PanelMultiView.hidePopup(this._identityPopup);
- },
-
- /**
- * Helper to parse out the important parts of _sslStatus (of the SSL cert in
- * particular) for use in constructing identity UI strings
- */
- getIdentityData() {
- var result = {};
- var cert = this._sslStatus.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;
- },
-
- /**
- * 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 nsIURIFixup.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._sslStatus = gBrowser.securityUI
- .QueryInterface(Ci.nsISSLStatusProvider)
- .SSLStatus;
- if (this._sslStatus) {
- this._sslStatus.QueryInterface(Ci.nsISSLStatus);
- }
-
- // 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) {
- PanelMultiView.hidePopup(this._identityPopup);
- }
-
- // 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.
-
- // Finally, if there are warnings to issue, issue them
- if (this._isCertDistrustImminent) {
- let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
- let windowId = gBrowser.selectedBrowser.innerWindowID;
- let message = gBrowserBundle.GetStringFromName("certImminentDistrust.message");
- // Use uri.prePath instead of initWithSourceURI() so that these can be
- // de-duplicated on the scheme+host+port combination.
- consoleMsg.initWithWindowID(message, uri.prePath, null, 0, 0,
- Ci.nsIScriptError.warningFlag, "SSL",
- windowId);
- Services.console.logMessage(consoleMsg);
- }
- },
-
- /**
- * This is called asynchronously when requested by the Logins module, after
- * the insecure login forms state for the page has been updated.
- */
- refreshForInsecureLoginForms() {
- // Check this._uri because we don't want to refresh the user interface if
- // this is called before the first page load in the window for any reason.
- if (!this._uri) {
- return;
- }
- this.refreshIdentityBlock();
- },
-
- updateSharingIndicator() {
- let tab = gBrowser.selectedTab;
- this._sharingState = tab._sharingState;
-
- this._identityBox.removeAttribute("paused");
- this._identityBox.removeAttribute("sharing");
- if (this._sharingState && this._sharingState.sharing) {
- this._identityBox.setAttribute("sharing", this._sharingState.sharing);
- if (this._sharingState.paused) {
- this._identityBox.setAttribute("paused", "true");
- }
- }
-
- if (this._identityPopup.state == "open") {
- this.updateSitePermissions();
- PanelView.forNode(this._identityPopupMainView)
- .descriptionHeightWorkaround();
- }
- },
-
- /**
- * 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;
- }
- },
-
- /**
- * 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._isEV) {
- return "verifiedIdentity";
- }
- if (this._uriHasHost && this._isSecure) {
- return "verifiedDomain";
- }
- return "unknownIdentity";
- },
-
- /**
- * Updates the identity block user interface with the data from this object.
- */
- refreshIdentityBlock() {
- if (!this._identityBox) {
- return;
- }
-
- let icon_label = "";
- let tooltip = "";
- let icon_country_label = "";
- let icon_labels_dir = "ltr";
-
- if (this._isSecureInternalUI) {
- this._identityBox.className = "chromeUI";
- let brandBundle = document.getElementById("bundle_brand");
- icon_label = brandBundle.getString("brandShorterName");
- } else if (this._uriHasHost && this._isEV) {
- this._identityBox.className = "verifiedIdentity";
- if (this._isMixedActiveContentBlocked) {
- this._identityBox.classList.add("mixedActiveBlocked");
- }
-
- if (!this._isCertUserOverridden) {
- // If it's identified, then we can populate the dialog with credentials
- let iData = this.getIdentityData();
- tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
- [iData.caOrg]);
- icon_label = iData.subjectOrg;
- if (iData.country)
- icon_country_label = "(" + iData.country + ")";
-
- // If the organization name starts with an RTL character, then
- // swap the positions of the organization and country code labels.
- // The Unicode ranges reflect the definition of the UTF16_CODE_UNIT_IS_BIDI
- // macro in intl/unicharutil/util/nsBidiUtils.h. When bug 218823 gets
- // fixed, this test should be replaced by one adhering to the
- // Unicode Bidirectional Algorithm proper (at the paragraph level).
- icon_labels_dir = /^[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc\ud802\ud803\ud83a\ud83b]/.test(icon_label) ?
- "rtl" : "ltr";
- }
- } else if (this._pageExtensionPolicy) {
- this._identityBox.className = "extensionPage";
- let extensionName = this._pageExtensionPolicy.name;
- icon_label = gNavigatorBundle.getFormattedString(
- "identity.extension.label", [extensionName]);
- } else if (this._uriHasHost && this._isSecure) {
- 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._uriHasHost) {
- this._identityBox.className = "unknownIdentity";
- } else if (gBrowser.selectedBrowser.documentURI &&
- (gBrowser.selectedBrowser.documentURI.scheme == "about" ||
- gBrowser.selectedBrowser.documentURI.scheme == "chrome")) {
- // For net errors we should not show notSecure as it's likely confusing
- this._identityBox.className = "unknownIdentity";
- } else {
- if (this._isBroken) {
- 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 {
- let warnOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_icon.enabled") ||
- (Services.prefs.getBoolPref("security.insecure_connection_icon.pbmode.enabled") &&
- PrivateBrowsingUtils.isWindowPrivate(window));
- let className = warnOnInsecure ? "notSecure" : "unknownIdentity";
- this._identityBox.className = className;
-
- let warnTextOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_text.enabled") ||
- (Services.prefs.getBoolPref("security.insecure_connection_text.pbmode.enabled") &&
- PrivateBrowsingUtils.isWindowPrivate(window));
- if (warnTextOnInsecure) {
- icon_label = gNavigatorBundle.getString("identity.notSecure.label");
- this._identityBox.classList.add("notSecureText");
- }
- }
- if (this._hasInsecureLoginForms) {
- // Insecure login forms can only be present on "unknown identity"
- // pages, either already insecure or with mixed active content loaded.
- this._identityBox.classList.add("insecureLoginForms");
- }
- }
-
- 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");
- }
-
- let permissionAnchors = this._permissionAnchors;
-
- // hide all permission icons
- for (let icon of Object.values(permissionAnchors)) {
- icon.removeAttribute("showing");
- }
-
- // keeps track if we should show an indicator that there are active permissions
- let hasGrantedPermissions = false;
-
- // show permission icons
- let permissions = SitePermissions.getAllForBrowser(gBrowser.selectedBrowser);
- for (let permission of permissions) {
- if (permission.state == SitePermissions.BLOCK) {
-
- let icon = permissionAnchors[permission.id];
- if (icon) {
- icon.setAttribute("showing", "true");
- }
-
- } else if (permission.state != SitePermissions.UNKNOWN) {
- hasGrantedPermissions = true;
- }
- }
-
- if (hasGrantedPermissions) {
- this._identityBox.classList.add("grantedPermissions");
- }
-
- // Show blocked popup icon in the identity-box if popups are blocked
- // irrespective of popup permission capability value.
- if (gBrowser.selectedBrowser.blockedPopups &&
- gBrowser.selectedBrowser.blockedPopups.length) {
- let icon = permissionAnchors.popup;
- icon.setAttribute("showing", "true");
- }
-
- // Push the appropriate strings out to the UI
- this._connectionIcon.setAttribute("tooltiptext", tooltip);
-
- if (this._pageExtensionPolicy) {
- let extensionName = this._pageExtensionPolicy.name;
- this._extensionIcon.setAttribute("tooltiptext",
- gNavigatorBundle.getFormattedString("identity.extension.tooltip", [extensionName]));
- }
-
- this._identityIconLabels.setAttribute("tooltiptext", tooltip);
- this._identityIcon.setAttribute("tooltiptext", gNavigatorBundle.getString("identity.icon.tooltip"));
- this._identityIconLabel.setAttribute("value", icon_label);
- this._identityIconCountryLabel.setAttribute("value", icon_country_label);
- // Set cropping and direction
- this._identityIconLabel.setAttribute("crop", icon_country_label ? "end" : "center");
- this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
- // Hide completely if the organization label is empty
- this._identityIconLabel.parentNode.collapsed = !icon_label;
- },
-
- /**
- * 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 "Learn More" for Mixed Content Blocking and Insecure Login Forms.
- let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
- this._identityPopupMixedContentLearnMore
- .setAttribute("href", baseURL + "mixed-content");
- this._identityPopupInsecureLoginFormsLearnMore
- .setAttribute("href", baseURL + "insecure-password");
-
- // This is in the properties file because the expander used to switch its tooltip.
- this._popupExpander.tooltipText = gNavigatorBundle.getString("identity.showDetails.tooltip");
-
- // 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._isSecure) {
- connection = "secure";
- }
-
- // Determine if there are insecure login forms.
- let loginforms = "secure";
- if (this._hasInsecureLoginForms) {
- loginforms = "insecure";
- }
-
- // 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._isBroken && !this._isMixedActiveContentLoaded && !this._isMixedPassiveContentLoaded) {
- ciphers = "weak";
- }
-
- // Update all elements.
- let elementIDs = [
- "identity-popup",
- "identity-popup-securityView-body",
- ];
-
- function updateAttribute(elem, attr, value) {
- if (value) {
- elem.setAttribute(attr, value);
- } else {
- elem.removeAttribute(attr);
- }
- }
-
- for (let id of elementIDs) {
- let element = document.getElementById(id);
- updateAttribute(element, "connection", connection);
- updateAttribute(element, "loginforms", loginforms);
- updateAttribute(element, "ciphers", ciphers);
- updateAttribute(element, "mixedcontent", mixedcontent);
- updateAttribute(element, "isbroken", this._isBroken);
- }
-
- // Initialize the optional strings to empty values
- let supplemental = "";
- let verifier = "";
- let host = "";
- let owner = "";
- let hostless = false;
-
- try {
- host = this.getEffectiveHost();
- } catch (e) {
- // Some URIs might have no hosts.
- }
-
- // Fallback for special protocols.
- if (!host) {
- host = this._uri.specIgnoringRef;
- // Special URIs without a host (eg, about:) should crop the end so
- // the protocol can be seen.
- hostless = true;
- }
-
- if (this._pageExtensionPolicy) {
- host = this._pageExtensionPolicy.name;
- }
-
- // Fill in the CA name if we have a valid TLS certificate.
- if (this._isSecure || this._isCertUserOverridden) {
- verifier = this._identityIconLabels.tooltipText;
- }
-
- // Fill in organization information if we have a valid EV certificate.
- if (this._isEV) {
- let iData = this.getIdentityData();
- host = owner = iData.subjectOrg;
- verifier = this._identityIconLabels.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.
- this._identityPopupContentHosts.forEach((el) => {
- el.textContent = host;
- el.hidden = hostless;
- });
- this._identityPopupContentHostless.forEach((el) => {
- el.setAttribute("value", host);
- el.hidden = !hostless;
- });
- this._identityPopupContentOwner.textContent = owner;
- this._identityPopupContentSupp.textContent = supplemental;
- this._identityPopupContentVerif.textContent = verifier;
-
- // Update per-site permissions section.
- this.updateSitePermissions();
- },
-
- setURI(uri) {
- this._uri = uri;
-
- try {
- // Account for file: urls and catch when "" is the value
- this._uriHasHost = !!this._uri.host;
- } catch (ex) {
- this._uriHasHost = false;
- }
-
- this._isSecureInternalUI = uri.schemeIs("about") &&
- this._secureInternalUIWhitelist.test(uri.pathQueryRef);
-
- 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,
- // so long as we're not sharing any devices.
- // If we are sharing a device, the identity block is prevented by CSS from
- // being focused (and therefore, interacted with) by the user. However, we
- // want to allow opening the identity popup from the device control menu,
- // which calls click() on the identity button, so we don't return early.
- if (!this._sharingState &&
- gURLBar.getAttribute("pageproxystate") != "valid") {
- return;
- }
-
- this._popupTriggeredByKeyboard = event.type == "keypress";
-
- // Make sure that the display:none style we set in xul is removed now that
- // the popup is actually needed
- this._identityPopup.hidden = false;
-
- // Remove the reload hint that we show after a user has cleared a permission.
- this._permissionReloadHint.setAttribute("hidden", "true");
-
- // Update the popup strings
- this.refreshIdentityPopup();
-
- // Add the "open" attribute to the identity box for styling
- this._identityBox.setAttribute("open", "true");
-
- // Now open the popup, anchored off the primary chrome element
- PanelMultiView.openPopup(this._identityPopup, this._identityIcon,
- "bottomcenter topleft").catch(Cu.reportError);
- },
-
- onPopupShown(event) {
- if (event.target == this._identityPopup) {
- if (this._popupTriggeredByKeyboard) {
- // Move focus to the next available element in the identity popup.
- // This is required by role=alertdialog and fixes an issue where
- // an already open panel would steal focus from the identity popup.
- document.commandDispatcher.advanceFocusIntoSubtree(this._identityPopup);
- }
-
- window.addEventListener("focus", this, true);
- }
- },
-
- onPopupHidden(event) {
- if (event.target == this._identityPopup) {
- window.removeEventListener("focus", this, true);
- this._identityBox.removeAttribute("open");
- }
- },
-
- 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) {
- if (topic == "perm-changed") {
- this.refreshIdentityBlock();
- }
- },
-
- onDragStart(event) {
- 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 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(this._identityIcon, 16, 16);
- },
-
- onLocationChange() {
- this._permissionReloadHint.setAttribute("hidden", "true");
-
- if (!this._permissionList.hasChildNodes()) {
- this._permissionEmptyHint.removeAttribute("hidden");
- }
- },
-
- updateSitePermissions() {
- while (this._permissionList.hasChildNodes())
- this._permissionList.removeChild(this._permissionList.lastChild);
-
- let permissions =
- SitePermissions.getAllPermissionDetailsForBrowser(gBrowser.selectedBrowser);
-
- if (this._sharingState) {
- // If WebRTC device or screen permissions are in use, we need to find
- // the associated permission item to set the sharingState field.
- for (let id of ["camera", "microphone", "screen"]) {
- if (this._sharingState[id]) {
- let found = false;
- for (let permission of permissions) {
- if (permission.id != id)
- continue;
- found = true;
- permission.sharingState = this._sharingState[id];
- break;
- }
- if (!found) {
- // If the permission item we were looking for doesn't exist,
- // the user has temporarily allowed sharing and we need to add
- // an item in the permissions array to reflect this.
- permissions.push({
- id,
- state: SitePermissions.ALLOW,
- scope: SitePermissions.SCOPE_REQUEST,
- sharingState: this._sharingState[id],
- });
- }
- }
- }
- }
-
- let hasBlockedPopupIndicator = false;
- for (let permission of permissions) {
- let item = this._createPermissionItem(permission);
- this._permissionList.appendChild(item);
-
- if (permission.id == "popup" &&
- gBrowser.selectedBrowser.blockedPopups &&
- gBrowser.selectedBrowser.blockedPopups.length) {
- this._createBlockedPopupIndicator();
- hasBlockedPopupIndicator = true;
- }
- }
-
- if (gBrowser.selectedBrowser.blockedPopups &&
- gBrowser.selectedBrowser.blockedPopups.length &&
- !hasBlockedPopupIndicator) {
- let permission = {
- id: "popup",
- state: SitePermissions.getDefault("popup"),
- scope: SitePermissions.SCOPE_PERSISTENT,
- };
- let item = this._createPermissionItem(permission);
- this._permissionList.appendChild(item);
- this._createBlockedPopupIndicator();
- }
-
- // Show a placeholder text if there's no permission and no reload hint.
- if (!this._permissionList.hasChildNodes() &&
- this._permissionReloadHint.hasAttribute("hidden")) {
- this._permissionEmptyHint.removeAttribute("hidden");
- } else {
- this._permissionEmptyHint.setAttribute("hidden", "true");
- }
- },
-
- _createPermissionItem(aPermission) {
- let container = document.createElement("hbox");
- container.setAttribute("class", "identity-popup-permission-item");
- container.setAttribute("align", "center");
-
- let img = document.createElement("image");
- img.classList.add("identity-popup-permission-icon");
- if (aPermission.id == "plugin:flash") {
- img.classList.add("plugin-icon");
- } else {
- img.classList.add(aPermission.id + "-icon");
- }
- if (aPermission.state == SitePermissions.BLOCK)
- img.classList.add("blocked-permission-icon");
-
- if (aPermission.sharingState == Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED ||
- (aPermission.id == "screen" && aPermission.sharingState &&
- !aPermission.sharingState.includes("Paused"))) {
- img.classList.add("in-use");
-
- // Synchronize control center and identity block blinking animations.
- window.promiseDocumentFlushed(() => {
- let sharingIconBlink = document.getElementById("sharing-icon").getAnimations()[0];
- let imgBlink = img.getAnimations()[0];
- return [sharingIconBlink, imgBlink];
- }).then(([sharingIconBlink, imgBlink]) => {
- if (sharingIconBlink && imgBlink) {
- imgBlink.startTime = sharingIconBlink.startTime;
- }
- });
- }
-
- let nameLabel = document.createElement("label");
- nameLabel.setAttribute("flex", "1");
- nameLabel.setAttribute("class", "identity-popup-permission-label");
- nameLabel.textContent = SitePermissions.getPermissionLabel(aPermission.id);
-
- let isPolicyPermission = aPermission.scope == SitePermissions.SCOPE_POLICY;
-
- if (aPermission.id == "popup" && !isPolicyPermission) {
- let menulist = document.createElement("menulist");
- let menupopup = document.createElement("menupopup");
- let block = document.createElement("vbox");
- block.setAttribute("id", "identity-popup-popup-container");
- menulist.setAttribute("sizetopopup", "none");
- menulist.setAttribute("class", "identity-popup-popup-menulist");
- menulist.setAttribute("id", "identity-popup-popup-menulist");
-
- for (let state of SitePermissions.getAvailableStates(aPermission.id)) {
- let menuitem = document.createElement("menuitem");
- // We need to correctly display the default/unknown state, which has its
- // own integer value (0) but represents one of the other states.
- if (state == SitePermissions.getDefault(aPermission.id)) {
- menuitem.setAttribute("value", "0");
- } else {
- menuitem.setAttribute("value", state);
- }
- menuitem.setAttribute("label", SitePermissions.getMultichoiceStateLabel(state));
- menupopup.appendChild(menuitem);
- }
-
- menulist.appendChild(menupopup);
-
- if (aPermission.state == SitePermissions.getDefault(aPermission.id)) {
- menulist.value = "0";
- } else {
- menulist.value = aPermission.state;
- }
-
- // Avoiding listening to the "select" event on purpose. See Bug 1404262.
- menulist.addEventListener("command", () => {
- SitePermissions.set(gBrowser.currentURI, "popup", menulist.selectedItem.value);
- });
-
- container.appendChild(img);
- container.appendChild(nameLabel);
- container.appendChild(menulist);
- block.appendChild(container);
-
- return block;
- }
-
- let stateLabel = document.createElement("label");
- stateLabel.setAttribute("flex", "1");
- stateLabel.setAttribute("class", "identity-popup-permission-state-label");
- let {state, scope} = aPermission;
- // If the user did not permanently allow this device but it is currently
- // used, set the variables to display a "temporarily allowed" info.
- if (state != SitePermissions.ALLOW && aPermission.sharingState) {
- state = SitePermissions.ALLOW;
- scope = SitePermissions.SCOPE_REQUEST;
- }
- stateLabel.textContent = SitePermissions.getCurrentStateLabel(state, aPermission.id, scope);
-
- container.appendChild(img);
- container.appendChild(nameLabel);
- container.appendChild(stateLabel);
-
- /* We return the permission item here without a remove button if the permission is a
- SCOPE_POLICY permission. Policy permissions cannot be removed/changed for the duration
- of the browser session. */
- if (isPolicyPermission) {
- return container;
- }
-
- let button = document.createElement("button");
- button.setAttribute("class", "identity-popup-permission-remove-button");
- let tooltiptext = gNavigatorBundle.getString("permissions.remove.tooltip");
- button.setAttribute("tooltiptext", tooltiptext);
- button.addEventListener("command", () => {
- let browser = gBrowser.selectedBrowser;
- this._permissionList.removeChild(container);
- if (aPermission.sharingState &&
- ["camera", "microphone", "screen"].includes(aPermission.id)) {
- let windowId = this._sharingState.windowId;
- if (aPermission.id == "screen") {
- windowId = "screen:" + windowId;
- } else {
- // If we set persistent permissions or the sharing has
- // started due to existing persistent permissions, we need
- // to handle removing these even for frames with different hostnames.
- let uris = browser._devicePermissionURIs || [];
- for (let uri of uris) {
- // It's not possible to stop sharing one of camera/microphone
- // without the other.
- for (let id of ["camera", "microphone"]) {
- if (this._sharingState[id]) {
- let perm = SitePermissions.get(uri, id);
- if (perm.state == SitePermissions.ALLOW &&
- perm.scope == SitePermissions.SCOPE_PERSISTENT) {
- SitePermissions.remove(uri, id);
- }
- }
- }
- }
- }
- browser.messageManager.sendAsyncMessage("webrtc:StopSharing", windowId);
- webrtcUI.forgetActivePermissionsFromBrowser(gBrowser.selectedBrowser);
- }
- SitePermissions.remove(gBrowser.currentURI, aPermission.id, browser);
-
- this._permissionReloadHint.removeAttribute("hidden");
- PanelView.forNode(this._identityPopupMainView)
- .descriptionHeightWorkaround();
- });
-
- container.appendChild(button);
-
- return container;
- },
-
- _createBlockedPopupIndicator() {
- let indicator = document.createElement("hbox");
- indicator.setAttribute("class", "identity-popup-permission-item");
- indicator.setAttribute("align", "center");
- indicator.setAttribute("id", "blocked-popup-indicator-item");
-
- let icon = document.createElement("image");
- icon.setAttribute("class", "popup-subitem identity-popup-permission-icon");
-
- let text = document.createElement("label");
- text.setAttribute("flex", "1");
- text.setAttribute("class", "identity-popup-permission-label text-link");
-
- let popupCount = gBrowser.selectedBrowser.blockedPopups.length;
- let messageBase = gNavigatorBundle.getString("popupShowBlockedPopupsIndicatorText");
- let message = PluralForm.get(popupCount, messageBase)
- .replace("#1", popupCount);
- text.textContent = message;
-
- text.addEventListener("click", () => {
- gPopupBlockerObserver.showAllBlockedPopups(gBrowser.selectedBrowser);
- });
-
- indicator.appendChild(icon);
- indicator.appendChild(text);
-
- document.getElementById("identity-popup-popup-container").appendChild(indicator);
- },
-};
-
-/**
* Fired on the "marionette-remote-control" system notification,
* indicating if the browser session is under remote control.
*/
const gRemoteControl = {
observe(subject, topic, data) {
gRemoteControl.updateVisualCue(data);
},