Bug 767676 - Implement Security UI Telemetry. r=honzab,bsmith,felipc,dtownsend
authorDevdatta Akhawe <dev.akhawe@gmail.com>
Thu, 02 Aug 2012 18:51:17 -0700
changeset 106743 c2090a46d34e345af6499d4ac6b7d5ae70b9451e
parent 106742 766981b3d8682d684eb33468ef31d990aa7aa14b
child 106744 6a82b0462271dadc79f584028abb290237e48344
push id1490
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 18:29:50 +0000
treeherdermozilla-beta@f335e7dacdc1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab, bsmith, felipc, dtownsend
bugs767676
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 767676 - Implement Security UI Telemetry. r=honzab,bsmith,felipc,dtownsend
browser/base/content/browser-addons.js
browser/base/content/browser.js
docshell/base/nsDocShell.cpp
security/manager/boot/public/Makefile.in
security/manager/boot/public/nsISecurityUITelemetry.idl
security/manager/boot/src/nsSecurityWarningDialogs.cpp
security/manager/boot/src/nsSecurityWarningDialogs.h
security/manager/pki/resources/content/exceptionDialog.js
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/mozapps/extensions/amWebInstallListener.js
toolkit/mozapps/extensions/content/xpinstallConfirm.js
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -72,24 +72,27 @@ const gXPInstallObserver = {
 
       PopupNotifications.show(browser, notificationID, messageString, anchorID,
                               action, null, options);
       break;
     case "addon-install-blocked":
       messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
                         [brandShortName, installInfo.originatingURI.host]);
 
+      let secHistogram = Components.classes["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry).getHistogramById("SECURITY_UI");
       action = {
         label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
         accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
         callback: function() {
+          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED_CLICK_THROUGH);
           installInfo.install();
         }
       };
 
+      secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED);
       PopupNotifications.show(browser, notificationID, messageString, anchorID,
                               action, null, options);
       break;
     case "addon-install-started":
       function needsDownload(aInstall) {
         return aInstall.state != AddonManager.STATE_DOWNLOADED;
       }
       // If all installs have already been downloaded then there is no need to
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2514,19 +2514,23 @@ let BrowserOnClick = {
     }
     else if (/^about:home$/i.test(ownerDoc.documentURI)) {
       this.onAboutHome(originalTarget, ownerDoc);
     }
   },
 
   onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
