Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- Manifest: extensions/permissions/test/browser.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
const PERMISSIONS_FILE_NAME = "permissions.sqlite";
function getPermissionsFile() {
let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
file.append(PERMISSIONS_FILE_NAME);
return file;
}
function getInteractionCount(origin) {
let db = Services.storage.openDatabase(getPermissionsFile());
let stmt = db.createStatement(
"SELECT COUNT(*) FROM moz_origin_interactions WHERE origin = :origin"
);
stmt.bindByName("origin", origin);
stmt.executeStep();
let count = stmt.getInt64(0);
stmt.finalize();
db.close();
return count;
}
function getInteractionTime(origin) {
let db = Services.storage.openDatabase(getPermissionsFile());
let stmt = db.createStatement(
"SELECT lastInteractionTime FROM moz_origin_interactions WHERE origin = :origin"
);
stmt.bindByName("origin", origin);
let time = 0;
if (stmt.executeStep()) {
time = stmt.getInt64(0);
}
stmt.finalize();
db.close();
return time;
}
// Simulate a user interaction on the page. Synthesized mouse events have
// mReason=eSynthesized which is filtered out by EventStateManager before
// SetUserHasInteracted, so we also call userInteractionForTesting to
// ensure the interaction is recorded via the permission manager IPC path.
async function simulateUserInteraction(browser) {
await BrowserTestUtils.synthesizeMouseAtCenter("body", {}, browser);
await SpecialPowers.spawn(browser, [], () => {
content.document.userInteractionForTesting();
});
}
add_task(async function test_interaction_recorded_on_user_interaction() {
await SpecialPowers.pushPrefEnv({
set: [
["permissions.expireUnusedTypes", "desktop-notification"],
["permissions.expireUnused.enabled", true],
],
});
let pm = Services.perms;
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
);
pm.addFromPrincipal(
principal,
"desktop-notification",
Services.perms.ALLOW_ACTION
);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com" },
async function (browser) {
await simulateUserInteraction(browser);
await BrowserTestUtils.waitForCondition(
() => getInteractionTime("https://example.com") > 0,
"Waiting for interaction record to be written"
);
Assert.greater(
getInteractionTime("https://example.com"),
0,
"lastInteractionTime should be positive"
);
Assert.equal(
getInteractionCount("https://example.com"),
1,
"Should have exactly one interaction record"
);
}
);
pm.removeAll();
});
add_task(async function test_interaction_recorded_without_permission() {
await SpecialPowers.pushPrefEnv({
set: [
["permissions.expireUnusedTypes", "desktop-notification"],
["permissions.expireUnused.enabled", true],
],
});
let pm = Services.perms;
pm.removeAll();
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.org" },
async function (browser) {
await simulateUserInteraction(browser);
await BrowserTestUtils.waitForCondition(
() => getInteractionCount("https://example.org") > 0,
"Waiting for interaction record to be written"
);
is(
getInteractionCount("https://example.org"),
1,
"Should have an interaction record even without expirable permission"
);
}
);
pm.removeAll();
});
add_task(async function test_interaction_updates_on_repeat_interaction() {
await SpecialPowers.pushPrefEnv({
set: [
["permissions.expireUnusedTypes", "desktop-notification"],
["permissions.expireUnused.enabled", true],
],
});
let pm = Services.perms;
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
);
pm.addFromPrincipal(
principal,
"desktop-notification",
Services.perms.ALLOW_ACTION
);
let firstInteractionTime = 0;
// First interaction
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.net" },
async function (browser) {
await simulateUserInteraction(browser);
await BrowserTestUtils.waitForCondition(
() => getInteractionTime("https://example.net") > 0,
"Waiting for first interaction record"
);
firstInteractionTime = getInteractionTime("https://example.net");
}
);
Assert.greater(
firstInteractionTime,
0,
"Should have recorded first interaction"
);
// Second interaction
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.net" },
async function (browser) {
await simulateUserInteraction(browser);
await BrowserTestUtils.waitForCondition(
() => getInteractionTime("https://example.net") >= firstInteractionTime,
"Waiting for second interaction record"
);
let secondInteractionTime = getInteractionTime("https://example.net");
Assert.greaterOrEqual(
secondInteractionTime,
firstInteractionTime,
"Second interaction time should be >= first interaction time"
);
}
);
pm.removeAll();
});
add_task(async function test_no_interaction_tracking_when_disabled() {
await SpecialPowers.pushPrefEnv({
set: [
["permissions.expireUnusedTypes", "desktop-notification"],
["permissions.expireUnused.enabled", false],
],
});
let pm = Services.perms;
pm.removeAll();
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
);
pm.addFromPrincipal(
principal,
"desktop-notification",
Services.perms.ALLOW_ACTION
);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://www.example.com" },
async function (browser) {
await simulateUserInteraction(browser);
// Give background thread a chance to write (it shouldn't since disabled).
// Use a short waitForCondition that we expect to time out, then verify.
let appeared = false;
try {
await BrowserTestUtils.waitForCondition(
() => getInteractionCount("https://www.example.com") > 0,
"Checking no interaction is written",
100,
10
);
appeared = true;
} catch {}
is(
appeared,
false,
"Should not have an interaction record when feature is disabled"
);
}
);
pm.removeAll();
});