262 lines
8.2 KiB
JavaScript
262 lines
8.2 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
/* import-globals-from head.js */
|
|
|
|
function _getSupportsFile(path) {
|
|
const cr = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(
|
|
Ci.nsIChromeRegistry
|
|
);
|
|
const uri = Services.io.newURI(CHROME_URL_ROOT + path);
|
|
const fileurl = cr.convertChromeURL(uri);
|
|
return fileurl.QueryInterface(Ci.nsIFileURL);
|
|
}
|
|
|
|
async function enableExtensionDebugging() {
|
|
// Disable security prompt
|
|
await pushPref("devtools.debugger.prompt-connection", false);
|
|
}
|
|
/* exported enableExtensionDebugging */
|
|
|
|
/**
|
|
* Install an extension using the AddonManager so it does not show up as temporary.
|
|
*/
|
|
async function installRegularExtension(pathOrFile) {
|
|
const isFile = typeof pathOrFile.isFile === "function" && pathOrFile.isFile();
|
|
const file = isFile ? pathOrFile : _getSupportsFile(pathOrFile).file;
|
|
const install = await AddonManager.getInstallForFile(file);
|
|
return new Promise((resolve, reject) => {
|
|
if (!install) {
|
|
throw new Error(`An install was not created for ${file.path}`);
|
|
}
|
|
install.addListener({
|
|
onDownloadFailed: reject,
|
|
onDownloadCancelled: reject,
|
|
onInstallFailed: reject,
|
|
onInstallCancelled: reject,
|
|
onInstallEnded: resolve,
|
|
});
|
|
install.install();
|
|
});
|
|
}
|
|
/* exported installRegularExtension */
|
|
|
|
/**
|
|
* Install a temporary extension at the provided path, with the provided name.
|
|
* Will use a mock file picker to select the file.
|
|
*/
|
|
async function installTemporaryExtension(pathOrFile, name, document) {
|
|
const { Management } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Extension.sys.mjs"
|
|
);
|
|
|
|
info("Install temporary extension named " + name);
|
|
// Mock the file picker to select a test addon
|
|
prepareMockFilePicker(pathOrFile);
|
|
|
|
const onAddonInstalled = new Promise(done => {
|
|
Management.on("startup", function listener(event, extension) {
|
|
if (extension.name != name) {
|
|
return;
|
|
}
|
|
|
|
Management.off("startup", listener);
|
|
done(extension);
|
|
});
|
|
});
|
|
|
|
// Trigger the file picker by clicking on the button
|
|
document.querySelector(".qa-temporary-extension-install-button").click();
|
|
|
|
info("Wait for addon to be installed");
|
|
return onAddonInstalled;
|
|
}
|
|
/* exported installTemporaryExtension */
|
|
|
|
function createTemporaryXPI(xpiData) {
|
|
const { ExtensionTestCommon } = ChromeUtils.importESModule(
|
|
"resource://testing-common/ExtensionTestCommon.sys.mjs"
|
|
);
|
|
|
|
const { background, files, id, name, extraProperties } = xpiData;
|
|
info("Generate XPI file for " + id);
|
|
|
|
const manifest = Object.assign(
|
|
{},
|
|
{
|
|
browser_specific_settings: { gecko: { id } },
|
|
manifest_version: 2,
|
|
name,
|
|
version: "1.0",
|
|
},
|
|
extraProperties
|
|
);
|
|
|
|
const xpiFile = ExtensionTestCommon.generateXPI({
|
|
background,
|
|
files,
|
|
manifest,
|
|
});
|
|
registerCleanupFunction(() => xpiFile.exists() && xpiFile.remove(false));
|
|
return xpiFile;
|
|
}
|
|
/* exported createTemporaryXPI */
|
|
|
|
/**
|
|
* Remove the existing temporary XPI file generated by ExtensionTestCommon and create a
|
|
* new one at the same location.
|
|
* @return {File} the temporary extension XPI file created
|
|
*/
|
|
function updateTemporaryXPI(xpiData, existingXPI) {
|
|
info("Delete and regenerate XPI for " + xpiData.id);
|
|
|
|
// Store the current name to check the xpi is correctly replaced.
|
|
const existingName = existingXPI.leafName;
|
|
info("Delete existing XPI named: " + existingName);
|
|
existingXPI.exists() && existingXPI.remove(false);
|
|
|
|
const xpiFile = createTemporaryXPI(xpiData);
|
|
// Check that the name of the new file is correct
|
|
if (xpiFile.leafName !== existingName) {
|
|
throw new Error(
|
|
"New XPI created with unexpected name: " + xpiFile.leafName
|
|
);
|
|
}
|
|
return xpiFile;
|
|
}
|
|
/* exported updateTemporaryXPI */
|
|
|
|
/**
|
|
* Install a fake temporary extension by creating a temporary in-memory XPI file.
|
|
* @return {File} the temporary extension XPI file created
|
|
*/
|
|
async function installTemporaryExtensionFromXPI(xpiData, document) {
|
|
const xpiFile = createTemporaryXPI(xpiData);
|
|
const extension = await installTemporaryExtension(
|
|
xpiFile,
|
|
xpiData.name,
|
|
document
|
|
);
|
|
|
|
info("Wait until the addon debug target appears");
|
|
await waitUntil(() => findDebugTargetByText(xpiData.name, document));
|
|
return { extension, xpiFile };
|
|
}
|
|
/* exported installTemporaryExtensionFromXPI */
|
|
|
|
async function removeTemporaryExtension(name, document) {
|
|
info(`Wait for removable extension with name: '${name}'`);
|
|
const buttonName = ".qa-temporary-extension-remove-button";
|
|
await waitUntil(() => {
|
|
const extension = findDebugTargetByText(name, document);
|
|
return extension && extension.querySelector(buttonName);
|
|
});
|
|
info(`Remove the temporary extension with name: '${name}'`);
|
|
const temporaryExtensionItem = findDebugTargetByText(name, document);
|
|
temporaryExtensionItem.querySelector(buttonName).click();
|
|
|
|
info("Wait until the debug target item disappears");
|
|
await waitUntil(() => !findDebugTargetByText(name, document));
|
|
}
|
|
/* exported removeTemporaryExtension */
|
|
|
|
async function removeExtension(id, name, document) {
|
|
info(
|
|
"Retrieve the extension instance from the addon manager, and uninstall it"
|
|
);
|
|
const extension = await AddonManager.getAddonByID(id);
|
|
extension.uninstall();
|
|
|
|
info("Wait until the addon disappears from about:debugging");
|
|
await waitUntil(() => !findDebugTargetByText(name, document));
|
|
}
|
|
/* exported removeExtension */
|
|
|
|
function prepareMockFilePicker(pathOrFile) {
|
|
const isFile = typeof pathOrFile.isFile === "function" && pathOrFile.isFile();
|
|
const file = isFile ? pathOrFile : _getSupportsFile(pathOrFile).file;
|
|
|
|
// Mock the file picker to select a test addon
|
|
const MockFilePicker = SpecialPowers.MockFilePicker;
|
|
MockFilePicker.init(window.browsingContext);
|
|
MockFilePicker.setFiles([file]);
|
|
}
|
|
/* exported prepareMockFilePicker */
|
|
|
|
function promiseBackgroundContextEvent(extensionId, eventName) {
|
|
const { Management } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Extension.sys.mjs"
|
|
);
|
|
|
|
return new Promise(resolve => {
|
|
Management.on(eventName, function listener(_evtName, context) {
|
|
if (context.extension.id === extensionId) {
|
|
Management.off(eventName, listener);
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function promiseBackgroundContextLoaded(extensionId) {
|
|
return promiseBackgroundContextEvent(extensionId, "proxy-context-load");
|
|
}
|
|
/* exported promiseBackgroundContextLoaded */
|
|
|
|
function promiseBackgroundContextUnloaded(extensionId) {
|
|
return promiseBackgroundContextEvent(extensionId, "proxy-context-unload");
|
|
}
|
|
/* exported promiseBackgroundContextUnloaded */
|
|
|
|
async function assertBackgroundStatus(
|
|
extName,
|
|
{ document, expectedStatus, targetElement }
|
|
) {
|
|
const target = targetElement || findDebugTargetByText(extName, document);
|
|
const getBackgroundStatusElement = () =>
|
|
target.querySelector(".extension-backgroundscript__status");
|
|
await waitFor(
|
|
() =>
|
|
getBackgroundStatusElement()?.classList.contains(
|
|
`extension-backgroundscript__status--${expectedStatus}`
|
|
),
|
|
`Wait ${extName} Background script status "${expectedStatus}" to be rendered`
|
|
);
|
|
}
|
|
/* exported assertBackgroundStatus */
|
|
|
|
function getExtensionInstance(extensionId) {
|
|
const policy = WebExtensionPolicy.getByID(extensionId);
|
|
ok(policy, `Got a WebExtensionPolicy instance for ${extensionId}`);
|
|
ok(policy.extension, `Got an Extension class instance for ${extensionId}`);
|
|
return policy.extension;
|
|
}
|
|
/* exported getExtensionInstance */
|
|
|
|
async function triggerExtensionEventPageIdleTimeout(extensionId) {
|
|
await getExtensionInstance(extensionId).terminateBackground();
|
|
}
|
|
/* exported triggerExtensionEventPageIdleTimeout */
|
|
|
|
async function wakeupExtensionEventPage(extensionId) {
|
|
await getExtensionInstance(extensionId).wakeupBackground();
|
|
}
|
|
/* exported wakeupExtensionEventPage */
|
|
|
|
function promiseTerminateBackgroundScriptIgnored(extensionId) {
|
|
const extension = getExtensionInstance(extensionId);
|
|
return new Promise(resolve => {
|
|
extension.once("background-script-suspend-ignored", resolve);
|
|
});
|
|
}
|
|
/* exported promiseTerminateBackgroundScriptIgnored */
|
|
|
|
async function promiseBackgroundStatusUpdate(window) {
|
|
waitForDispatch(
|
|
window.AboutDebugging.store,
|
|
"EXTENSION_BGSCRIPT_STATUS_UPDATED"
|
|
);
|
|
}
|
|
/* exported promiseBackgroundStatusUpdate */
|