fune/devtools/server/tests/browser/head.js
arthur.iakab 457fa2b8ab Backed out 7 changesets (bug 1473513) for failing devtools e.g. leakcheck | default process: 1618727 bytes leaked
Backed out changeset deb8812556ef (bug 1473513)
Backed out changeset 5bf38cfa04f9 (bug 1473513)
Backed out changeset 6e157bea362a (bug 1473513)
Backed out changeset 12eb1139a802 (bug 1473513)
Backed out changeset ce86ea60a31c (bug 1473513)
Backed out changeset 7acc52a7f81f (bug 1473513)
Backed out changeset 4e1e283b347e (bug 1473513)

--HG--
extra : rebase_source : a79787b05e0cf6c837e08c9541d559e4509b0deb
2018-08-22 18:23:46 +03:00

343 lines
10 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/. */
"use strict";
/* eslint no-unused-vars: [2, {"vars": "local"}] */
/* import-globals-from ../../../client/shared/test/shared-head.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
this);
const {DebuggerClient} = require("devtools/shared/client/debugger-client");
const {DebuggerServer} = require("devtools/server/main");
const PATH = "browser/devtools/server/tests/browser/";
const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/server/actors/storage.js,
// devtools/client/storage/ui.js and devtools/client/storage/test/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
// All tests are asynchronous.
waitForExplicitFinish();
/**
* Add a new test tab in the browser and load the given url.
* @param {String} url The url to be loaded in the new tab
* @return a promise that resolves to the new browser that the document
* is loaded in. Note that we cannot return the document
* directly, since this would be a CPOW in the e10s case,
* and Promises cannot be resolved with CPOWs (see bug 1233497).
*/
var addTab = async function(url) {
info(`Adding a new tab with URL: ${url}`);
const tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, url);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
info(`Tab added and URL ${url} loaded`);
return tab.linkedBrowser;
};
async function initAnimationsFrontForUrl(url) {
const {AnimationsFront} = require("devtools/shared/fronts/animation");
const {InspectorFront} = require("devtools/shared/fronts/inspector");
await addTab(url);
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const inspector = InspectorFront(client, form);
const walker = await inspector.getWalker();
const animations = AnimationsFront(client, form);
return {inspector, walker, animations, client};
}
async function initLayoutFrontForUrl(url) {
const {InspectorFront} = require("devtools/shared/fronts/inspector");
await addTab(url);
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const inspector = InspectorFront(client, form);
const walker = await inspector.getWalker();
const layout = await walker.getLayoutInspector();
return {inspector, walker, layout, client};
}
async function initAccessibilityFrontForUrl(url) {
const {AccessibilityFront} = require("devtools/shared/fronts/accessibility");
const {InspectorFront} = require("devtools/shared/fronts/inspector");
await addTab(url);
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const inspector = InspectorFront(client, form);
const walker = await inspector.getWalker();
const accessibility = AccessibilityFront(client, form);
await accessibility.bootstrap();
return {inspector, walker, accessibility, client};
}
function initDebuggerServer() {
try {
// Sometimes debugger server does not get destroyed correctly by previous
// tests.
DebuggerServer.destroy();
} catch (e) {
info(`DebuggerServer destroy error: ${e}\n${e.stack}`);
}
DebuggerServer.init();
DebuggerServer.registerAllActors();
}
async function initPerfFront() {
const {PerfFront} = require("devtools/shared/fronts/perf");
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
await waitUntilClientConnected(client);
const rootForm = await getRootForm(client);
const front = PerfFront(client, rootForm);
return {front, client};
}
/**
* Gets the RootActor form from a DebuggerClient.
* @param {DebuggerClient} client
* @return {RootActor} Resolves when connected.
*/
function getRootForm(client) {
return client.listTabs();
}
/**
* Wait until a DebuggerClient is connected.
* @param {DebuggerClient} client
* @return {Promise} Resolves when connected.
*/
function waitUntilClientConnected(client) {
return new Promise(resolve => {
client.addOneTimeListener("connected", resolve);
});
}
/**
* Connect a debugger client.
*
* @param {DebuggerClient}
* @return {Promise} Resolves to the targetActor form for the selected tab when the client
* is connected.
*/
function connectDebuggerClient(client) {
return client.connect()
.then(() => client.listTabs())
.then(tabs => {
return tabs.tabs[tabs.selected];
});
}
/**
* Wait for eventName on target.
* @param {Object} target An observable object that either supports on/off or
* addEventListener/removeEventListener
* @param {String} eventName
* @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
* @return A promise that resolves when the event has been handled
*/
function once(target, eventName, useCapture = false) {
info("Waiting for event: '" + eventName + "' on " + target + ".");
return new Promise(resolve => {
for (const [add, remove] of [
["addEventListener", "removeEventListener"],
["addListener", "removeListener"],
["on", "off"]
]) {
if ((add in target) && (remove in target)) {
target[add](eventName, function onEvent(...aArgs) {
info("Got event: '" + eventName + "' on " + target + ".");
target[remove](eventName, onEvent, useCapture);
resolve(...aArgs);
}, useCapture);
break;
}
}
});
}
/**
* Forces GC, CC and Shrinking GC to get rid of disconnected docshells and
* windows.
*/
function forceCollections() {
Cu.forceGC();
Cu.forceCC();
Cu.forceShrinkingGC();
}
registerCleanupFunction(function tearDown() {
Services.cookies.removeAll();
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
});
function idleWait(time) {
return DevToolsUtils.waitForTime(time);
}
function busyWait(time) {
const start = Date.now();
// eslint-disable-next-line
let stack;
while (Date.now() - start < time) {
stack = Components.stack;
}
}
/**
* Waits until a predicate returns true.
*
* @param function predicate
* Invoked once in a while until it returns true.
* @param number interval [optional]
* How often the predicate is invoked, in milliseconds.
*/
function waitUntil(predicate, interval = 10) {
if (predicate()) {
return Promise.resolve(true);
}
return new Promise(resolve => {
setTimeout(function() {
waitUntil(predicate).then(() => resolve(true));
}, interval);
});
}
function waitForMarkerType(front, types, predicate,
unpackFun = (name, data) => data.markers,
eventName = "timeline-data") {
types = [].concat(types);
predicate = predicate || function() {
return true;
};
let filteredMarkers = [];
const { promise, resolve } = defer();
info("Waiting for markers of type: " + types);
function handler(name, data) {
if (typeof name === "string" && name !== "markers") {
return;
}
const markers = unpackFun(name, data);
info("Got markers: " + JSON.stringify(markers, null, 2));
filteredMarkers = filteredMarkers.concat(
markers.filter(m => types.includes(m.name)));
if (types.every(t => filteredMarkers.some(m => m.name === t)) &&
predicate(filteredMarkers)) {
front.off(eventName, handler);
resolve(filteredMarkers);
}
}
front.on(eventName, handler);
return promise;
}
function getCookieId(name, domain, path) {
return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`;
}
/**
* Trigger DOM activity and wait for the corresponding accessibility event.
* @param {Object} emitter Devtools event emitter, usually a front.
* @param {Sting} name Accessibility event in question.
* @param {Function} handler Accessibility event handler function with checks.
* @param {Promise} task A promise that resolves when DOM activity is done.
*/
async function emitA11yEvent(emitter, name, handler, task) {
const promise = emitter.once(name, handler);
await task();
await promise;
}
/**
* Check that accessibilty front is correct and its attributes are also
* up-to-date.
* @param {Object} front Accessibility front to be tested.
* @param {Object} expected A map of a11y front properties to be verified.
* @param {Object} expectedFront Expected accessibility front.
*/
function checkA11yFront(front, expected, expectedFront) {
ok(front, "The accessibility front is created");
if (expectedFront) {
is(front, expectedFront, "Matching accessibility front");
}
for (const key in expected) {
if (["actions", "states", "attributes"].includes(key)) {
SimpleTest.isDeeply(front[key], expected[key],
`Accessible Front has correct ${key}`);
} else {
is(front[key], expected[key], `accessibility front has correct ${key}`);
}
}
}
function getA11yInitOrShutdownPromise() {
return new Promise(resolve => {
const observe = (subject, topic, data) => {
Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
resolve(data);
};
Services.obs.addObserver(observe, "a11y-init-or-shutdown");
});
}
/**
* Wait for accessibility service to shut down. We consider it shut down when
* an "a11y-init-or-shutdown" event is received with a value of "0".
*/
async function waitForA11yShutdown() {
if (!Services.appinfo.accessibilityEnabled) {
return;
}
await getA11yInitOrShutdownPromise().then(data =>
data === "0" ? Promise.resolve() : Promise.reject());
}
/**
* Wait for accessibility service to initialize. We consider it initialized when
* an "a11y-init-or-shutdown" event is received with a value of "1".
*/
async function waitForA11yInit() {
if (Services.appinfo.accessibilityEnabled) {
return;
}
await getA11yInitOrShutdownPromise().then(data =>
data === "1" ? Promise.resolve() : Promise.reject());
}