+    let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
+                        getService(Ci.nsITelemetry).
+                        getHistogramById("SECURITY_UI");
 
     switch (elmId) {
       case "exceptionDialogButton":
+        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_CLICK_ADD_EXCEPTION);
         let params = { exceptionAdded : false, handlePrivateBrowsing : true };
 
         try {
           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
             case 2 : // Pre-fetch & pre-populate
               params.prefetchCert = true;
             case 1 : // Pre-populate
               params.location = aOwnerDoc.location.href;
@@ -2540,39 +2544,59 @@ let BrowserOnClick = {
 
         // If the user added the exception cert, attempt to reload the page
         if (params.exceptionAdded) {
           aOwnerDoc.location.reload();
         }
         break;
 
       case "getMeOutOfHereButton":
+        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_GET_ME_OUT_OF_HERE);
         getMeOutOfHere();
         break;
+
+      case "technicalContent":
+        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TECHNICAL_DETAILS);
+        break;
+
+      case "expertContent":
+        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_UNDERSTAND_RISKS);
+        break;
+
     }
   },
 
   onAboutBlocked: function BrowserOnClick_onAboutBlocked(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
+    let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
+                        getService(Ci.nsITelemetry).
+                        getHistogramById("SECURITY_UI");
 
     // The event came from a button on a malware/phishing block page
     // First check whether it's malware or phishing, so that we can
     // use the right strings/links
     let isMalware = /e=malwareBlocked/.test(aOwnerDoc.documentURI);
+    let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
+    let nsISecTel = Ci.nsISecurityUITelemetry;
 
     switch (elmId) {
       case "getMeOutButton":
+        secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
         getMeOutOfHere();
         break;
 
       case "reportButton":
         // This is the "Why is this site blocked" button.  For malware,
         // we can fetch a site-specific report, for phishing, we redirect
         // to the generic page describing phishing protection.
 
+        // We log even if malware/phishing info URL couldn't be found: 
+        // the measurement is for how many users clicked the WHY BLOCKED button
+        secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]);
+
         if (isMalware) {
           // Get the stop badware "why is this blocked" report url,
           // append the current url, and go there.
           try {
             let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
             reportURL += aOwnerDoc.location.href;
             content.location = reportURL;
           } catch (e) {
@@ -2584,16 +2608,17 @@ let BrowserOnClick = {
             content.location = formatURL("browser.safebrowsing.warning.infoURL", true);
           } catch (e) {
             Components.utils.reportError("Couldn't get phishing info URL: " + e);
           }
         }
         break;
 
       case "ignoreWarningButton":
+        secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
         this.ignoreWarningButton(isMalware);
         break;
     }
   },
 
   ignoreWarningButton: function BrowserOnClick_ignoreWarningButton(aIsMalware) {
     // Allow users to override and continue through to the site,
     // but add a notify bar as a reminder, so that they don't lose
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -187,16 +187,19 @@
 
 #include "nsXULAppAPI.h"
 
 #include "nsDOMNavigationTiming.h"
 #include "nsITimedChannel.h"
 #include "mozilla/StartupTimeline.h"
 #include "nsIFrameMessageManager.h"
 
+#include "mozilla/Telemetry.h"
+#include "nsISecurityUITelemetry.h"
+
 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
                      NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 //#define DEBUG_DOCSHELL_FOCUS
 #define DEBUG_PAGE_CACHE
 #endif
@@ -4035,30 +4038,42 @@ nsDocShell::DisplayLoadError(nsresult aE
                 nsCOMPtr<nsIStrictTransportSecurityService> stss =
                           do_GetService(NS_STSSERVICE_CONTRACTID, &rv);
                 NS_ENSURE_SUCCESS(rv, rv);
 
                 bool isStsHost = false;
                 rv = stss->IsStsURI(aURI, &isStsHost);
                 NS_ENSURE_SUCCESS(rv, rv);
 
-                if (isStsHost)
+                PRUint32 bucketId;
+                if (isStsHost) {
                   cssClass.AssignLiteral("badStsCert");
+                  //measuring STS separately allows us to measure click through
+                  //rates easily
+                  bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_STS;
+                } else {
+                  bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT;
+                }
+
 
                 if (Preferences::GetBool(
                         "browser.xul.error_pages.expert_bad_cert", false)) {
                     cssClass.AssignLiteral("expertBadCert");
                 }
 
                 // See if an alternate cert error page is registered
                 nsAdoptingCString alternateErrorPage =
                     Preferences::GetCString(
                         "security.alternate_certificate_error_page");
                 if (alternateErrorPage)
                     errorPage.Assign(alternateErrorPage);
+
+                if (errorPage.EqualsIgnoreCase("certerror")) 
+                    mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
+
             } else {
                 error.AssignLiteral("nssFailure2");
             }
         }
     } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
         nsCAutoString host;
         aURI->GetHost(host);
         CopyUTF8toUTF16(host, formatStrs[0]);
