fune/toolkit/components/cleardata/SiteDataTestUtils.sys.mjs
Stanca Serban 582e249d86 Backed out 4 changesets (bug 1854940) for causing multiple failures.
Backed out changeset 2d264402493e (bug 1854940)
Backed out changeset 144b76e6db22 (bug 1854940)
Backed out changeset 5a9e45504f32 (bug 1854940)
Backed out changeset c8c7ec382574 (bug 1854940)
2023-11-08 14:20:06 +02:00

407 lines
12 KiB
JavaScript

/* 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/. */
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { BrowserTestUtils } from "resource://testing-common/BrowserTestUtils.sys.mjs";
const lazy = {};
XPCOMUtils.defineLazyServiceGetter(
lazy,
"swm",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager"
);
/**
* This module assists with tasks around testing functionality that shows
* or clears site data.
*
* Please note that you will have to clean up changes made manually, for
* example using SiteDataTestUtils.clear().
*/
export var SiteDataTestUtils = {
/**
* Makes an origin have persistent data storage.
*
* @param {String} origin - the origin of the site to give persistent storage
*
* @returns a Promise that resolves when storage was persisted
*/
persist(origin, value = Services.perms.ALLOW_ACTION) {
return new Promise(resolve => {
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
Services.perms.addFromPrincipal(principal, "persistent-storage", value);
Services.qms.persist(principal).callback = () => resolve();
});
},
/**
* Adds a new blob entry to a dummy indexedDB database for the specified origin.
*
* @param {String} origin - the origin of the site to add test data for
* @param {Number} size [optional] - the size of the entry in bytes
*
* @returns a Promise that resolves when the data was added successfully.
*/
addToIndexedDB(origin, size = 1024) {
return new Promise(resolve => {
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
let request = indexedDB.openForPrincipal(principal, "TestDatabase", 1);
request.onupgradeneeded = function (e) {
let db = e.target.result;
db.createObjectStore("TestStore");
};
request.onsuccess = function (e) {
let db = e.target.result;
let tx = db.transaction("TestStore", "readwrite");
let store = tx.objectStore("TestStore");
tx.oncomplete = resolve;
let buffer = new ArrayBuffer(size);
let blob = new Blob([buffer]);
store.add(blob, Cu.now());
};
});
},
/**
* Adds a new cookie for the specified origin or host + path + oa, with the
* specified contents. The cookie will be valid for one day.
* @param {object} options
* @param {String} [options.origin] - Origin of the site to add test data for.
* If set, overrides host, path and originAttributes args.
* @param {String} [options.host] - Host of the site to add test data for.
* @param {String} [options.path] - Path to set cookie for.
* @param {Object} [options.originAttributes] - Object of origin attributes to
* set cookie for.
* @param {String} [options.name] - Cookie name
* @param {String} [options.value] - Cookie value
*/
addToCookies({
origin,
host,
path = "path",
originAttributes = {},
name = "foo",
value = "bar",
}) {
if (origin) {
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
host = principal.host;
path = principal.URI.pathQueryRef;
originAttributes = Object.keys(originAttributes).length
? originAttributes
: principal.originAttributes;
}
Services.cookies.add(
host,
path,
name,
value,
false,
false,
false,
Math.floor(Date.now() / 1000) + 24 * 60 * 60,
originAttributes,
Ci.nsICookie.SAMESITE_NONE,
Ci.nsICookie.SCHEME_UNSET
);
},
/**
* Adds a new localStorage entry for the specified origin, with the specified contents.
*
* @param {String} origin - the origin of the site to add test data for
* @param {String} [key] - the localStorage key
* @param {String} [value] - the localStorage value
*/
addToLocalStorage(origin, key = "foo", value = "bar") {
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
let storage = Services.domStorageManager.createStorage(
null,
principal,
principal,
""
);
storage.setItem(key, value);
},
/**
* Checks whether the given origin is storing data in localStorage
*
* @param {String} origin - the origin of the site to check
* @param {{key: String, value: String}[]} [testEntries] - An array of entries
* to test for.
*
* @returns {Boolean} whether the origin has localStorage data
*/
hasLocalStorage(origin, testEntries) {
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
let storage = Services.domStorageManager.createStorage(
null,
principal,
principal,
""
);
if (!storage.length) {
return false;
}
if (!testEntries) {
return true;
}
return (
storage.length >= testEntries.length &&
testEntries.every(({ key, value }) => storage.getItem(key) == value)
);
},
/**
* Adds a new serviceworker with the specified path. Note that this
* method will open a new tab at the domain of the SW path to that effect.
*
* @param {String} path - the path to the service worker to add.
*
* @returns a Promise that resolves when the service worker was registered
*/
addServiceWorker(path) {
let uri = Services.io.newURI(path);
// Register a dummy ServiceWorker.
return BrowserTestUtils.withNewTab(uri.prePath, async function (browser) {
return browser.ownerGlobal.SpecialPowers.spawn(
browser,
[{ path }],
async ({ path: p }) => {
// eslint-disable-next-line no-undef
let r = await content.navigator.serviceWorker.register(p);
return new Promise(resolve => {
let worker = r.installing || r.waiting || r.active;
if (worker.state == "activated") {
resolve();
} else {
worker.addEventListener("statechange", () => {
if (worker.state == "activated") {
resolve();
}
});
}
});
}
);
});
},
hasCookies(origin, testEntries = null, testPBMCookies = false) {
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
let originAttributes = principal.originAttributes;
if (testPBMCookies) {
// Override the origin attributes to set PBM.
// This needs to be updated when adding support for multiple PBM contexts.
originAttributes = {
...principal.originAttributes,
privateBrowsingId: 1,
};
}
// Need to do an additional filter step since getCookiesFromHost returns all
// cookies for the base domain (including subdomains). This method takes an
// origin so we need to do an exact host match.
let cookies = Services.cookies
.getCookiesFromHost(principal.baseDomain, originAttributes)
.filter(
cookie =>
cookie.host == principal.host || cookie.host == `.${principal.host}`
);
// If we don't have to filter specific testEntries we can return now.
if (!testEntries) {
return !!cookies.length;
}
// Check if the returned cookies match testEntries.
if (cookies.length < testEntries.length) {
return false;
}
// This code isn't very efficient. It should only be used for testing
// a small amount of cookies.
return testEntries.every(({ key, value }) =>
cookies.some(cookie => cookie.name == key && cookie.value == value)
);
},
hasIndexedDB(origin) {
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
return new Promise(resolve => {
let data = true;
let request = indexedDB.openForPrincipal(principal, "TestDatabase", 1);
request.onupgradeneeded = function (e) {
data = false;
};
request.onsuccess = function (e) {
resolve(data);
};
});
},
_getCacheStorage(where, lci) {
switch (where) {
case "disk":
return Services.cache2.diskCacheStorage(lci);
case "memory":
return Services.cache2.memoryCacheStorage(lci);
case "pin":
return Services.cache2.pinningCacheStorage(lci);
}
return null;
},
hasCacheEntry(path, where, lci = Services.loadContextInfo.default) {
let storage = this._getCacheStorage(where, lci);
return storage.exists(Services.io.newURI(path), "");
},
addCacheEntry(path, where, lci = Services.loadContextInfo.default) {
return new Promise(resolve => {
function CacheListener() {}
CacheListener.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsICacheEntryOpenCallback"]),
onCacheEntryCheck(entry) {
return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
},
onCacheEntryAvailable(entry, isnew, status) {
resolve();
},
};
let storage = this._getCacheStorage(where, lci);
storage.asyncOpenURI(
Services.io.newURI(path),
"",
Ci.nsICacheStorage.OPEN_NORMALLY,
new CacheListener()
);
});
},
/**
* Checks whether the specified origin has registered ServiceWorkers.
*
* @param {String} origin - the origin of the site to check
*
* @returns {Boolean} whether or not the site has ServiceWorkers.
*/
hasServiceWorkers(origin) {
let serviceWorkers = lazy.swm.getAllRegistrations();
for (let i = 0; i < serviceWorkers.length; i++) {
let sw = serviceWorkers.queryElementAt(
i,
Ci.nsIServiceWorkerRegistrationInfo
);
if (sw.principal.origin == origin) {
return true;
}
}
return false;
},
/**
* Waits for a ServiceWorker to be registered.
*
* @param {String} the url of the ServiceWorker to wait for
*
* @returns a Promise that resolves when a ServiceWorker at the
* specified location has been registered.
*/
promiseServiceWorkerRegistered(url) {
if (!(url instanceof Ci.nsIURI)) {
url = Services.io.newURI(url);
}
return new Promise(resolve => {
let listener = {
onRegister: registration => {
if (registration.principal.host != url.host) {
return;
}
lazy.swm.removeListener(listener);
resolve(registration);
},
};
lazy.swm.addListener(listener);
});
},
/**
* Waits for a ServiceWorker to be unregistered.
*
* @param {String} the url of the ServiceWorker to wait for
*
* @returns a Promise that resolves when a ServiceWorker at the
* specified location has been unregistered.
*/
promiseServiceWorkerUnregistered(url) {
if (!(url instanceof Ci.nsIURI)) {
url = Services.io.newURI(url);
}
return new Promise(resolve => {
let listener = {
onUnregister: registration => {
if (registration.principal.host != url.host) {
return;
}
lazy.swm.removeListener(listener);
resolve(registration);
},
};
lazy.swm.addListener(listener);
});
},
/**
* Gets the current quota usage for the specified origin.
*
* @returns a Promise that resolves to an integer with the total
* amount of disk usage by a given origin.
*/
getQuotaUsage(origin) {
return new Promise(resolve => {
let principal =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin);
Services.qms.getUsageForPrincipal(principal, request =>
resolve(request.result.usage)
);
});
},
/**
* Cleans up all site data.
*/
clear() {
return new Promise(resolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_COOKIES |
Ci.nsIClearDataService.CLEAR_ALL_CACHES |
Ci.nsIClearDataService.CLEAR_MEDIA_DEVICES |
Ci.nsIClearDataService.CLEAR_DOM_STORAGES |
Ci.nsIClearDataService.CLEAR_PREDICTOR_NETWORK_DATA |
Ci.nsIClearDataService.CLEAR_CLIENT_AUTH_REMEMBER_SERVICE |
Ci.nsIClearDataService.CLEAR_EME |
Ci.nsIClearDataService.CLEAR_STORAGE_ACCESS,
resolve
);
});
},
};