Files
tubestation/devtools/client/aboutdebugging/test/head.js
Kumar McMillan f908e5cfaa Bug 1246030 - Allow reloading an add-on in about:debugging. r=ochameau
MozReview-Commit-ID: Lh2iwPgmlhU
2016-03-31 12:01:02 -05:00

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();
});
});
}