@@ -4066,20 +4081,29 @@ nsDocShell::DisplayLoadError(nsresult aE
 
         // Malware and phishing detectors may want to use an alternate error
         // page, but if the pref's not set, we'll fall back on the standard page
         nsAdoptingCString alternateErrorPage =
             Preferences::GetCString("urlclassifier.alternate_error_page");
         if (alternateErrorPage)
             errorPage.Assign(alternateErrorPage);
 
-        if (NS_ERROR_PHISHING_URI == aError)
+        PRUint32 bucketId;
+        if (NS_ERROR_PHISHING_URI == aError) {
             error.AssignLiteral("phishingBlocked");
-        else
+            bucketId = nsISecurityUITelemetry::WARNING_PHISHING_PAGE;
+        } else {
             error.AssignLiteral("malwareBlocked");
+            bucketId = nsISecurityUITelemetry::WARNING_MALWARE_PAGE;
+        }
+
+        if (errorPage.EqualsIgnoreCase("blocked"))
+            mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI,
+                                           bucketId);
+
         cssClass.AssignLiteral("blacklist");
     }
     else {
         // Errors requiring simple formatting
         switch (aError) {
         case NS_ERROR_MALFORMED_URI:
             // URI is malformed
             error.AssignLiteral("malformedURI");
--- a/security/manager/boot/public/Makefile.in
+++ b/security/manager/boot/public/Makefile.in
@@ -14,11 +14,12 @@ GRE_MODULE	= 1
 
 SDK_XPIDLSRCS = \
     nsISecurityWarningDialogs.idl \
     $(NULL)
 
 XPIDLSRCS = \
     nsISSLStatusProvider.idl \
     nsIBufEntropyCollector.idl \
+    nsISecurityUITelemetry.idl \
     $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/security/manager/boot/public/nsISecurityUITelemetry.idl
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(454b5cbb-fd18-4f34-a616-4d543f68717d)]
+
+interface nsISecurityUITelemetry : nsISupports {
+
+/*
+ * Addon installation warnings
+ */
+
+// Firefox prevented this site from asking you to install addon
+const PRUint32 WARNING_ADDON_ASKING_PREVENTED = 1;
+// User clicks through and allows site to ask to install addons
+const PRUint32 WARNING_ADDON_ASKING_PREVENTED_CLICK_THROUGH = 2;
+// Are you sure you want to install this addon? Only install addons you trust
+const PRUint32 WARNING_CONFIRM_ADDON_INSTALL = 3;
+// User clicked she is sure after waiting 3secs
+const PRUint32 WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH  = 4;
+
+
+/*
+ * modal dialogs/warnings 
+ */
+
+const PRUint32 WARNING_ENTERING_SECURE_SITE = 5;
+const PRUint32 WARNING_ENTERING_WEAK_SITE = 6;
+const PRUint32 WARNING_LEAVING_SECURE_SITE = 7;
+const PRUint32 WARNING_MIXED_CONTENT  = 8;
+
+// For confirmation dialogs, the clickthrough constant needs to be 1
+// more than the dialog constant so that
+// WARNING_CONFIRM_<X> + 1 == WARNING_CONFIRM_<X>_CLICK_THROUGH
+const PRUint32 WARNING_CONFIRM_POST_TO_INSECURE_FROM_SECURE = 9;
+const PRUint32 WARNING_CONFIRM_POST_TO_INSECURE_FROM_SECURE_CLICK_THROUGH = 10;
+const PRUint32 WARNING_CONFIRM_POST_TO_INSECURE_FROM_INSECURE = 11;
+const PRUint32 WARNING_CONFIRM_POST_TO_INSECURE_FROM_INSECURE_CLICK_THROUGH = 12;
+
+/*
+ * Phishing / Malware page warnings
+ */
+
+const PRUint32 WARNING_MALWARE_PAGE = 13;
+const PRUint32 WARNING_MALWARE_PAGE_WHY_BLOCKED = 14;
+const PRUint32 WARNING_MALWARE_PAGE_GET_ME_OUT_OF_HERE = 15;
+const PRUint32 WARNING_MALWARE_PAGE_IGNORE_WARNING = 16;
+
+const PRUint32 WARNING_PHISHING_PAGE = 17;
+const PRUint32 WARNING_PHISHING_PAGE_WHY_BLOCKED = 18;
+const PRUint32 WARNING_PHISHING_PAGE_GET_ME_OUT_OF_HERE = 19;
+const PRUint32 WARNING_PHISHING_PAGE_IGNORE_WARNING = 20;
+
+/* 
+ * SSL Error dialogs
+ */
+
+const PRUint32 WARNING_BAD_CERT = 21;
+const PRUint32 WARNING_BAD_CERT_STS = 22;
+const PRUint32 WARNING_BAD_CERT_CLICK_ADD_EXCEPTION = 23;
+const PRUint32 WARNING_BAD_CERT_CLICK_VIEW_CERT = 24;
+const PRUint32 WARNING_BAD_CERT_DONT_REMEMBER_EXCEPTION = 25;
+const PRUint32 WARNING_BAD_CERT_GET_ME_OUT_OF_HERE = 27;
+const PRUint32 WARNING_BAD_CERT_UNDERSTAND_RISKS = 28;
+const PRUint32 WARNING_BAD_CERT_TECHINICAL_DETAILS = 29;
+
+/*
+ * Note that if we add more possibilities in the warning dialogs,
+ * it is a new experiment and we shouldn't reuse these buckets.
+ */
+const PRUint32 WARNING_BAD_CERT_ADD_EXCEPTION_BASE = 30;
+const PRUint32 WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_UNTRUSTED = 1;
+const PRUint32 WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_DOMAIN = 2;
+const PRUint32 WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_TIME = 4;
+
+const PRUint32 WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_BASE = 38;
+const PRUint32 WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED = 1;
+const PRUint32 WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN = 2;
+const PRUint32 WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_TIME = 4;
+// This uses up buckets till 45
+
+};
--- a/security/manager/boot/src/nsSecurityWarningDialogs.cpp
+++ b/security/manager/boot/src/nsSecurityWarningDialogs.cpp
@@ -12,16 +12,19 @@
 #include "nsIPrompt.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
 
+#include "mozilla/Telemetry.h"
+#include "nsISecurityUITelemetry.h"
+
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsSecurityWarningDialogs, nsISecurityWarningDialogs)
 
 #define STRING_BUNDLE_URL    "chrome://pipnss/locale/security.properties"
 
 #define ENTER_SITE_PREF      "security.warn_entering_secure"
 #define WEAK_SITE_PREF       "security.warn_entering_weak"
 #define LEAVE_SITE_PREF      "security.warn_leaving_secure"
 #define MIXEDCONTENT_PREF    "security.warn_viewing_mixed"
