Bug 1219030 - Collect notification management telemetry. r=wchen,MattN; p=ally

This commit is contained in:
Kit Cambridge 2015-11-13 21:22:59 -08:00
parent e20d04675c
commit c8fab6a7a3
8 changed files with 353 additions and 23 deletions

View File

@ -132,6 +132,9 @@ var gContentPane = {
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
"resizable=yes", params);
Services.telemetry
.getHistogramById("WEB_NOTIFICATION_EXCEPTIONS_OPENED").add();
},
@ -171,7 +174,7 @@ var gContentPane = {
},
/**
*
*
*/
_selectDefaultLanguageGroup: function (aLanguageGroup, aIsSerif)
{
@ -243,7 +246,7 @@ var gContentPane = {
/**
* Displays the fonts dialog, where web page font names and sizes can be
* configured.
*/
*/
configureFonts: function ()
{
gSubDialog.open("chrome://browser/content/preferences/fonts.xul", "resizable=no");

View File

@ -10,6 +10,7 @@
#include "mozilla/OwningNonNull.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h"
#include "mozilla/unused.h"
#include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
@ -21,8 +22,10 @@
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
#include "nsAlertsUtils.h"
#include "nsContentPermissionHelper.h"
#include "nsContentUtils.h"
#include "nsCRTGlue.h"
#include "nsDOMJSUtils.h"
#include "nsGlobalWindow.h"
#include "nsIAlertsService.h"
@ -32,8 +35,10 @@
#include "nsILoadContext.h"
#include "nsINotificationStorage.h"
#include "nsIPermissionManager.h"
#include "nsIPermission.h"
#include "nsIScriptSecurityManager.h"
#include "nsIServiceWorkerManager.h"
#include "nsISimpleEnumerator.h"
#include "nsIUUIDGenerator.h"
#include "nsIXPConnect.h"
#include "nsNetUtil.h"
@ -659,6 +664,195 @@ NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
aTypes);
}
NS_IMPL_ISUPPORTS(NotificationTelemetryService, nsIObserver)
NotificationTelemetryService::NotificationTelemetryService()
: mDNDRecorded(false)
{}
NotificationTelemetryService::~NotificationTelemetryService()
{
Unused << NS_WARN_IF(NS_FAILED(RemovePermissionChangeObserver()));
}
/* static */ already_AddRefed<NotificationTelemetryService>
NotificationTelemetryService::GetInstance()
{
nsCOMPtr<nsISupports> telemetrySupports =
do_GetService(NOTIFICATIONTELEMETRYSERVICE_CONTRACTID);
if (!telemetrySupports) {
return nullptr;
}
RefPtr<NotificationTelemetryService> telemetry =
static_cast<NotificationTelemetryService*>(telemetrySupports.get());
return telemetry.forget();
}
nsresult
NotificationTelemetryService::Init()
{
nsresult rv = AddPermissionChangeObserver();
NS_ENSURE_SUCCESS(rv, rv);
RecordPermissions();
return NS_OK;
}
nsresult
NotificationTelemetryService::RemovePermissionChangeObserver()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_OUT_OF_MEMORY;
}
return obs->RemoveObserver(this, "perm-changed");
}
nsresult
NotificationTelemetryService::AddPermissionChangeObserver()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_OUT_OF_MEMORY;
}
return obs->AddObserver(this, "perm-changed", false);
}
void
NotificationTelemetryService::RecordPermissions()
{
if (!Telemetry::CanRecordBase() || !Telemetry::CanRecordExtended()) {
return;
}
nsCOMPtr<nsIPermissionManager> permissionManager =
services::GetPermissionManager();
if (!permissionManager) {
return;
}
nsCOMPtr<nsISimpleEnumerator> enumerator;
nsresult rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
for (;;) {
bool hasMoreElements;
nsresult rv = enumerator->HasMoreElements(&hasMoreElements);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (!hasMoreElements) {
break;
}
nsCOMPtr<nsISupports> supportsPermission;
rv = enumerator->GetNext(getter_AddRefs(supportsPermission));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
uint32_t capability;
if (!GetNotificationPermission(supportsPermission, &capability)) {
continue;
}
if (capability == nsIPermissionManager::DENY_ACTION) {
Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_PERMISSIONS, 0);
} else if (capability == nsIPermissionManager::ALLOW_ACTION) {
Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_PERMISSIONS, 1);
}
}
}
bool
NotificationTelemetryService::GetNotificationPermission(nsISupports* aSupports,
uint32_t* aCapability)
{
nsCOMPtr<nsIPermission> permission = do_QueryInterface(aSupports);
if (!permission) {
return false;
}
nsAutoCString type;
permission->GetType(type);
if (!type.Equals("desktop-notification")) {
return false;
}
permission->GetCapability(aCapability);
return true;
}
void
NotificationTelemetryService::RecordDNDSupported()
{
if (mDNDRecorded) {
return;
}
nsCOMPtr<nsIAlertsService> alertService =
do_GetService(NS_ALERTSERVICE_CONTRACTID);
if (!alertService) {
return;
}
nsCOMPtr<nsIAlertsDoNotDisturb> alertServiceDND =
do_QueryInterface(alertService);
if (!alertServiceDND) {
return;
}
mDNDRecorded = true;
bool isEnabled;
nsresult rv = alertServiceDND->GetManualDoNotDisturb(&isEnabled);
if (NS_FAILED(rv)) {
return;
}
Telemetry::Accumulate(
Telemetry::ALERTS_SERVICE_DND_SUPPORTED_FLAG, true);
}
nsresult
NotificationTelemetryService::RecordSender(nsIPrincipal* aPrincipal)
{
if (!Telemetry::CanRecordBase() || !Telemetry::CanRecordExtended() ||
!nsAlertsUtils::IsActionablePrincipal(aPrincipal)) {
return NS_OK;
}
nsAutoString origin;
nsresult rv = Notification::GetOrigin(aPrincipal, origin);
if (NS_FAILED(rv)) {
return rv;
}
if (!mOrigins.Contains(origin)) {
mOrigins.PutEntry(origin);
Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_SENDERS, 1);
}
return NS_OK;
}
NS_IMETHODIMP
NotificationTelemetryService::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
uint32_t capability;
if (strcmp("perm-changed", aTopic) ||
!NS_strcmp(MOZ_UTF16("cleared"), aData) ||
!GetNotificationPermission(aSubject, &capability)) {
return NS_OK;
}
if (!NS_strcmp(MOZ_UTF16("deleted"), aData)) {
if (capability == nsIPermissionManager::DENY_ACTION) {
Telemetry::Accumulate(
Telemetry::WEB_NOTIFICATION_PERMISSION_REMOVED, 0);
} else if (capability == nsIPermissionManager::ALLOW_ACTION) {
Telemetry::Accumulate(
Telemetry::WEB_NOTIFICATION_PERMISSION_REMOVED, 1);
}
}
return NS_OK;
}
// Observer that the alert service calls to do common tasks and/or dispatch to the
// specific observer for the context e.g. main thread, worker, or service worker.
class NotificationObserver final : public nsIObserver
@ -666,11 +860,14 @@ class NotificationObserver final : public nsIObserver
public:
nsCOMPtr<nsIObserver> mObserver;
nsCOMPtr<nsIPrincipal> mPrincipal;
bool mInPrivateBrowsing;
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NotificationObserver(nsIObserver* aObserver, nsIPrincipal* aPrincipal)
: mObserver(aObserver), mPrincipal(aPrincipal)
NotificationObserver(nsIObserver* aObserver, nsIPrincipal* aPrincipal,
bool aInPrivateBrowsing)
: mObserver(aObserver), mPrincipal(aPrincipal),
mInPrivateBrowsing(aInPrivateBrowsing)
{
AssertIsOnMainThread();
MOZ_ASSERT(mObserver);
@ -1185,6 +1382,17 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
} else if (!strcmp("alertshow", aTopic) ||
!strcmp("alertfinished", aTopic)) {
RefPtr<NotificationTelemetryService> telemetry =
NotificationTelemetryService::GetInstance();
if (telemetry) {
// Record whether "do not disturb" is supported after the first
// notification, to account for falling back to XUL alerts.
telemetry->RecordDNDSupported();
if (!mInPrivateBrowsing) {
// Ignore senders in private windows.
Unused << NS_WARN_IF(NS_FAILED(telemetry->RecordSender(mPrincipal)));
}
}
Unused << NS_WARN_IF(NS_FAILED(AdjustPushQuota(aTopic)));
if (!strcmp("alertshow", aTopic)) {
@ -1425,6 +1633,30 @@ ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
return NS_OK;
}
bool
Notification::IsInPrivateBrowsing()
{
nsIDocument* doc = mWorkerPrivate ? mWorkerPrivate->GetDocument()
: GetOwner()->GetExtantDoc();
if (doc) {
nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
return loadContext && loadContext->UsePrivateBrowsing();
}
if (mWorkerPrivate) {
// Not all workers may have a document, but with Bug 1107516 fixed, they
// should all have a loadcontext.
nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(nullptr, loadGroup, NS_GET_IID(nsILoadContext),
getter_AddRefs(loadContext));
return loadContext && loadContext->UsePrivateBrowsing();
}
//XXXnsm Should this default to true?
return false;
}
void
Notification::ShowInternal()
{
@ -1499,7 +1731,8 @@ Notification::ShowInternal()
}
MOZ_ASSERT(observer);
nsCOMPtr<nsIObserver> alertObserver = new NotificationObserver(observer,
GetPrincipal());
GetPrincipal(),
IsInPrivateBrowsing());
#ifdef MOZ_B2G
@ -1550,22 +1783,7 @@ Notification::ShowInternal()
// nsIObserver. Thus the cookie must be unique to differentiate observers.
nsString uniqueCookie = NS_LITERAL_STRING("notification:");
uniqueCookie.AppendInt(sCount++);
//XXXnsm Should this default to true?
bool inPrivateBrowsing = false;
nsIDocument* doc = mWorkerPrivate ? mWorkerPrivate->GetDocument()
: GetOwner()->GetExtantDoc();
if (doc) {
nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
} else if (mWorkerPrivate) {
// Not all workers may have a document, but with Bug 1107516 fixed, they
// should all have a loadcontext.
nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(nullptr, loadGroup, NS_GET_IID(nsILoadContext),
getter_AddRefs(loadContext));
inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
}
bool inPrivateBrowsing = IsInPrivateBrowsing();
nsAutoString alertName;
GetAlertName(alertName);

