Bug 1713325 - Exclude time when the user is idle from the page view time for history metadata. r=mak

Differential Revision: https://phabricator.services.mozilla.com/D116275
This commit is contained in:
Mark Banner 2021-06-10 17:50:13 +00:00
parent b30e42f4ec
commit 0948a5fbc8
4 changed files with 132 additions and 4 deletions

View File

@ -1937,6 +1937,7 @@ BrowserGlue.prototype = {
BrowserUsageTelemetry.uninit();
SearchSERPTelemetry.uninit();
Interactions.uninit();
PageThumbs.uninit();
NewTabUtils.uninit();

View File

@ -16,6 +16,17 @@ XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",
});
XPCOMUtils.defineLazyServiceGetters(this, {
idleService: ["@mozilla.org/widget/useridleservice;1", "nsIUserIdleService"],
});
XPCOMUtils.defineLazyPreferenceGetter(
this,
"pageViewIdleTime",
"browser.places.interactions.pageViewIdleTime",
60
);
const DOMWINDOW_OPENED_TOPIC = "domwindowopened";
/**
@ -61,6 +72,13 @@ class _Interactions {
*/
#activeWindow = undefined;
/**
* Tracks if the user is idle.
*
* @type {boolean}
*/
#userIsIdle = false;
/**
* This stores the page view start time of the current page view.
* For any single page view, this may be moved multiple times as the
@ -113,6 +131,14 @@ class _Interactions {
}
}
Services.obs.addObserver(this, DOMWINDOW_OPENED_TOPIC, true);
idleService.addIdleObserver(this, pageViewIdleTime);
}
/**
* Uninitializes, removes any observers that need cleaning up manually.
*/
uninit() {
idleService.removeIdleObserver(this, pageViewIdleTime);
}
/**
@ -179,7 +205,16 @@ class _Interactions {
!this.#activeWindow ||
(browser && browser.ownerGlobal != this.#activeWindow)
) {
this.logConsole.debug("No active window");
this.logConsole.debug("No update due to no active window");
return;
}
// We do not update the interaction when the user is idle, since we will
// have already updated it when idle was signalled.
// Sometimes an interaction may be signalled before idle is cleared, however
// worst case we'd only loose approx 2 seconds of interaction detail.
if (this.#userIsIdle) {
this.logConsole.debug("No update due to user is idle");
return;
}
@ -275,6 +310,18 @@ class _Interactions {
case DOMWINDOW_OPENED_TOPIC:
this.#onWindowOpen(subject);
break;
case "idle":
this.logConsole.debug("idle");
// We save the state of the current interaction when we are notified
// that the user is idle.
this.#updateInteraction();
this.#userIsIdle = true;
break;
case "active":
this.logConsole.debug("active");
this.#userIsIdle = false;
this._pageViewStartTime = Cu.now();
break;
}
}
@ -341,7 +388,9 @@ class _Interactions {
* @param {InteractionInfo} interactionInfo
* The document information to write.
*/
async _updateDatabase({ url, totalViewTime }) {}
async _updateDatabase(interactionInfo) {
this.logConsole.debug("Would update database: ", interactionInfo);
}
}
const Interactions = new _Interactions();

View File

@ -13,6 +13,7 @@ const TEST_URL4 = "https://example.com/browser/browser/components";
add_task(async function setup() {
sinon.spy(Interactions, "_updateDatabase");
disableIdleService();
registerCleanupFunction(() => {
sinon.restore();
@ -37,13 +38,20 @@ async function assertDatabaseValues(expected) {
Assert.equal(
actual.totalViewTime,
expected[i].exactTotalViewTime,
"Should have kept the exact time."
"Should have kept the exact time"
);
} else {
Assert.greater(
actual.totalViewTime,
expected[i].totalViewTime,
"Should have stored the interaction time."
"Should have stored the interaction time"
);
}
if (expected[i].maxViewTime) {
Assert.less(
actual.totalViewTime,
expected[i].maxViewTime,
"Should have recorded an interaction below the maximum expected"
);
}
}
@ -388,3 +396,50 @@ add_task(async function test_interactions_private_browsing() {
BrowserTestUtils.removeTab(tabInOriginalWindow);
await BrowserTestUtils.closeWindow(privateWin);
});
add_task(async function test_interactions_idle() {
sinon.reset();
let lastViewTime;
await BrowserTestUtils.withNewTab(TEST_URL, async browser => {
Interactions._pageViewStartTime = Cu.now() - 10000;
Interactions.observe(null, "idle", "");
await assertDatabaseValues([
{
url: TEST_URL,
totalViewTime: 10000,
},
]);
lastViewTime = Interactions._updateDatabase.args[0][0].totalViewTime;
Interactions._pageViewStartTime = Cu.now() - 20000;
Interactions.observe(null, "active", "");
await assertDatabaseValues([
{
url: TEST_URL,
exactTotalViewTime: lastViewTime,
},
]);
Interactions._pageViewStartTime = Cu.now() - 30000;
});
await assertDatabaseValues([
{
url: TEST_URL,
// Note: this should be `exactTotalViewTime: lastViewTime`, but
// apply updates to the same object.
totalViewTime: lastViewTime + 30000,
},
{
url: TEST_URL,
totalViewTime: lastViewTime + 30000,
maxViewTime: lastViewTime + 30000 + 10000,
},
]);
});

View File

@ -7,3 +7,26 @@ const { Interactions } = ChromeUtils.import(
);
const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm");
XPCOMUtils.defineLazyPreferenceGetter(
this,
"pageViewIdleTime",
"browser.places.interactions.pageViewIdleTime",
60
);
/**
* Register the mock idleSerice.
*
* @param {Fun} registerCleanupFunction
*/
function disableIdleService() {
let idleService = Cc["@mozilla.org/widget/useridleservice;1"].getService(
Ci.nsIUserIdleService
);
idleService.removeIdleObserver(Interactions, pageViewIdleTime);
registerCleanupFunction(() => {
idleService.addIdleObserver(Interactions, pageViewIdleTime);
});
}