@@ -55,102 +58,110 @@ nsSecurityWarningDialogs::Init()
 NS_IMETHODIMP 
 nsSecurityWarningDialogs::ConfirmEnteringSecure(nsIInterfaceRequestor *ctx, bool *_retval)
 {
   nsresult rv;
 
   rv = AlertDialog(ctx, ENTER_SITE_PREF, 
                    NS_LITERAL_STRING("EnterSecureMessage").get(),
                    NS_LITERAL_STRING("EnterSecureShowAgain").get(),
-                   false);
+                   false,
+                   nsISecurityUITelemetry::WARNING_ENTERING_SECURE_SITE);
 
   *_retval = true;
   return rv;
 }
 
 NS_IMETHODIMP 
 nsSecurityWarningDialogs::ConfirmEnteringWeak(nsIInterfaceRequestor *ctx, bool *_retval)
 {
   nsresult rv;
 
   rv = AlertDialog(ctx, WEAK_SITE_PREF,
                    NS_LITERAL_STRING("WeakSecureMessage").get(),
                    NS_LITERAL_STRING("WeakSecureShowAgain").get(),
-                   false);
+                   false,
+                   nsISecurityUITelemetry::WARNING_ENTERING_WEAK_SITE);
 
   *_retval = true;
   return rv;
 }
 
 NS_IMETHODIMP 
 nsSecurityWarningDialogs::ConfirmLeavingSecure(nsIInterfaceRequestor *ctx, bool *_retval)
 {
   nsresult rv;
 
   rv = AlertDialog(ctx, LEAVE_SITE_PREF, 
                    NS_LITERAL_STRING("LeaveSecureMessage").get(),
                    NS_LITERAL_STRING("LeaveSecureShowAgain").get(),
-                   false);
+                   false,
+                   nsISecurityUITelemetry::WARNING_LEAVING_SECURE_SITE);
 
   *_retval = true;
   return rv;
 }
 
 
 NS_IMETHODIMP 
 nsSecurityWarningDialogs::ConfirmMixedMode(nsIInterfaceRequestor *ctx, bool *_retval)
 {
   nsresult rv;
 
   rv = AlertDialog(ctx, MIXEDCONTENT_PREF, 
                    NS_LITERAL_STRING("MixedContentMessage").get(),
                    NS_LITERAL_STRING("MixedContentShowAgain").get(),
-                   true);
+                   true,
+                   nsISecurityUITelemetry::WARNING_MIXED_CONTENT);
 
   *_retval = true;
   return rv;
 }
 
 class nsAsyncAlert : public nsRunnable
 {
 public:
   nsAsyncAlert(nsIPrompt*       aPrompt,
                const char*      aPrefName,
                const PRUnichar* aDialogMessageName,
                const PRUnichar* aShowAgainName,
                nsIPrefBranch*   aPrefBranch,
-               nsIStringBundle* aStringBundle)
+               nsIStringBundle* aStringBundle,
+               PRUint32         aBucket)
   : mPrompt(aPrompt), mPrefName(aPrefName),
     mDialogMessageName(aDialogMessageName),
     mShowAgainName(aShowAgainName), mPrefBranch(aPrefBranch),