View File

@ -15,6 +15,11 @@
#include "nsIObserver.h"
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"
#include "nsTHashtable.h"
#define NOTIFICATIONTELEMETRYSERVICE_CONTRACTID \
"@mozilla.org/notificationTelemetryService;1"
class nsIPrincipal;
class nsIVariant;
@ -44,6 +49,35 @@ public:
Notify(JSContext* aCx, workers::Status aStatus) override;
};
// Records telemetry probes at application startup, when a notification is
// shown, and when the notification permission is revoked for a site.
class NotificationTelemetryService final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NotificationTelemetryService();
static already_AddRefed<NotificationTelemetryService> GetInstance();
nsresult Init();
void RecordDNDSupported();
void RecordPermissions();
nsresult RecordSender(nsIPrincipal* aPrincipal);
private:
virtual ~NotificationTelemetryService();
nsresult AddPermissionChangeObserver();
nsresult RemovePermissionChangeObserver();
bool GetNotificationPermission(nsISupports* aSupports,
uint32_t* aCapability);
bool mDNDRecorded;
nsTHashtable<nsStringHashKey> mOrigins;
};
/*
* Notifications on workers introduce some lifetime issues. The property we
@ -107,6 +141,7 @@ class Notification : public DOMEventTargetHelper
friend class ServiceWorkerNotificationObserver;
friend class WorkerGetRunnable;
friend class WorkerNotificationObserver;
friend class NotificationTelemetryService;
public:
IMPL_EVENT_HANDLER(click)
@ -290,6 +325,7 @@ protected:
const nsAString& aTitle,
const NotificationOptions& aOptions);
bool IsInPrivateBrowsing();
void ShowInternal();
void CloseInternal();

View File

@ -82,4 +82,7 @@
#define SERVICEWORKERMANAGER_CID \
{ 0xc74bde32, 0xbcc7, 0x4840, { 0x84, 0x30, 0xc7, 0x33, 0x35, 0x1b, 0x21, 0x2a } }
#define NOTIFICATIONTELEMETRYSERVICE_CID \
{ 0x5995b782, 0x6a0e, 0x4066, { 0xaa, 0xc5, 0x27, 0x6f, 0x0a, 0x9a, 0xd8, 0xcf } }
#endif /* nsLayoutCID_h__ */

