mirror of
https://github.com/ManchildProductions/UXP-Fixed.git
synced 2026-06-25 16:51:12 +00:00
312 lines
9.6 KiB
JavaScript
312 lines
9.6 KiB
JavaScript
const TESTPAGE = `${SECURE_TESTROOT}webapi_checkavailable.html`;
|
|
const XPI_URL = `${SECURE_TESTROOT}addons/browser_webapi_install.xpi`;
|
|
const XPI_SHA = "sha256:d4bab17ff9ba5f635e97c84021f4c527c502250d62ab7f6e6c9e8ee28822f772";
|
|
|
|
const ID = "webapi_install@tests.mozilla.org";
|
|
// eh, would be good to just stat the real file instead of this...
|
|
const XPI_LEN = 4782;
|
|
|
|
function waitForClear() {
|
|
const MSG = "WebAPICleanup";
|
|
return new Promise(resolve => {
|
|
let listener = {
|
|
receiveMessage: function(msg) {
|
|
if (msg.name == MSG) {
|
|
Services.mm.removeMessageListener(MSG, listener);
|
|
resolve();
|
|
}
|
|
}
|
|
};
|
|
|
|
Services.mm.addMessageListener(MSG, listener, true);
|
|
});
|
|
}
|
|
|
|
add_task(function* setup() {
|
|
yield SpecialPowers.pushPrefEnv({
|
|
set: [["extensions.webapi.testing", true],
|
|
["extensions.install.requireBuiltInCerts", false]],
|
|
});
|
|
info("added preferences");
|
|
});
|
|
|
|
// Wrapper around a common task to run in the content process to test
|
|
// the mozAddonManager API. Takes a URL for the XPI to install and an
|
|
// array of steps, each of which can either be an action to take
|
|
// (i.e., start or cancel the install) or an install event to wait for.
|
|
// Steps that look for a specific event may also include a "props" property
|
|
// with properties that the AddonInstall object is expected to have when
|
|
// that event is triggered.
|
|
function* testInstall(browser, args, steps, description) {
|
|
let success = yield ContentTask.spawn(browser, {args, steps}, function* (opts) {
|
|
let { args, steps } = opts;
|
|
let install = yield content.navigator.mozAddonManager.createInstall(args);
|
|
if (!install) {
|
|
yield Promise.reject("createInstall() did not return an install object");
|
|
}
|
|
|
|
// Check that the initial state of the AddonInstall is sane.
|
|
if (install.state != "STATE_AVAILABLE") {
|
|
yield Promise.reject("new install should be in STATE_AVAILABLE");
|
|
}
|
|
if (install.error != null) {
|
|
yield Promise.reject("new install should have null error");
|
|
}
|
|
|
|
const events = [
|
|
"onDownloadStarted",
|
|
"onDownloadProgress",
|
|
"onDownloadEnded",
|
|
"onDownloadCancelled",
|
|
"onDownloadFailed",
|
|
"onInstallStarted",
|
|
"onInstallEnded",
|
|
"onInstallCancelled",
|
|
"onInstallFailed",
|
|
];
|
|
let eventWaiter = null;
|
|
let receivedEvents = [];
|
|
let prevEvent = null;
|
|
events.forEach(event => {
|
|
install.addEventListener(event, e => {
|
|
receivedEvents.push({
|
|
event,
|
|
state: install.state,
|
|
error: install.error,
|
|
progress: install.progress,
|
|
maxProgress: install.maxProgress,
|
|
});
|
|
if (eventWaiter) {
|
|
eventWaiter();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Returns a promise that is resolved when the given event occurs
|
|
// or rejects if a different event comes first or if props is supplied
|
|
// and properties on the AddonInstall don't match those in props.
|
|
function expectEvent(event, props) {
|
|
return new Promise((resolve, reject) => {
|
|
function check() {
|
|
let received = receivedEvents.shift();
|
|
// Skip any repeated onDownloadProgress events.
|
|
while (received &&
|
|
received.event == prevEvent &&
|
|
prevEvent == "onDownloadProgress") {
|
|
received = receivedEvents.shift();
|
|
}
|
|
// Wait for more events if we skipped all there were.
|
|
if (!received) {
|
|
eventWaiter = () => {
|
|
eventWaiter = null;
|
|
check();
|
|
}
|
|
return;
|
|
}
|
|
prevEvent = received.event;
|
|
if (received.event != event) {
|
|
let err = new Error(`expected ${event} but got ${received.event}`);
|
|
reject(err);
|
|
}
|
|
if (props) {
|
|
for (let key of Object.keys(props)) {
|
|
if (received[key] != props[key]) {
|
|
throw new Error(`AddonInstall property ${key} was ${received[key]} but expected ${props[key]}`);
|
|
}
|
|
}
|
|
}
|
|
resolve();
|
|
}
|
|
check();
|
|
});
|
|
}
|
|
|
|
while (steps.length > 0) {
|
|
let nextStep = steps.shift();
|
|
if (nextStep.action) {
|
|
if (nextStep.action == "install") {
|
|
yield install.install();
|
|
} else if (nextStep.action == "cancel") {
|
|
yield install.cancel();
|
|
} else {
|
|
throw new Error(`unknown action ${nextStep.action}`);
|
|
}
|
|
} else {
|
|
yield expectEvent(nextStep.event, nextStep.props);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
is(success, true, description);
|
|
}
|
|
|
|
function makeInstallTest(task) {
|
|
return function*() {
|
|
// withNewTab() will close the test tab before returning, at which point
|
|
// the cleanup event will come from the content process. We need to see
|
|
// that event but don't want to race to install a listener for it after
|
|
// the tab is closed. So set up the listener now but don't yield the
|
|
// listening promise until below.
|
|
let clearPromise = waitForClear();
|
|
|
|
yield BrowserTestUtils.withNewTab(TESTPAGE, task);
|
|
|
|
yield clearPromise;
|
|
is(AddonManager.webAPI.installs.size, 0, "AddonInstall was cleaned up");
|
|
};
|
|
}
|
|
|
|
function makeRegularTest(options, what) {
|
|
return makeInstallTest(function* (browser) {
|
|
let steps = [
|
|
{action: "install"},
|
|
{
|
|
event: "onDownloadStarted",
|
|
props: {state: "STATE_DOWNLOADING"},
|
|
},
|
|
{
|
|
event: "onDownloadProgress",
|
|
props: {maxProgress: XPI_LEN},
|
|
},
|
|
{
|
|
event: "onDownloadEnded",
|
|
props: {
|
|
state: "STATE_DOWNLOADED",
|
|
progress: XPI_LEN,
|
|
maxProgress: XPI_LEN,
|
|
},
|
|
},
|
|
{
|
|
event: "onInstallStarted",
|
|
props: {state: "STATE_INSTALLING"},
|
|
},
|
|
{
|
|
event: "onInstallEnded",
|
|
props: {state: "STATE_INSTALLED"},
|
|
},
|
|
];
|
|
|
|
yield testInstall(browser, options, steps, what);
|
|
|
|
let version = Services.prefs.getIntPref("webapitest.active_version");
|
|
is(version, 1, "the install really did work");
|
|
|
|
// Sanity check to ensure that the test in makeInstallTest() that
|
|
// installs.size == 0 means we actually did clean up.
|
|
ok(AddonManager.webAPI.installs.size > 0, "webAPI is tracking the AddonInstall");
|
|
|
|
let addons = yield promiseAddonsByIDs([ID]);
|
|
isnot(addons[0], null, "Found the addon");
|
|
|
|
yield addons[0].uninstall();
|
|
|
|
addons = yield promiseAddonsByIDs([ID]);
|
|
is(addons[0], null, "Addon was uninstalled");
|
|
});
|
|
}
|
|
|
|
add_task(makeRegularTest({url: XPI_URL}, "a basic install works"));
|
|
add_task(makeRegularTest({url: XPI_URL, hash: null}, "install with hash=null works"));
|
|
add_task(makeRegularTest({url: XPI_URL, hash: ""}, "install with empty string for hash works"));
|
|
add_task(makeRegularTest({url: XPI_URL, hash: XPI_SHA}, "install with hash works"));
|
|
|
|
add_task(makeInstallTest(function* (browser) {
|
|
let steps = [
|
|
{action: "cancel"},
|
|
{
|
|
event: "onDownloadCancelled",
|
|
props: {
|
|
state: "STATE_CANCELLED",
|
|
error: null,
|
|
},
|
|
}
|
|
];
|
|
|
|
yield testInstall(browser, {url: XPI_URL}, steps, "canceling an install works");
|
|
|
|
let addons = yield promiseAddonsByIDs([ID]);
|
|
is(addons[0], null, "The addon was not installed");
|
|
|
|
ok(AddonManager.webAPI.installs.size > 0, "webAPI is tracking the AddonInstall");
|
|
}));
|
|
|
|
add_task(makeInstallTest(function* (browser) {
|
|
let steps = [
|
|
{action: "install"},
|
|
{
|
|
event: "onDownloadStarted",
|
|
props: {state: "STATE_DOWNLOADING"},
|
|
},
|
|
{event: "onDownloadProgress"},
|
|
{
|
|
event: "onDownloadFailed",
|
|
props: {
|
|
state: "STATE_DOWNLOAD_FAILED",
|
|
error: "ERROR_NETWORK_FAILURE",
|
|
},
|
|
}
|
|
];
|
|
|
|
yield testInstall(browser, {url: XPI_URL + "bogus"}, steps, "install of a bad url fails");
|
|
|
|
let addons = yield promiseAddonsByIDs([ID]);
|
|
is(addons[0], null, "The addon was not installed");
|
|
|
|
ok(AddonManager.webAPI.installs.size > 0, "webAPI is tracking the AddonInstall");
|
|
}));
|
|
|
|
add_task(makeInstallTest(function* (browser) {
|
|
let steps = [
|
|
{action: "install"},
|
|
{
|
|
event: "onDownloadStarted",
|
|
props: {state: "STATE_DOWNLOADING"},
|
|
},
|
|
{event: "onDownloadProgress"},
|
|
{
|
|
event: "onDownloadFailed",
|
|
props: {
|
|
state: "STATE_DOWNLOAD_FAILED",
|
|
error: "ERROR_INCORRECT_HASH",
|
|
},
|
|
}
|
|
];
|
|
|
|
yield testInstall(browser, {url: XPI_URL, hash: "sha256:bogus"}, steps, "install with bad hash fails");
|
|
|
|
let addons = yield promiseAddonsByIDs([ID]);
|
|
is(addons[0], null, "The addon was not installed");
|
|
|
|
ok(AddonManager.webAPI.installs.size > 0, "webAPI is tracking the AddonInstall");
|
|
}));
|
|
|
|
add_task(function* test_permissions() {
|
|
function testBadUrl(url, pattern, successMessage) {
|
|
return BrowserTestUtils.withNewTab(TESTPAGE, function* (browser) {
|
|
let result = yield ContentTask.spawn(browser, {url, pattern}, function (opts) {
|
|
return new Promise(resolve => {
|
|
content.navigator.mozAddonManager.createInstall({url: opts.url})
|
|
.then(() => {
|
|
resolve({success: false, message: "createInstall should not have succeeded"});
|
|
}, err => {
|
|
if (err.message.match(new RegExp(opts.pattern))) {
|
|
resolve({success: true});
|
|
}
|
|
resolve({success: false, message: `Wrong error message: ${err.message}`});
|
|
});
|
|
});
|
|
});
|
|
is(result.success, true, result.message || successMessage);
|
|
});
|
|
}
|
|
|
|
yield testBadUrl("i am not a url", "NS_ERROR_MALFORMED_URI",
|
|
"Installing from an unparseable URL fails");
|
|
|
|
yield testBadUrl("https://addons.not-really-mozilla.org/impostor.xpi",
|
|
"not permitted",
|
|
"Installing from non-approved URL fails");
|
|
});
|