-    mStringBundle(aStringBundle) {}
+    mStringBundle(aStringBundle),
+    mBucket(aBucket) {}
   NS_IMETHOD Run();
 
 protected:
   nsCOMPtr<nsIPrompt>       mPrompt;
   nsCString                 mPrefName;
   nsString                  mDialogMessageName;
   nsString                  mShowAgainName;
   nsCOMPtr<nsIPrefBranch>   mPrefBranch;
   nsCOMPtr<nsIStringBundle> mStringBundle;
+  PRUint32                  mBucket;
 };
 
 NS_IMETHODIMP
 nsAsyncAlert::Run()
 {
   nsresult rv;
 
   // Get user's preference for this alert
   bool prefValue;
   rv = mPrefBranch->GetBoolPref(mPrefName.get(), &prefValue);
   if (NS_FAILED(rv)) prefValue = true;
 
   // Stop if alert is not requested
   if (!prefValue) return NS_OK;
 
+  mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, mBucket);
   // Check for a show-once pref for this dialog.
   // If the show-once pref is set to true:
   //   - The default value of the "show every time" checkbox is unchecked
   //   - If the user checks the checkbox, we clear the show-once pref.
 
   nsCAutoString showOncePref(mPrefName);
   showOncePref += ".show_once";
 
@@ -184,64 +195,72 @@ nsAsyncAlert::Run()
 }
 
 
 nsresult
 nsSecurityWarningDialogs::AlertDialog(nsIInterfaceRequestor* aCtx,
                                       const char* aPrefName,
                                       const PRUnichar* aDialogMessageName,
                                       const PRUnichar* aShowAgainName,
-                                      bool aAsync)
+                                      bool aAsync,
+                                      const PRUint32 aBucket)
 {
   // Get Prompt to use
   nsCOMPtr<nsIPrompt> prompt = do_GetInterface(aCtx);
   if (!prompt) return NS_ERROR_FAILURE;
 
   nsRefPtr<nsAsyncAlert> alert = new nsAsyncAlert(prompt,
                                                   aPrefName,
                                                   aDialogMessageName,
                                                   aShowAgainName,
                                                   mPrefBranch,
-                                                  mStringBundle);
+                                                  mStringBundle,
+                                                  aBucket);
+
   NS_ENSURE_TRUE(alert, NS_ERROR_OUT_OF_MEMORY);
   return aAsync ? NS_DispatchToCurrentThread(alert) : alert->Run();
 }
 
 
 
 NS_IMETHODIMP 
 nsSecurityWarningDialogs::ConfirmPostToInsecure(nsIInterfaceRequestor *ctx, bool* _result)
 {
   nsresult rv;
 
+  // The Telemetry clickthrough constant is 1 more than the constant for the dialog.
   rv = ConfirmDialog(ctx, INSECURE_SUBMIT_PREF,
                      NS_LITERAL_STRING("PostToInsecureFromInsecureMessage").get(),
                      NS_LITERAL_STRING("PostToInsecureFromInsecureShowAgain").get(),
+                     nsISecurityUITelemetry::WARNING_CONFIRM_POST_TO_INSECURE_FROM_INSECURE,
                      _result);
 
   return rv;
 }
 
 NS_IMETHODIMP 
 nsSecurityWarningDialogs::ConfirmPostToInsecureFromSecure(nsIInterfaceRequestor *ctx, bool* _result)
 {
   nsresult rv;
 
+  // The Telemetry clickthrough constant is 1 more than the constant for the dialog.
   rv = ConfirmDialog(ctx, nullptr, // No preference for this one - it's too important
                      NS_LITERAL_STRING("PostToInsecureFromSecureMessage").get(),
-                     nullptr, 
+                     nullptr,
+                     nsISecurityUITelemetry::WARNING_CONFIRM_POST_TO_INSECURE_FROM_SECURE,
                      _result);
 
   return rv;
 }
 
 nsresult
 nsSecurityWarningDialogs::ConfirmDialog(nsIInterfaceRequestor *ctx, const char *prefName,
                             const PRUnichar *messageName, 
                             const PRUnichar *showAgainName, 
+                            const PRUint32 aBucket,
                             bool* _result)
 {
   nsresult rv;
 
   // Get user's preference for this alert
   // prefName, showAgainName are null if there is no preference for this dialog
   bool prefValue = true;
   
@@ -251,16 +270,18 @@ nsSecurityWarningDialogs::ConfirmDialog(
   }
   
   // Stop if confirm is not requested
   if (!prefValue) {
     *_result = true;
     return NS_OK;
   }
   
+  MOZ_ASSERT(NS_IsMainThread());
+  mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, aBucket);
   // See AlertDialog() for a description of how showOnce works.
   nsCAutoString showOncePref(prefName);
   showOncePref += ".show_once";
 
   bool showOnce = false;
   mPrefBranch->GetBoolPref(showOncePref.get(), &showOnce);
 
   if (showOnce)
