Bug 767676 - Implement Security UI Telemetry. r=honzab,bsmith,felipc,dtownsend

This commit is contained in:
Devdatta Akhawe 2012-08-02 18:51:17 -07:00
parent 794c9b5215
commit 7aa2770d79
11 changed files with 219 additions and 18 deletions

View File

@ -77,14 +77,17 @@ const gXPInstallObserver = {
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;

View File

@ -2519,9 +2519,13 @@ let BrowserOnClick = {
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 {
@ -2545,21 +2549,37 @@ let BrowserOnClick = {
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;
@ -2568,6 +2588,10 @@ let BrowserOnClick = {
// 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.
@ -2589,6 +2613,7 @@ let BrowserOnClick = {
break;
case "ignoreWarningButton":
secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
this.ignoreWarningButton(isMalware);
break;
}

View File

@ -192,6 +192,9 @@
#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);
@ -4040,8 +4043,16 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
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)) {
@ -4054,6 +4065,10 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
"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");
}
@ -4071,10 +4086,19 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
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 {

View File

@ -19,6 +19,7 @@ SDK_XPIDLSRCS = \
XPIDLSRCS = \
nsISSLStatusProvider.idl \
nsIBufEntropyCollector.idl \
nsISecurityUITelemetry.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -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
};

View File

@ -17,6 +17,9 @@
#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"
@ -60,7 +63,8 @@ nsSecurityWarningDialogs::ConfirmEnteringSecure(nsIInterfaceRequestor *ctx, bool
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;
@ -74,7 +78,8 @@ nsSecurityWarningDialogs::ConfirmEnteringWeak(nsIInterfaceRequestor *ctx, bool *
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;
@ -88,7 +93,8 @@ nsSecurityWarningDialogs::ConfirmLeavingSecure(nsIInterfaceRequestor *ctx, bool
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;
@ -103,7 +109,8 @@ nsSecurityWarningDialogs::ConfirmMixedMode(nsIInterfaceRequestor *ctx, bool *_re
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;
@ -117,11 +124,13 @@ public:
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:
@ -131,6 +140,7 @@ protected:
nsString mShowAgainName;
nsCOMPtr<nsIPrefBranch> mPrefBranch;
nsCOMPtr<nsIStringBundle> mStringBundle;
PRUint32 mBucket;
};
NS_IMETHODIMP
@ -146,6 +156,7 @@ nsAsyncAlert::Run()
// 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
@ -189,7 +200,8 @@ 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);
@ -200,7 +212,9 @@ nsSecurityWarningDialogs::AlertDialog(nsIInterfaceRequestor* aCtx,
aDialogMessageName,
aShowAgainName,
mPrefBranch,
mStringBundle);
mStringBundle,
aBucket);
NS_ENSURE_TRUE(alert, NS_ERROR_OUT_OF_MEMORY);
return aAsync ? NS_DispatchToCurrentThread(alert) : alert->Run();
}
@ -212,9 +226,11 @@ nsSecurityWarningDialogs::ConfirmPostToInsecure(nsIInterfaceRequestor *ctx, bool
{
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;
@ -225,9 +241,11 @@ nsSecurityWarningDialogs::ConfirmPostToInsecureFromSecure(nsIInterfaceRequestor
{
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;
@ -237,6 +255,7 @@ nsresult
nsSecurityWarningDialogs::ConfirmDialog(nsIInterfaceRequestor *ctx, const char *prefName,
const PRUnichar *messageName,
const PRUnichar *showAgainName,
const PRUint32 aBucket,
bool* _result)
{
nsresult rv;
@ -256,6 +275,8 @@ nsSecurityWarningDialogs::ConfirmDialog(nsIInterfaceRequestor *ctx, const char *
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";
@ -312,6 +333,11 @@ nsSecurityWarningDialogs::ConfirmDialog(nsIInterfaceRequestor *ctx, const char *
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);

View File

@ -27,10 +27,11 @@ 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;
};

View File

@ -11,6 +11,8 @@ var gCert;
var gChecking;
var gBroken;
var gNeedReset;
var gSecHistogram;
var gNsISecTel;
function badCertListener() {}
badCertListener.prototype = {
@ -42,6 +44,10 @@ function initExceptionDialog() {
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");
@ -203,6 +209,7 @@ function updateCertStatus() {
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";
@ -213,11 +220,13 @@ function updateCertStatus() {
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;
@ -230,6 +239,7 @@ function updateCertStatus() {
}
}
if (gSSLStatus.isUntrusted) {
bucketId += gNsISecTel.WARNING_BAD_CERT_ADD_EXCEPTION_FLAG_UNTRUSTED;
if (!use1) {
use1 = true;
shortDesc = uts;
@ -246,7 +256,8 @@ function updateCertStatus() {
longDesc3 = utl;
}
}
gSecHistogram.add(bucketId);
// In these cases, we do want to enable the "Add Exception" button
gDialog.getButton("extra1").disabled = false;
@ -317,6 +328,7 @@ function updateCertStatus() {
* Handle user request to display certificate details
*/
function viewCertButtonClick() {
gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_CLICK_VIEW_CERT);
if (gCert)
viewCertHelper(this, gCert);
@ -332,16 +344,26 @@ function addException() {
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,

View File

@ -518,6 +518,11 @@ HISTOGRAM(BROWSERPROVIDER_XUL_IMPORT_BOOKMARKS, 1, 50000, 20, EXPONENTIAL, "Numb
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

View File

@ -173,6 +173,10 @@ Installer.prototype = {
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) {

View File

@ -138,6 +138,10 @@ XPInstallConfirm.init = function ()
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();
});