257 lines
7.7 KiB
JavaScript
257 lines
7.7 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
/* eslint-env browser */
|
|
/* eslint-disable mozilla/no-cpows-in-tests */
|
|
/* exported openAboutDebugging, closeAboutDebugging, installAddon,
|
|
uninstallAddon, waitForMutation, assertHasTarget,
|
|
waitForInitialAddonList, waitForServiceWorkerRegistered,
|
|
unregisterServiceWorker */
|
|
/* global sendAsyncMessage */
|
|
|
|
"use strict";
|
|
|
|
var { utils: Cu, classes: Cc, interfaces: Ci } = Components;
|
|
|
|
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
|
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
|
const Services = require("Services");
|
|
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
|
DevToolsUtils.testing = true;
|
|
|
|
const CHROME_ROOT = gTestPath.substr(0, gTestPath.lastIndexOf("/") + 1);
|
|
|
|
registerCleanupFunction(() => {
|
|
DevToolsUtils.testing = false;
|
|
});
|
|
|
|
function* openAboutDebugging(page) {
|
|
info("opening about:debugging");
|
|
let url = "about:debugging";
|
|
if (page) {
|
|
url += "#" + page;
|
|
}
|
|
|
|
let tab = yield addTab(url);
|
|
let browser = tab.linkedBrowser;
|
|
let document = browser.contentDocument;
|
|
|
|
if (!document.querySelector(".app")) {
|
|
yield waitForMutation(document.body, { childList: true });
|
|
}
|
|
|
|
return { tab, document };
|
|
}
|
|
|
|
function closeAboutDebugging(tab) {
|
|
info("Closing about:debugging");
|
|
return removeTab(tab);
|
|
}
|
|
|
|
function addTab(url, win) {
|
|
info("Adding tab: " + url);
|
|
|
|
return new Promise(done => {
|
|
let targetWindow = win || window;
|
|
let targetBrowser = targetWindow.gBrowser;
|
|
|
|
targetWindow.focus();
|
|
let tab = targetBrowser.selectedTab = targetBrowser.addTab(url);
|
|
let linkedBrowser = tab.linkedBrowser;
|
|
|
|
linkedBrowser.addEventListener("load", function onLoad() {
|
|
linkedBrowser.removeEventListener("load", onLoad, true);
|
|
info("Tab added and finished loading: " + url);
|
|
done(tab);
|
|
}, true);
|
|
});
|
|
}
|
|
|
|
function removeTab(tab, win) {
|
|
info("Removing tab.");
|
|
|
|
return new Promise(done => {
|
|
let targetWindow = win || window;
|
|
let targetBrowser = targetWindow.gBrowser;
|
|
let tabContainer = targetBrowser.tabContainer;
|
|
|
|
tabContainer.addEventListener("TabClose", function onClose() {
|
|
tabContainer.removeEventListener("TabClose", onClose, false);
|
|
info("Tab removed and finished closing.");
|
|
done();
|
|
}, false);
|
|
|
|
targetBrowser.removeTab(tab);
|
|
});
|
|
}
|
|
|
|
function getSupportsFile(path) {
|
|
let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
|
|
.getService(Ci.nsIChromeRegistry);
|
|
let uri = Services.io.newURI(CHROME_ROOT + path, null, null);
|
|
let fileurl = cr.convertChromeURL(uri);
|
|
return fileurl.QueryInterface(Ci.nsIFileURL);
|
|
}
|
|
|
|
function* installAddon(document, path, name, evt) {
|
|
// Mock the file picker to select a test addon
|
|
let MockFilePicker = SpecialPowers.MockFilePicker;
|
|
MockFilePicker.init(null);
|
|
let file = getSupportsFile(path);
|
|
MockFilePicker.returnFiles = [file.file];
|
|
|
|
let addonList = document.querySelector("#addons .targets");
|
|
let addonListMutation = waitForMutation(addonList, { childList: true });
|
|
|
|
// Wait for a message sent by the addon's bootstrap.js file
|
|
let onAddonInstalled = new Promise(done => {
|
|
Services.obs.addObserver(function listener() {
|
|
Services.obs.removeObserver(listener, evt);
|
|
|
|
done();
|
|
}, evt, false);
|
|
});
|
|
// Trigger the file picker by clicking on the button
|
|
document.getElementById("load-addon-from-file").click();
|
|
|
|
yield onAddonInstalled;
|
|
ok(true, "Addon installed and running its bootstrap.js file");
|
|
|
|
// Check that the addon appears in the UI
|
|
yield addonListMutation;
|
|
let names = [...addonList.querySelectorAll(".target-name")];
|
|
names = names.map(element => element.textContent);
|
|
ok(names.includes(name),
|
|
"The addon name appears in the list of addons: " + names);
|
|
}
|
|
|
|
function* uninstallAddon(document, addonId, addonName) {
|
|
let addonList = document.querySelector("#addons .targets");
|
|
let addonListMutation = waitForMutation(addonList, { childList: true });
|
|
|
|
// Now uninstall this addon
|
|
yield new Promise(done => {
|
|
AddonManager.getAddonByID(addonId, addon => {
|
|
let listener = {
|
|
onUninstalled: function(uninstalledAddon) {
|
|
if (uninstalledAddon != addon) {
|
|
return;
|
|
}
|
|
AddonManager.removeAddonListener(listener);
|
|
|
|
done();
|
|
}
|
|
};
|
|
AddonManager.addAddonListener(listener);
|
|
addon.uninstall();
|
|
});
|
|
});
|
|
|
|
// Ensure that the UI removes the addon from the list
|
|
yield addonListMutation;
|
|
let names = [...addonList.querySelectorAll(".target-name")];
|
|
names = names.map(element => element.textContent);
|
|
ok(!names.includes(addonName),
|
|
"After uninstall, the addon name disappears from the list of addons: "
|
|
+ names);
|
|
}
|
|
|
|
/**
|
|
* Returns a promise that will resolve when the add-on list has been updated.
|
|
*
|
|
* @param {Node} document
|
|
* @return {Promise}
|
|
*/
|
|
function waitForInitialAddonList(document) {
|
|
// Wait for actor to load initial list of add-ons.
|
|
return waitForMutation(document.querySelector("#addons .targets"),
|
|
{ childList: true });
|
|
}
|
|
|
|
/**
|
|
* Returns a promise that will resolve after receiving a mutation matching the
|
|
* provided mutation options on the provided target.
|
|
* @param {Node} target
|
|
* @param {Object} mutationOptions
|
|
* @return {Promise}
|
|
*/
|
|
function waitForMutation(target, mutationOptions) {
|
|
return new Promise(resolve => {
|
|
let observer = new MutationObserver(() => {
|
|
observer.disconnect();
|
|
resolve();
|
|
});
|
|
observer.observe(target, mutationOptions);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Checks if an about:debugging TargetList element contains a Target element
|
|
* corresponding to the specified name.
|
|
* @param {Boolean} expected
|
|
* @param {Document} document
|
|
* @param {String} type
|
|
* @param {String} name
|
|
*/
|
|
function assertHasTarget(expected, document, type, name) {
|
|
let names = [...document.querySelectorAll("#" + type + " .target-name")];
|
|
names = names.map(element => element.textContent);
|
|
is(names.includes(name), expected,
|
|
"The " + type + " url appears in the list: " + names);
|
|
}
|
|
|
|
/**
|
|
* Returns a promise that will resolve after the service worker in the page
|
|
* has successfully registered itself.
|
|
* @param {Tab} tab
|
|
*/
|
|
function waitForServiceWorkerRegistered(tab) {
|
|
// Make the test page notify us when the service worker is registered.
|
|
let frameScript = function() {
|
|
// Retrieve the `sw` promise created in the html page.
|
|
let { sw } = content.wrappedJSObject;
|
|
sw.then(function(registration) {
|
|
sendAsyncMessage("sw-registered");
|
|
});
|
|
};
|
|
let mm = tab.linkedBrowser.messageManager;
|
|
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
|
|
|
return new Promise(done => {
|
|
mm.addMessageListener("sw-registered", function listener() {
|
|
mm.removeMessageListener("sw-registered", listener);
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Asks the service worker within the test page to unregister, and returns a
|
|
* promise that will resolve when it has successfully unregistered itself.
|
|
* @param {Tab} tab
|
|
*/
|
|
function unregisterServiceWorker(tab) {
|
|
// Use message manager to work with e10s.
|
|
let frameScript = function() {
|
|
// Retrieve the `sw` promise created in the html page.
|
|
let { sw } = content.wrappedJSObject;
|
|
sw.then(function(registration) {
|
|
registration.unregister().then(function() {
|
|
sendAsyncMessage("sw-unregistered");
|
|
},
|
|
function(e) {
|
|
dump("SW not unregistered; " + e + "\n");
|
|
});
|
|
});
|
|
};
|
|
let mm = tab.linkedBrowser.messageManager;
|
|
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
|
|
|
return new Promise(done => {
|
|
mm.addMessageListener("sw-unregistered", function listener() {
|
|
mm.removeMessageListener("sw-unregistered", listener);
|
|
done();
|
|
});
|
|
});
|
|
}
|