@@ -307,16 +328,21 @@ nsSecurityWarningDialogs::ConfirmDialog(
                           nullptr,
                           alertMe, 
                           &prefValue, 
                           &buttonPressed);
 
   if (NS_FAILED(rv)) return rv;
 
   *_result = (buttonPressed != 1);
+  if (*_result) {
+  // For confirmation dialogs, the clickthrough constant is 1 more
+  // than the constant for the dialog.
+  mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, aBucket + 1);
+  }
 
   if (!prefValue && prefName != nullptr) {
     mPrefBranch->SetBoolPref(prefName, false);
   } else if (prefValue && showOnce) {
     mPrefBranch->SetBoolPref(showOncePref.get(), false);
   }
 
   return rv;
--- a/security/manager/boot/src/nsSecurityWarningDialogs.h
+++ b/security/manager/boot/src/nsSecurityWarningDialogs.h
@@ -22,20 +22,21 @@ public:
   virtual ~nsSecurityWarningDialogs();
 
   nsresult Init();
 
 protected:
   nsresult AlertDialog(nsIInterfaceRequestor *ctx, const char *prefName,
                    const PRUnichar *messageName,
                    const PRUnichar *showAgainName,
-                   bool aAsync);
+                   bool aAsync, const PRUint32 aBucket);
   nsresult ConfirmDialog(nsIInterfaceRequestor *ctx, const char *prefName,
                    const PRUnichar *messageName, 
-                   const PRUnichar *showAgainName, bool* _result);
+                   const PRUnichar *showAgainName, const PRUint32 aBucket,
+                   bool* _result);
   nsCOMPtr<nsIStringBundle> mStringBundle;
   nsCOMPtr<nsIPrefBranch> mPrefBranch;
 };
 
 #define NS_SECURITYWARNINGDIALOGS_CID \
  { /* 8d995d4f-adcc-4159-b7f1-e94af72eeb88 */       \
   0x8d995d4f, 0xadcc, 0x4159,                       \
  {0xb7, 0xf1, 0xe9, 0x4a, 0xf7, 0x2e, 0xeb, 0x88} }
--- a/security/manager/pki/resources/content/exceptionDialog.js
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -6,16 +6,18 @@
 var gDialog;
 var gBundleBrand;
 var gPKIBundle;
 var gSSLStatus;
 var gCert;
 var gChecking;
 var gBroken;
 var gNeedReset;
