Bug 1685801: Part 2 - Move site origin telemetry to separate module. r=mccr8

Differential Revision: https://phabricator.services.mozilla.com/D101482
This commit is contained in:
Kris Maglione 2021-01-28 18:48:25 +00:00
parent 4a273b246f
commit 59963809cd
7 changed files with 213 additions and 192 deletions

View File

@ -22,6 +22,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
NewTabPagePreloading: "resource:///modules/NewTabPagePreloading.jsm",
BrowserSearchTelemetry: "resource:///modules/BrowserSearchTelemetry.jsm",
BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
BrowserTelemetryUtils: "resource://gre/modules/BrowserTelemetryUtils.jsm",
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
CFRPageActions: "resource://activity-stream/lib/CFRPageActions.jsm",
@ -5723,7 +5724,7 @@ var TabsProgressListener = {
) {
if (recordLoadTelemetry) {
TelemetryStopwatch.finish(histogram, aBrowser);
BrowserUtils.recordSiteOriginTelemetry(browserWindows());
BrowserTelemetryUtils.recordSiteOriginTelemetry(browserWindows());
}
}
} else if (

View File

@ -19,7 +19,7 @@ const { XPCOMUtils } = ChromeUtils.import(
XPCOMUtils.defineLazyModuleGetters(this, {
AppConstants: "resource://gre/modules/AppConstants.jsm",
ClientID: "resource://gre/modules/ClientID.jsm",
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
BrowserTelemetryUtils: "resource://gre/modules/BrowserTelemetryUtils.jsm",
CustomizableUI: "resource:///modules/CustomizableUI.jsm",
PageActions: "resource:///modules/PageActions.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
@ -1208,7 +1208,7 @@ let BrowserUsageTelemetry = {
}
const { loadedTabCount } = getOpenTabsAndWinsCounts();
const siteOrigins = BrowserUtils.computeSiteOriginCount(
const siteOrigins = BrowserTelemetryUtils.computeSiteOriginCount(
Services.wm.getEnumerator("navigator:browser"),
false
);

View File

@ -134,9 +134,13 @@ add_task(async function() {
],
});
const { BrowserTelemetryUtils } = ChromeUtils.import(
"resource://gre/modules/BrowserTelemetryUtils.jsm"
);
// Make sure we actually record telemetry for our disqualifying origin
// count.
BrowserUtils.min_interval = 1;
BrowserTelemetryUtils.min_interval = 1;
let tabs = [];
@ -184,7 +188,7 @@ add_task(async function() {
Services.prefs.clearUserPref(PREF_QUALIFIED);
Services.prefs.clearUserPref(PREF_LAST_QUALIFIED);
Services.prefs.clearUserPref(PREF_LAST_DISQUALIFIED);
BrowserUtils._checkedInitialExperimentQualification = false;
BrowserTelemetryUtils._checkedInitialExperimentQualification = false;
info("Open a new tab to trigger the origin count code again");
tabs.push(await openTab(SITE_ORIGINS[0]));
@ -205,5 +209,5 @@ add_task(async function() {
// Clear the cached recording interval so it resets to the default
// value on the next call.
BrowserUtils.min_interval = null;
BrowserTelemetryUtils.min_interval = null;
});

View File

@ -29,7 +29,7 @@ XPCOMUtils.defineLazyServiceGetter(
);
XPCOMUtils.defineLazyModuleGetters(this, {
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
BrowserTelemetryUtils: "resource://gre/modules/BrowserTelemetryUtils.jsm",
HistogramStopwatch: "resource://gre/modules/GeckoViewTelemetry.jsm",
});
@ -435,7 +435,7 @@ class StateTracker extends Tracker {
success: aIsSuccess,
});
BrowserUtils.recordSiteOriginTelemetry(
BrowserTelemetryUtils.recordSiteOriginTelemetry(
Services.wm.getEnumerator("navigator:geckoview"),
true
);

View File

@ -0,0 +1,199 @@
/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["BrowserTelemetryUtils"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
// The maximum number of concurrently-loaded origins allowed in order to
// qualify for the Fission rollout experiment.
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentMaxOrigins",
"fission.experiment.max-origins.origin-cap",
30
);
// The length of the sliding window during which a user must stay below
// the max origin cap. If the last time a user passed the max origin cap
// fell outside of this window, they will requalify for the experiment.
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentSlidingWindowMS",
"fission.experiment.max-origins.sliding-window-ms",
7 * 24 * 60 * 60 * 1000
);
// The pref holding the current qaualification state of the user. If
// true, the user is currently qualified from the experiment.
const FISSION_EXPERIMENT_PREF_QUALIFIED =
"fission.experiment.max-origins.qualified";
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentQualified",
FISSION_EXPERIMENT_PREF_QUALIFIED,
false
);
// The pref holding the timestamp of the last time we saw an origin
// count below the cap while the user was not currently marked as
// qualified.
const FISSION_EXPERIMENT_PREF_LAST_QUALIFIED =
"fission.experiment.max-origins.last-qualified";
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentLastQualified",
FISSION_EXPERIMENT_PREF_LAST_QUALIFIED,
0
);
// The pref holding the timestamp of the last time we saw an origin
// count exceeding the cap.
const FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED =
"fission.experiment.max-origins.last-disqualified";
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentLastDisqualified",
FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED,
0
);
var BrowserTelemetryUtils = {
recordSiteOriginTelemetry(aWindows, aIsGeckoView) {
Services.tm.idleDispatchToMainThread(() => {
this._recordSiteOriginTelemetry(aWindows, aIsGeckoView);
});
},
computeSiteOriginCount(aWindows, aIsGeckoView) {
// Geckoview and Desktop work differently. On desktop, aBrowser objects
// holds an array of tabs which we can use to get the <browser> objects.
// In Geckoview, it is apps' responsibility to keep track of the tabs, so
// there isn't an easy way for us to get the tabs.
let tabs = [];
if (aIsGeckoView) {
// To get all active windows; Each tab has its own window
tabs = aWindows;
} else {
for (const win of aWindows) {
tabs = tabs.concat(win.gBrowser.tabs);
}
}
let topLevelBCs = [];
for (const tab of tabs) {
let browser;
if (aIsGeckoView) {
browser = tab.browser;
} else {
browser = tab.linkedBrowser;
}
if (browser.browsingContext) {
// This is the top level browsingContext
topLevelBCs.push(browser.browsingContext);
}
}
return CanonicalBrowsingContext.countSiteOrigins(topLevelBCs);
},
_recordSiteOriginTelemetry(aWindows, aIsGeckoView) {
let currentTime = Date.now();
// default is 5 minutes
if (!this.min_interval) {
this.min_interval = Services.prefs.getIntPref(
"telemetry.number_of_site_origin.min_interval",
300000
);
}
let originCount = this.computeSiteOriginCount(aWindows, aIsGeckoView);
let histogram = Services.telemetry.getHistogramById(
"FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_ALL_TABS"
);
// Discard the first load because most of the time the first load only has 1
// tab and 1 window open, so it is useless to report it.
if (!this._lastRecordSiteOrigin) {
this._lastRecordSiteOrigin = currentTime;
} else if (currentTime >= this._lastRecordSiteOrigin + this.min_interval) {
this._lastRecordSiteOrigin = currentTime;
histogram.add(originCount);
}
// Update the Fission experiment qualification state based on the
// current origin count:
// If we don't already have a last disqualification timestamp, look
// through the existing histogram values, and use the existing
// maximum value as the initial count. This will prevent us from
// enrolling users in the experiment if they have a history of
// exceeding our origin cap.
if (!this._checkedInitialExperimentQualification) {
this._checkedInitialExperimentQualification = true;
if (
!Services.prefs.prefHasUserValue(
FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED
)
) {
for (let [bucketStr, entryCount] of Object.entries(
histogram.snapshot().values
)) {
let bucket = Number(bucketStr);
if (bucket > originCount && entryCount > 0) {
originCount = bucket;
}
}
Services.prefs.setIntPref(FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED, 0);
}
}
let currentTimeSec = currentTime / 1000;
if (originCount < fissionExperimentMaxOrigins) {
let lastDisqualified = fissionExperimentLastDisqualified;
let lastQualified = fissionExperimentLastQualified;
// If the last time we saw a qualifying origin count was earlier
// than the last time we say a disqualifying count, update any
// existing last disqualified timestamp to just before now, on the
// basis that our origin count has probably just fallen below the
// cap.
if (lastDisqualified > 0 && lastQualified <= lastDisqualified) {
Services.prefs.setIntPref(
FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED,
currentTimeSec - 1
);
}
if (!fissionExperimentQualified) {
Services.prefs.setIntPref(
FISSION_EXPERIMENT_PREF_LAST_QUALIFIED,
currentTimeSec
);
// We have a qualifying origin count now. If the last time we were
// disqualified was prior to the start of our current sliding
// window, re-qualify the user.
if (
currentTimeSec - lastDisqualified >=
fissionExperimentSlidingWindowMS / 1000
) {
Services.prefs.setBoolPref(FISSION_EXPERIMENT_PREF_QUALIFIED, true);
}
}
} else {
Services.prefs.setIntPref(
FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED,
currentTimeSec
);
Services.prefs.setBoolPref(FISSION_EXPERIMENT_PREF_QUALIFIED, false);
}
},
};

View File

@ -17,55 +17,6 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/PlacesUtils.jsm"
);
// The maximum number of concurrently-loaded origins allowed in order to
// qualify for the Fission rollout experiment.
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentMaxOrigins",
"fission.experiment.max-origins.origin-cap",
30
);
// The length of the sliding window during which a user must stay below
// the max origin cap. If the last time a user passed the max origin cap
// fell outside of this window, they will requalify for the experiment.
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentSlidingWindowMS",
"fission.experiment.max-origins.sliding-window-ms",
7 * 24 * 60 * 60 * 1000
);
// The pref holding the current qaualification state of the user. If
// true, the user is currently qualified from the experiment.
const FISSION_EXPERIMENT_PREF_QUALIFIED =
"fission.experiment.max-origins.qualified";
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentQualified",
FISSION_EXPERIMENT_PREF_QUALIFIED,
false
);
// The pref holding the timestamp of the last time we saw an origin
// count below the cap while the user was not currently marked as
// qualified.
const FISSION_EXPERIMENT_PREF_LAST_QUALIFIED =
"fission.experiment.max-origins.last-qualified";
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentLastQualified",
FISSION_EXPERIMENT_PREF_LAST_QUALIFIED,
0
);
// The pref holding the timestamp of the last time we saw an origin
// count exceeding the cap.
const FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED =
"fission.experiment.max-origins.last-disqualified";
XPCOMUtils.defineLazyPreferenceGetter(
this,
"fissionExperimentLastDisqualified",
FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED,
0
);
var BrowserUtils = {
/**
* Prints arguments separated by a space and appends a new line.
@ -879,141 +830,6 @@ var BrowserUtils = {
? url.substring(this.trimURLProtocol.length)
: url;
},
recordSiteOriginTelemetry(aWindows, aIsGeckoView) {
Services.tm.idleDispatchToMainThread(() => {
this._recordSiteOriginTelemetry(aWindows, aIsGeckoView);
});
},
computeSiteOriginCount(aWindows, aIsGeckoView) {
// Geckoview and Desktop work differently. On desktop, aBrowser objects
// holds an array of tabs which we can use to get the <browser> objects.
// In Geckoview, it is apps' responsibility to keep track of the tabs, so
// there isn't an easy way for us to get the tabs.
let tabs = [];
if (aIsGeckoView) {
// To get all active windows; Each tab has its own window
tabs = aWindows;
} else {
for (const win of aWindows) {
tabs = tabs.concat(win.gBrowser.tabs);
}
}
let topLevelBCs = [];
for (const tab of tabs) {
let browser;
if (aIsGeckoView) {
browser = tab.browser;
} else {
browser = tab.linkedBrowser;
}
if (browser.browsingContext) {
// This is the top level browsingContext
topLevelBCs.push(browser.browsingContext);
}
}
return CanonicalBrowsingContext.countSiteOrigins(topLevelBCs);
},
_recordSiteOriginTelemetry(aWindows, aIsGeckoView) {
let currentTime = Date.now();
// default is 5 minutes
if (!this.min_interval) {
this.min_interval = Services.prefs.getIntPref(
"telemetry.number_of_site_origin.min_interval",
300000
);
}
let originCount = this.computeSiteOriginCount(aWindows, aIsGeckoView);
let histogram = Services.telemetry.getHistogramById(
"FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_ALL_TABS"
);
// Discard the first load because most of the time the first load only has 1
// tab and 1 window open, so it is useless to report it.
if (!this._lastRecordSiteOrigin) {
this._lastRecordSiteOrigin = currentTime;
} else if (currentTime >= this._lastRecordSiteOrigin + this.min_interval) {
this._lastRecordSiteOrigin = currentTime;
histogram.add(originCount);
}
// Update the Fission experiment qualification state based on the
// current origin count:
// If we don't already have a last disqualification timestamp, look
// through the existing histogram values, and use the existing
// maximum value as the initial count. This will prevent us from
// enrolling users in the experiment if they have a history of
// exceeding our origin cap.
if (!this._checkedInitialExperimentQualification) {
this._checkedInitialExperimentQualification = true;
if (
!Services.prefs.prefHasUserValue(
FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED
)
) {
for (let [bucketStr, entryCount] of Object.entries(
histogram.snapshot().values
)) {
let bucket = Number(bucketStr);
if (bucket > originCount && entryCount > 0) {
originCount = bucket;
}
}
Services.prefs.setIntPref(FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED, 0);
}
}
let currentTimeSec = currentTime / 1000;
if (originCount < fissionExperimentMaxOrigins) {
let lastDisqualified = fissionExperimentLastDisqualified;
let lastQualified = fissionExperimentLastQualified;
// If the last time we saw a qualifying origin count was earlier
// than the last time we say a disqualifying count, update any
// existing last disqualified timestamp to just before now, on the
// basis that our origin count has probably just fallen below the
// cap.
if (lastDisqualified > 0 && lastQualified <= lastDisqualified) {
Services.prefs.setIntPref(
FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED,
currentTimeSec - 1
);
}
if (!fissionExperimentQualified) {
Services.prefs.setIntPref(
FISSION_EXPERIMENT_PREF_LAST_QUALIFIED,
currentTimeSec
);
// We have a qualifying origin count now. If the last time we were
// disqualified was prior to the start of our current sliding
// window, re-qualify the user.
if (
currentTimeSec - lastDisqualified >=
fissionExperimentSlidingWindowMS / 1000
) {
Services.prefs.setBoolPref(FISSION_EXPERIMENT_PREF_QUALIFIED, true);
}
}
} else {
Services.prefs.setIntPref(
FISSION_EXPERIMENT_PREF_LAST_DISQUALIFIED,
currentTimeSec
);
Services.prefs.setBoolPref(FISSION_EXPERIMENT_PREF_QUALIFIED, false);
}
},
};
XPCOMUtils.defineLazyPreferenceGetter(

View File

@ -160,6 +160,7 @@ EXTRA_JS_MODULES += [
"AppMenuNotifications.jsm",
"AsyncPrefs.jsm",
"BinarySearch.jsm",
"BrowserTelemetryUtils.jsm",
"BrowserUtils.jsm",
"CanonicalJSON.jsm",
"CertUtils.jsm",