View File

@ -87,6 +87,7 @@
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/dom/workers/ServiceWorkerManager.h"
#include "mozilla/dom/workers/WorkerDebuggerManager.h"
#include "mozilla/dom/Notification.h"
#include "mozilla/OSFileConstants.h"
#include "mozilla/Services.h"
@ -273,6 +274,7 @@ using mozilla::dom::UDPSocketChild;
using mozilla::dom::time::TimeService;
using mozilla::net::StreamingProtocolControllerService;
using mozilla::gmp::GeckoMediaPluginService;
using mozilla::dom::NotificationTelemetryService;
// Transformiix
/* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
@ -400,6 +402,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(InputPortData)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPresentationService,
NS_CreatePresentationService)
NS_GENERIC_FACTORY_CONSTRUCTOR(PresentationSessionTransport)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NotificationTelemetryService, Init)
//-----------------------------------------------------------------------------
static bool gInitialized = false;
@ -762,6 +765,7 @@ NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID);
NS_DEFINE_NAMED_CID(QUOTAMANAGER_SERVICE_CID);
NS_DEFINE_NAMED_CID(SERVICEWORKERMANAGER_CID);
NS_DEFINE_NAMED_CID(NOTIFICATIONTELEMETRYSERVICE_CID);
NS_DEFINE_NAMED_CID(WORKERDEBUGGERMANAGER_CID);
#ifdef MOZ_WIDGET_GONK
NS_DEFINE_NAMED_CID(SYSTEMWORKERMANAGER_CID);
@ -1070,6 +1074,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
{ &kDOMREQUEST_SERVICE_CID, false, nullptr, DOMRequestServiceConstructor },
{ &kQUOTAMANAGER_SERVICE_CID, false, nullptr, QuotaManagerServiceConstructor },
{ &kSERVICEWORKERMANAGER_CID, false, nullptr, ServiceWorkerManagerConstructor },
{ &kNOTIFICATIONTELEMETRYSERVICE_CID, false, nullptr, NotificationTelemetryServiceConstructor },
{ &kWORKERDEBUGGERMANAGER_CID, true, nullptr, WorkerDebuggerManagerConstructor },
#ifdef MOZ_WIDGET_GONK
{ &kSYSTEMWORKERMANAGER_CID, true, nullptr, SystemWorkerManagerConstructor },
@ -1238,6 +1243,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
{ DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID },
{ QUOTAMANAGER_SERVICE_CONTRACTID, &kQUOTAMANAGER_SERVICE_CID },
{ SERVICEWORKERMANAGER_CONTRACTID, &kSERVICEWORKERMANAGER_CID },
{ NOTIFICATIONTELEMETRYSERVICE_CONTRACTID, &kNOTIFICATIONTELEMETRYSERVICE_CID },
{ WORKERDEBUGGERMANAGER_CONTRACTID, &kWORKERDEBUGGERMANAGER_CID },
#ifdef MOZ_WIDGET_GONK
{ SYSTEMWORKERMANAGER_CONTRACTID, &kSYSTEMWORKERMANAGER_CID },
@ -1352,6 +1358,7 @@ static const mozilla::Module::CategoryEntry kLayoutCategories[] = {
#endif
{ "profile-after-change", "PresentationDeviceManager", PRESENTATION_DEVICE_MANAGER_CONTRACTID },
{ "profile-after-change", "PresentationService", PRESENTATION_SERVICE_CONTRACTID },
{ "profile-after-change", "Notification Telemetry Service", NOTIFICATIONTELEMETRYSERVICE_CONTRACTID },
{ nullptr }
};

View File

@ -5,6 +5,7 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/Telemetry.h"
#include "nsXULAppAPI.h"
#include "nsAlertsService.h"
@ -177,15 +178,19 @@ NS_IMETHODIMP nsAlertsService::SetManualDoNotDisturb(bool aDoNotDisturb)
#else
// Try the system notification service.
nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID));
nsresult rv;
if (sysAlerts) {
nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID));
if (!alertsDND) {
return NS_ERROR_NOT_IMPLEMENTED;
}
return alertsDND->SetManualDoNotDisturb(aDoNotDisturb);
rv = alertsDND->SetManualDoNotDisturb(aDoNotDisturb);
} else {
rv = mXULAlerts.SetManualDoNotDisturb(aDoNotDisturb);
}
return mXULAlerts.SetManualDoNotDisturb(aDoNotDisturb);
Telemetry::Accumulate(Telemetry::ALERTS_SERVICE_DND_ENABLED, 1);
return rv;
#endif
}

View File

@ -7,6 +7,8 @@
#include "nsAutoPtr.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/dom/Notification.h"
#include "mozilla/unused.h"
#include "nsIServiceManager.h"
#include "nsAlertsUtils.h"
#include "nsISupportsArray.h"
@ -15,6 +17,7 @@
#include "nsIWindowWatcher.h"
using namespace mozilla;
using mozilla::dom::NotificationTelemetryService;
#define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul"
@ -49,6 +52,17 @@ nsXULAlerts::ShowAlertNotification(const nsAString& aImageUrl, const nsAString&
bool aInPrivateBrowsing)
{
if (mDoNotDisturb) {
if (!aInPrivateBrowsing) {
RefPtr<NotificationTelemetryService> telemetry =
NotificationTelemetryService::GetInstance();
if (telemetry) {
// Record the number of unique senders for XUL alerts. The OS X and
// libnotify backends will fire `alertshow` even if "do not disturb"
// is enabled. In that case, `NotificationObserver` will record the
// sender.
Unused << NS_WARN_IF(NS_FAILED(telemetry->RecordSender(aPrincipal)));
}
}
return NS_OK;
}

View File

@ -10233,6 +10233,50 @@
"bug_numbers": [1188391],
"description": "The number of ICE connections which immediately failed (0) vs. reached at least checking state (1)."
},
"ALERTS_SERVICE_DND_ENABLED": {
"alert_emails": ["firefox-dev@mozilla.org"],
"bug_numbers": [1219030],
"expires_in_version": "50",
"kind": "boolean",
"description": "XUL-only: whether the user has toggled do not disturb."
},
"ALERTS_SERVICE_DND_SUPPORTED_FLAG": {
"alert_emails": ["firefox-dev@mozilla.org"],
"bug_numbers": [1219030],
"expires_in_version": "50",
"kind": "flag",
"description": "Whether the do not disturb option is supported. True if the browser uses XUL alerts."
},
"WEB_NOTIFICATION_EXCEPTIONS_OPENED": {
"alert_emails": ["firefox-dev@mozilla.org"],
"bug_numbers": [1219030],
"expires_in_version": "50",
"kind": "count",
"description": "Number of times the Notification Permissions dialog has been opened."
},
"WEB_NOTIFICATION_PERMISSIONS": {
"alert_emails": ["firefox-dev@mozilla.org"],
"bug_numbers": [1219030],
"expires_in_version": "50",
"kind": "enumerated",
"n_values": 10,
"description": "Number of origins with the web notifications permission (0 = denied, 1 = allowed)."
},
"WEB_NOTIFICATION_PERMISSION_REMOVED": {
"alert_emails": ["firefox-dev@mozilla.org"],
"bug_numbers": [1219030],
"expires_in_version": "50",
"kind": "enumerated",
"n_values": 10,
"description": "Number of removed web notifications permissions (0 = remove deny, 1 = remove allow)."
},
"WEB_NOTIFICATION_SENDERS": {
"alert_emails": ["firefox-dev@mozilla.org"],
"bug_numbers": [1219030],
"expires_in_version": "50",
"kind": "count",
"description": "Number of origins that have shown a web notification. Excludes system alerts like update reminders and add-ons."
},
"YOUTUBE_REWRITABLE_EMBED_SEEN": {
"alert_emails": ["cpeterson@mozilla.com"],
"expires_in_version": "48",