+var gSecHistogram;
+var gNsISecTel;
 
 function badCertListener() {}
 badCertListener.prototype = {
   getInterface: function (aIID) {
     return this.QueryInterface(aIID);
   },
   QueryInterface: function(aIID) {
     if (aIID.equals(Components.interfaces.nsIBadCertListener2) ||
@@ -37,16 +39,20 @@ badCertListener.prototype = {
   }
 }
 
 function initExceptionDialog() {
   gNeedReset = false;
   gDialog = document.documentElement;
   gBundleBrand = srGetStrBundle("chrome://branding/locale/brand.properties");
   gPKIBundle = srGetStrBundle("chrome://pippki/locale/pippki.properties");
+  gSecHistogram = Components.classes["@mozilla.org/base/telemetry;1"].
+                    getService(Components.interfaces.nsITelemetry).
+                    getHistogramById("SECURITY_UI");
+  gNsISecTel = Components.interfaces.nsISecurityUITelemetry;
 
   var brandName = gBundleBrand.GetStringFromName("brandShortName");
   
   setText("warningText", gPKIBundle.formatStringFromName("addExceptionBrandedWarning2",
                                                          [brandName], 1));
   gDialog.getButton("extra1").disabled = true;
   
   var args = window.arguments;
@@ -198,60 +204,65 @@ function handleTextChange() {
 }
 
 function updateCertStatus() {
   var shortDesc, longDesc;
   var shortDesc2, longDesc2;
   var shortDesc3, longDesc3;
   var use2 = false;
   var use3 = false;
+  let bucketId = gNsISecTel.WARNING_BAD_CERT_ADD_EXCEPTION_BASE;
   if(gCert) {
     if(gBroken) { 
       var mms = "addExceptionDomainMismatchShort";
       var mml = "addExceptionDomainMismatchLong";
       var exs = "addExceptionExpiredShort";
       var exl = "addExceptionExpiredLong";
       var uts = "addExceptionUnverifiedOrBadSignatureShort";
       var utl = "addExceptionUnverifiedOrBadSignatureLong";
       var use1 = false;
       if (gSSLStatus.isDomainMismatch) {
+        bucketId += gNsISecTel.WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_DOMAIN;
         use1 = true;
         shortDesc = mms;
         longDesc  = mml;
       }
       if (gSSLStatus.isNotValidAtThisTime) {
+        bucketId += gNsISecTel.WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_TIME;
         if (!use1) {
           use1 = true;
           shortDesc = exs;
           longDesc  = exl;
         }
         else {
           use2 = true;
           shortDesc2 = exs;
           longDesc2  = exl;
         }
       }
       if (gSSLStatus.isUntrusted) {
+        bucketId += gNsISecTel.WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_UNTRUSTED;
         if (!use1) {
           use1 = true;
           shortDesc = uts;
           longDesc  = utl;
         }
         else if (!use2) {
           use2 = true;
           shortDesc2 = uts;
           longDesc2  = utl;
         } 
         else {
           use3 = true;
           shortDesc3 = uts;
           longDesc3  = utl;
         }
       }
-      
+      gSecHistogram.add(bucketId);
+
       // In these cases, we do want to enable the "Add Exception" button
       gDialog.getButton("extra1").disabled = false;
 
       // If the Private Browsing service is available and the mode is active,
       // don't store permanent exceptions, since they would persist after
       // private browsing mode was disabled.
       var inPrivateBrowsing = inPrivateBrowsingMode();
       var pe = document.getElementById("permanent");
@@ -312,41 +323,52 @@ function updateCertStatus() {
 
   gNeedReset = true;
 }
 
 /**
  * Handle user request to display certificate details
  */
 function viewCertButtonClick() {
+  gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_CLICK_VIEW_CERT);
   if (gCert)
     viewCertHelper(this, gCert);
     
 }
 
 /**
  * Handle user request to add an exception for the specified cert
  */
 function addException() {
   if(!gCert || !gSSLStatus)
     return;
 
   var overrideService = Components.classes["@mozilla.org/security/certoverride;1"]
                                   .getService(Components.interfaces.nsICertOverrideService);
   var flags = 0;
-  if(gSSLStatus.isUntrusted)
+  let confirmBucketId = gNsISecTel.WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_BASE;
+  if (gSSLStatus.isUntrusted) {
     flags |= overrideService.ERROR_UNTRUSTED;
-  if(gSSLStatus.isDomainMismatch)
+    confirmBucketId += gNsISecTel.WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED;
+  }
+  if (gSSLStatus.isDomainMismatch) {
     flags |= overrideService.ERROR_MISMATCH;
-  if(gSSLStatus.isNotValidAtThisTime)
+    confirmBucketId += gNsISecTel.WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN;
+  }
+  if (gSSLStatus.isNotValidAtThisTime) {
     flags |= overrideService.ERROR_TIME;
+    confirmBucketId += gNsISecTel.WARNING_BAD_CERT_CONFIRM_ADD_EXCEPTION_FLAG_TIME;
+  }
   
   var permanentCheckbox = document.getElementById("permanent");
   var shouldStorePermanently = permanentCheckbox.checked && !inPrivateBrowsingMode();
+  if(!permanentCheckbox.checked)
+   gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_DONT_REMEMBER_EXCEPTION);
 
+  gSecHistogram.add(confirmBucketId);
   var uri = getURI();
   overrideService.rememberValidityOverride(
     uri.asciiHost, uri.port,
     gCert,
     flags,
     !shouldStorePermanently);
   
   var args = window.arguments;
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -513,11 +513,16 @@ HISTOGRAM(PANORAMA_MEDIAN_TABS_IN_GROUPS
  * Native Fennec Telemetry
  */
 #if defined(ANDROID)
 HISTOGRAM(BROWSERPROVIDER_XUL_IMPORT_TIME, 20, 600000, 20, EXPONENTIAL, "Time for the initial conversion of a XUL places database (ms)")
 HISTOGRAM(BROWSERPROVIDER_XUL_IMPORT_BOOKMARKS, 1, 50000, 20, EXPONENTIAL, "Number of bookmarks in the original XUL places database")
 HISTOGRAM(BROWSERPROVIDER_XUL_IMPORT_HISTORY, 1, 1000000, 20, EXPONENTIAL, "Number of history entries in the original XUL places database")
 #endif
 
+/**
+ * Security UI Telemetry
+ */
+HISTOGRAM_ENUMERATED_VALUES(SECURITY_UI, 100, "Security UI Telemetry")
+
 #undef HISTOGRAM_ENUMERATED_VALUES
 #undef HISTOGRAM_BOOLEAN
 #undef HISTOGRAM_FLAG
--- a/toolkit/mozapps/extensions/amWebInstallListener.js
+++ b/toolkit/mozapps/extensions/amWebInstallListener.js
@@ -168,16 +168,20 @@ Installer.prototype = {
     }
 
     let args = {};
     args.url = this.url;
     args.installs = this.downloads;
     args.wrappedJSObject = args;
 
     try {
+      Cc["@mozilla.org/base/telemetry;1"].
+            getService(Ci.nsITelemetry).
+            getHistogramById("SECURITY_UI").
+            add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
       Services.ww.openWindow(this.window, URI_XPINSTALL_DIALOG,
                              null, "chrome,modal,centerscreen", args);
     } catch (e) {
       this.downloads.forEach(function(aInstall) {
         aInstall.removeListener(this);
         // Cancel the installs, as currently there is no way to make them fail
         // from here.
         aInstall.cancel();
--- a/toolkit/mozapps/extensions/content/xpinstallConfirm.js
+++ b/toolkit/mozapps/extensions/content/xpinstallConfirm.js
@@ -133,16 +133,20 @@ XPInstallConfirm.init = function ()
     setWidgetsAfterFocus();
   }
   else
     okButton.label = bundle.getString("installButtonLabel");
 }
 
 XPInstallConfirm.onOK = function ()
 {
+  Components.classes["@mozilla.org/base/telemetry;1"].
+    getService(Components.interfaces.nsITelemetry).
+    getHistogramById("SECURITY_UI").
+    add(Components.interfaces.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
   args.installs.forEach(function(install) {
     install.install();
   });
   return true;
 }
 
 XPInstallConfirm.onCancel = function ()
 {