Files
tubestation/browser/base/content/test/sanitize/head.js
Butkovits Atila d779b4bd81 Backed out 12 changesets (bug 1915419) for causing failures at test_bouncetracking_purge.js. CLOSED TREE
Backed out changeset e5c32a502e0f (bug 1915419)
Backed out changeset 8cbcbb25fc99 (bug 1915419)
Backed out changeset 947379a2f167 (bug 1915419)
Backed out changeset 4cfe3a954ade (bug 1915419)
Backed out changeset 94fba1a2786b (bug 1915419)
Backed out changeset 477b4f7c2760 (bug 1915419)
Backed out changeset ed6c8d9d39d4 (bug 1915419)
Backed out changeset e534c390101c (bug 1915419)
Backed out changeset bc3fe74a8b03 (bug 1915419)
Backed out changeset afefdce605f9 (bug 1915419)
Backed out changeset e58887b90d39 (bug 1915419)
Backed out changeset 114298b7b855 (bug 1915419)
2024-09-18 17:49:45 +03:00

738 lines
22 KiB
JavaScript

ChromeUtils.defineESModuleGetters(this, {
Downloads: "resource://gre/modules/Downloads.sys.mjs",
FormHistory: "resource://gre/modules/FormHistory.sys.mjs",
PermissionTestUtils: "resource://testing-common/PermissionTestUtils.sys.mjs",
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs",
Sanitizer: "resource:///modules/Sanitizer.sys.mjs",
SiteDataTestUtils: "resource://testing-common/SiteDataTestUtils.sys.mjs",
});
const kMsecPerMin = 60 * 1000;
const kUsecPerMin = kMsecPerMin * 1000;
let today = Date.now() - new Date().setHours(0, 0, 0, 0);
let nowMSec = Date.now();
let nowUSec = nowMSec * 1000;
const TEST_TARGET_FILE_NAME = "test-download.txt";
const TEST_QUOTA_USAGE_HOST = "example.com";
const TEST_QUOTA_USAGE_ORIGIN = "https://" + TEST_QUOTA_USAGE_HOST;
const TEST_QUOTA_USAGE_URL =
getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
TEST_QUOTA_USAGE_ORIGIN
) + "site_data_test.html";
const SITE_ORIGINS = [
"https://www.example.com",
"https://example.org",
"http://localhost:8000",
"http://localhost:3000",
];
let fileURL;
function createIndexedDB(host, originAttributes) {
let uri = Services.io.newURI("https://" + host);
let principal = Services.scriptSecurityManager.createContentPrincipal(
uri,
originAttributes
);
return SiteDataTestUtils.addToIndexedDB(principal.origin);
}
function checkIndexedDB(host, originAttributes) {
return new Promise(resolve => {
let data = true;
let uri = Services.io.newURI("https://" + host);
let principal = Services.scriptSecurityManager.createContentPrincipal(
uri,
originAttributes
);
let request = indexedDB.openForPrincipal(principal, "TestDatabase", 1);
request.onupgradeneeded = function () {
data = false;
};
request.onsuccess = function () {
resolve(data);
};
});
}
function createHostCookie(host, originAttributes) {
Services.cookies.add(
host,
"/test",
"foo",
"bar",
false,
false,
false,
Date.now() + 24000 * 60 * 60,
originAttributes,
Ci.nsICookie.SAMESITE_NONE,
Ci.nsICookie.SCHEME_HTTPS
);
}
function createDomainCookie(host, originAttributes) {
Services.cookies.add(
"." + host,
"/test",
"foo",
"bar",
false,
false,
false,
Date.now() + 24000 * 60 * 60,
originAttributes,
Ci.nsICookie.SAMESITE_NONE,
Ci.nsICookie.SCHEME_HTTPS
);
}
function checkCookie(host, originAttributes) {
for (let cookie of Services.cookies.cookies) {
if (
ChromeUtils.isOriginAttributesEqual(
originAttributes,
cookie.originAttributes
) &&
cookie.host.includes(host)
) {
return true;
}
}
return false;
}
async function deleteOnShutdown(opt) {
// Let's clean up all the data.
await new Promise(resolve => {
Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve);
});
await SpecialPowers.pushPrefEnv({
set: [
["privacy.sanitize.sanitizeOnShutdown", opt.sanitize],
["privacy.clearOnShutdown.cookies", opt.sanitize],
["privacy.clearOnShutdown.offlineApps", opt.sanitize],
["browser.sanitizer.loglevel", "All"],
],
});
// Custom permission without considering OriginAttributes
if (opt.cookiePermission !== undefined) {
let uri = Services.io.newURI("https://www.example.com");
PermissionTestUtils.add(uri, "cookie", opt.cookiePermission);
}
// Let's create a tab with some data.
await opt.createData(
(opt.fullHost ? "www." : "") + "example.org",
opt.originAttributes
);
ok(
await opt.checkData(
(opt.fullHost ? "www." : "") + "example.org",
opt.originAttributes
),
"We have data for www.example.org"
);
await opt.createData(
(opt.fullHost ? "www." : "") + "example.com",
opt.originAttributes
);
ok(
await opt.checkData(
(opt.fullHost ? "www." : "") + "example.com",
opt.originAttributes
),
"We have data for www.example.com"
);
// Cleaning up.
await Sanitizer.runSanitizeOnShutdown();
// All gone!
is(
!!(await opt.checkData(
(opt.fullHost ? "www." : "") + "example.org",
opt.originAttributes
)),
opt.expectedForOrg,
"Do we have data for www.example.org?"
);
is(
!!(await opt.checkData(
(opt.fullHost ? "www." : "") + "example.com",
opt.originAttributes
)),
opt.expectedForCom,
"Do we have data for www.example.com?"
);
// Clean up.
await Sanitizer.sanitize(["cookies", "offlineApps"]);
if (opt.cookiePermission !== undefined) {
let uri = Services.io.newURI("https://www.example.com");
PermissionTestUtils.remove(uri, "cookie");
}
}
function runAllCookiePermissionTests(originAttributes) {
let tests = [
{ name: "IDB", createData: createIndexedDB, checkData: checkIndexedDB },
{
name: "Host Cookie",
createData: createHostCookie,
checkData: checkCookie,
},
{
name: "Domain Cookie",
createData: createDomainCookie,
checkData: checkCookie,
},
];
// Delete all, no custom permission, data in example.com, cookie permission set
// for www.example.com
tests.forEach(methods => {
add_task(async function deleteStorageOnShutdown() {
info(
methods.name +
": Delete all, no custom permission, data in example.com, cookie permission set for www.example.com - OA: " +
originAttributes.name
);
await deleteOnShutdown({
sanitize: true,
createData: methods.createData,
checkData: methods.checkData,
originAttributes: originAttributes.oa,
cookiePermission: undefined,
expectedForOrg: false,
expectedForCom: false,
fullHost: false,
});
});
});
// Delete all, no custom permission, data in www.example.com, cookie permission
// set for www.example.com
tests.forEach(methods => {
add_task(async function deleteStorageOnShutdown() {
info(
methods.name +
": Delete all, no custom permission, data in www.example.com, cookie permission set for www.example.com - OA: " +
originAttributes.name
);
await deleteOnShutdown({
sanitize: true,
createData: methods.createData,
checkData: methods.checkData,
originAttributes: originAttributes.oa,
cookiePermission: undefined,
expectedForOrg: false,
expectedForCom: false,
fullHost: true,
});
});
});
// All is session, but with ALLOW custom permission, data in example.com,
// cookie permission set for www.example.com
tests.forEach(methods => {
add_task(async function deleteStorageWithCustomPermission() {
info(
methods.name +
": All is session, but with ALLOW custom permission, data in example.com, cookie permission set for www.example.com - OA: " +
originAttributes.name
);
await deleteOnShutdown({
sanitize: true,
createData: methods.createData,
checkData: methods.checkData,
originAttributes: originAttributes.oa,
cookiePermission: Ci.nsICookiePermission.ACCESS_ALLOW,
expectedForOrg: false,
expectedForCom: true,
fullHost: false,
});
});
});
// All is session, but with ALLOW custom permission, data in www.example.com,
// cookie permission set for www.example.com
tests.forEach(methods => {
add_task(async function deleteStorageWithCustomPermission() {
info(
methods.name +
": All is session, but with ALLOW custom permission, data in www.example.com, cookie permission set for www.example.com - OA: " +
originAttributes.name
);
await deleteOnShutdown({
sanitize: true,
createData: methods.createData,
checkData: methods.checkData,
originAttributes: originAttributes.oa,
cookiePermission: Ci.nsICookiePermission.ACCESS_ALLOW,
expectedForOrg: false,
expectedForCom: true,
fullHost: true,
});
});
});
// All is default, but with SESSION custom permission, data in example.com,
// cookie permission set for www.example.com
tests.forEach(methods => {
add_task(async function deleteStorageOnlyCustomPermission() {
info(
methods.name +
": All is default, but with SESSION custom permission, data in example.com, cookie permission set for www.example.com - OA: " +
originAttributes.name
);
await deleteOnShutdown({
sanitize: false,
createData: methods.createData,
checkData: methods.checkData,
originAttributes: originAttributes.oa,
cookiePermission: Ci.nsICookiePermission.ACCESS_SESSION,
expectedForOrg: true,
// expected data just for example.com when using indexedDB because
// QuotaManager deletes for principal.
expectedForCom: false,
fullHost: false,
});
});
});
// All is default, but with SESSION custom permission, data in www.example.com,
// cookie permission set for www.example.com
tests.forEach(methods => {
add_task(async function deleteStorageOnlyCustomPermission() {
info(
methods.name +
": All is default, but with SESSION custom permission, data in www.example.com, cookie permission set for www.example.com - OA: " +
originAttributes.name
);
await deleteOnShutdown({
sanitize: false,
createData: methods.createData,
checkData: methods.checkData,
originAttributes: originAttributes.oa,
cookiePermission: Ci.nsICookiePermission.ACCESS_SESSION,
expectedForOrg: true,
expectedForCom: false,
fullHost: true,
});
});
});
// Session mode, but with unsupported custom permission, data in
// www.example.com, cookie permission set for www.example.com
tests.forEach(methods => {
add_task(async function deleteStorageOnlyCustomPermission() {
info(
methods.name +
": All is session only, but with unsupported custom custom permission, data in www.example.com, cookie permission set for www.example.com - OA: " +
originAttributes.name
);
await deleteOnShutdown({
sanitize: true,
createData: methods.createData,
checkData: methods.checkData,
originAttributes: originAttributes.oa,
cookiePermission: 123, // invalid cookie permission
expectedForOrg: false,
expectedForCom: false,
fullHost: true,
});
});
});
}
function openPreferencesViaOpenPreferencesAPI(aPane, aOptions) {
return new Promise(resolve => {
let finalPrefPaneLoaded = TestUtils.topicObserved(
"sync-pane-loaded",
() => true
);
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank");
openPreferences(aPane);
let newTabBrowser = gBrowser.selectedBrowser;
newTabBrowser.addEventListener(
"Initialized",
function () {
newTabBrowser.contentWindow.addEventListener(
"load",
async function () {
let win = gBrowser.contentWindow;
let selectedPane = win.history.state;
await finalPrefPaneLoaded;
if (!aOptions || !aOptions.leaveOpen) {
gBrowser.removeCurrentTab();
}
resolve({ selectedPane });
},
{ once: true }
);
},
{ capture: true, once: true }
);
});
}
async function createDummyDataForHost(host) {
let origin = "https://" + host;
let dummySWURL =
getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
"dummy.js";
await SiteDataTestUtils.addToIndexedDB(origin);
await SiteDataTestUtils.addServiceWorker(dummySWURL);
}
/**
* Helper function to create file URL to open
*
* @returns {Object} a file URL
*/
function createFileURL() {
if (!fileURL) {
let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
file.append("foo.txt");
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
fileURL = Services.io.newFileURI(file);
}
return fileURL;
}
/**
* Removes all history visits, downloads, and form entries.
*/
async function blankSlate() {
let publicList = await Downloads.getList(Downloads.PUBLIC);
let downloads = await publicList.getAll();
for (let download of downloads) {
await publicList.remove(download);
await download.finalize(true);
}
await FormHistory.update({ op: "remove" });
await PlacesUtils.history.clear();
}
/**
* Adds multiple downloads to the PUBLIC download list
*/
async function addToDownloadList() {
const url = createFileURL();
const downloadsList = await Downloads.getList(Downloads.PUBLIC);
let timeOptions = [1, 2, 4, 24, 128, 128];
let buffer = 100000;
for (let i = 0; i < timeOptions.length; i++) {
let timeDownloaded = 60 * kMsecPerMin * timeOptions[i];
if (timeOptions[i] === 24) {
timeDownloaded = today;
}
let download = await Downloads.createDownload({
source: { url: url.spec, isPrivate: false },
target: { path: FileTestUtils.getTempFile(TEST_TARGET_FILE_NAME).path },
startTime: {
getTime: _ => {
return nowMSec - timeDownloaded + buffer;
},
},
});
Assert.ok(!!download);
downloadsList.add(download);
}
let items = await downloadsList.getAll();
Assert.equal(items.length, 6, "Items were added to the list");
}
async function addToSiteUsage() {
// Fill indexedDB with test data.
// Don't wait for the page to load, to register the content event handler as quickly as possible.
// If this test goes intermittent, we might have to tell the page to wait longer before
// firing the event.
BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL, false);
await BrowserTestUtils.waitForContentEvent(
gBrowser.selectedBrowser,
"test-indexedDB-done",
false,
null,
true
);
BrowserTestUtils.removeTab(gBrowser.selectedTab);
let siteLastAccessed = [1, 2, 4, 24];
let staticUsage = 4096 * 6;
// Add a time buffer so the site access falls within the time range
const buffer = 10000;
// Change lastAccessed of sites
for (let index = 0; index < siteLastAccessed.length; index++) {
let lastAccessedTime = 60 * kMsecPerMin * siteLastAccessed[index];
if (siteLastAccessed[index] === 24) {
lastAccessedTime = today;
}
let site = SiteDataManager._testInsertSite(SITE_ORIGINS[index], {
quotaUsage: staticUsage,
lastAccessed: (nowMSec - lastAccessedTime + buffer) * 1000,
});
Assert.ok(site, "Site added successfully");
}
}
function promiseSanitizationComplete() {
return TestUtils.topicObserved("sanitizer-sanitization-complete");
}
/**
* This wraps the dialog and provides some convenience methods for interacting
* with it.
*
* @param {Window} browserWin (optional)
* The browser window that the dialog is expected to open in. If not
* supplied, the initial browser window of the test run is used.
* @param {Object} {mode, checkingDataSizes}
* mode: context to open the dialog in
* One of
* clear on shutdown settings context ("clearOnShutdown"),
* clear site data settings context ("clearSiteData"),
* clear history context ("clearHistory"),
* browser context ("browser")
* "browser" by default
* checkingDataSizes: boolean check if we should wait for the data sizes
* to load
*
*/
function ClearHistoryDialogHelper({
mode = "browser",
checkingDataSizes = false,
} = {}) {
this._browserWin = window;
this.win = null;
this._mode = mode;
this._checkingDataSizes = checkingDataSizes;
this.promiseClosed = new Promise(resolve => {
this._resolveClosed = resolve;
});
}
ClearHistoryDialogHelper.prototype = {
/**
* "Presses" the dialog's OK button.
*/
acceptDialog() {
let dialogEl = this.win.document.querySelector("dialog");
is(
dialogEl.getButton("accept").disabled,
false,
"Dialog's OK button should not be disabled"
);
dialogEl.acceptDialog();
},
/**
* "Presses" the dialog's Cancel button.
*/
cancelDialog() {
this.win.document.querySelector("dialog").cancelDialog();
},
/**
* (Un)checks a history scope checkbox (browser & download history,
* form history, etc.).
*
* @param aPrefName
* The final portion of the checkbox's privacy.cpd.* preference name
* @param aCheckState
* True if the checkbox should be checked, false otherwise
*/
checkPrefCheckbox(aPrefName, aCheckState) {
var cb = this.win.document.querySelectorAll(
"checkbox[id='" + aPrefName + "']"
);
is(cb.length, 1, "found checkbox for " + aPrefName + " id");
if (cb[0].checked != aCheckState) {
cb[0].click();
}
},
/**
* @param {String} aCheckboxId
* The checkbox id name
* @param {Boolean} aCheckState
* True if the checkbox should be checked, false otherwise
*/
validateCheckbox(aCheckboxId, aCheckState) {
let cb = this.win.document.querySelectorAll(
"checkbox[id='" + aCheckboxId + "']"
);
is(cb.length, 1, `found checkbox for id=${aCheckboxId}`);
is(
cb[0].checked,
aCheckState,
`checkbox for ${aCheckboxId} is ${aCheckState}`
);
},
/**
* Makes sure all the checkboxes are checked.
*/
_checkAllCheckboxesCustom(check) {
var cb = this.win.document.querySelectorAll(".clearingItemCheckbox");
ok(cb.length, "found checkboxes for ids");
for (var i = 0; i < cb.length; ++i) {
if (cb[i].checked != check) {
cb[i].click();
}
}
},
checkAllCheckboxes() {
this._checkAllCheckboxesCustom(true);
},
uncheckAllCheckboxes() {
this._checkAllCheckboxesCustom(false);
},
/**
* @return The dialog's duration dropdown
*/
getDurationDropdown() {
return this.win.document.getElementById("sanitizeDurationChoice");
},
/**
* @return The clear-everything warning box
*/
getWarningPanel() {
return this.win.document.getElementById("sanitizeEverythingWarningBox");
},
/**
* @return True if the "Everything" warning panel is visible (as opposed to
* the tree)
*/
isWarningPanelVisible() {
return !this.getWarningPanel().hidden;
},
/**
* Opens the clear recent history dialog. Before calling this, set
* this.onload to a function to execute onload. It should close the dialog
* when done so that the tests may continue. Set this.onunload to a function
* to execute onunload. this.onunload is optional. If it returns true, the
* caller is expected to call promiseAsyncUpdates at some point; if false is
* returned, promiseAsyncUpdates is called automatically.
*/
async open() {
let dialogPromise = BrowserTestUtils.promiseAlertDialogOpen(
null,
"chrome://browser/content/sanitize_v2.xhtml",
{
isSubDialog: true,
}
);
// We want to simulate opening the dialog inside preferences for clear history
// and clear site data
if (this._mode != "browser") {
await openPreferencesViaOpenPreferencesAPI("privacy", {
leaveOpen: true,
});
let tabWindow = gBrowser.selectedBrowser.contentWindow;
let clearDialogOpenButtonId = this._mode + "Button";
// the id for clear on shutdown is of a different format
if (this._mode == "clearOnShutdown") {
// set always clear to true to enable the clear on shutdown dialog
let enableSettingsCheckbox =
tabWindow.document.getElementById("alwaysClear");
if (!enableSettingsCheckbox.checked) {
enableSettingsCheckbox.click();
}
clearDialogOpenButtonId = "clearDataSettings";
}
// open dialog
tabWindow.document.getElementById(clearDialogOpenButtonId).click();
}
// We open the dialog in the chrome context in other cases
else {
executeSoon(() => {
Sanitizer.showUI(this._browserWin, this._mode);
});
}
this.win = await dialogPromise;
this.win.addEventListener(
"load",
() => {
// Run onload on next tick so that gSanitizePromptDialog.init can run first.
executeSoon(async () => {
if (this._checkingDataSizes) {
// we wait for the data sizes to load to avoid async errors when validating sizes
await this.win.gSanitizePromptDialog
.dataSizesFinishedUpdatingPromise;
}
this.onload();
});
},
{ once: true }
);
this.win.addEventListener(
"unload",
() => {
// Some exceptions that reach here don't reach the test harness, but
// ok()/is() do...
(async () => {
if (this.onunload) {
await this.onunload();
}
if (this._mode != "browser") {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
}
await PlacesTestUtils.promiseAsyncUpdates();
this._resolveClosed();
this.win = null;
})();
},
{ once: true }
);
},
/**
* Selects a duration in the duration dropdown.
*
* @param aDurVal
* One of the Sanitizer.TIMESPAN_* values
*/
selectDuration(aDurVal) {
this.getDurationDropdown().value = aDurVal;
if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) {
is(
this.isWarningPanelVisible(),
true,
"Warning panel should be visible for TIMESPAN_EVERYTHING"
);
} else {
is(
this.isWarningPanelVisible(),
false,
"Warning panel should not be visible for non-TIMESPAN_EVERYTHING"
);
}
},
};