/* 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/. */
/**
* The purpose of this test it to measure the performance of a
* content process startup.
*
* In practice it measures a bit more than the content process startup. First
* the parent process starts the clock and requests a new tab with a simple
* page and then the child stops the clock in the frame script that will be
* able to process the URL to handle. So it does measure a few things
* pre process creation (browser element and tab creation on parent side) but
* does not measure the part where we actually parse and render the page on
* the content side, just the overhead of spawning a new content process.
*/
ChromeUtils.defineESModuleGetters(
this, {
TalosParentProfiler:
"resource://talos-powers/TalosParentProfiler.sys.mjs",
});
const PREALLOCATED_PREF =
"dom.ipc.processPrelaunch.enabled";
const MESSAGES = [
"CPStartup:Go",
"CPStartup:BrowserChildReady"];
const BROWSER_FLUSH_TOPIC =
"sessionstore-browser-shutdown-flush";
let domainID = 1;
/* global ExtensionAPI */
this.cpstartup =
class extends ExtensionAPI {
onStartup() {
for (let msgName of MESSAGES) {
Services.mm.addMessageListener(msgName,
this);
}
this.framescriptURL =
this.extension.baseURI.resolve(
"/framescript.js");
Services.mm.loadFrameScript(
this.framescriptURL,
true);
this.originalPreallocatedEnabled =
Services.prefs.getBoolPref(PREALLOCATED_PREF);
Services.prefs.setBoolPref(PREALLOCATED_PREF,
false);
this.readyCallback =
null;
this.startStamp =
null;
this.tab =
null;
}
onShutdown() {
Services.prefs.setBoolPref(
PREALLOCATED_PREF,
this.originalPreallocatedEnabled
);
Services.mm.removeDelayedFrameScript(
this.framescriptURL);
for (let msgName of MESSAGES) {
Services.mm.removeMessageListener(msgName,
this);
}
}
receiveMessage(msg) {
let browser = msg.target;
let gBrowser = browser.ownerGlobal.gBrowser;
switch (msg.name) {
case "CPStartup:Go": {
this.openTab(gBrowser, msg.data.target).then(results =>
this.reportResults(results)
);
break;
}
case "CPStartup:BrowserChildReady": {
// Content has reported that it's ready to process an URL.
if (!
this.readyCallback) {
throw new Error(
"Content:BrowserChildReady fired without a readyCallback set"
);
}
let tab = gBrowser.getTabForBrowser(browser);
if (tab !=
this.tab) {
// Let's ignore the message if it's not from the tab we've just opened.
break;
}
// The child stopped the timer when it was ready to process the first URL, it's time to
// calculate the difference and report it.
let delta = msg.data.time -
this.startStamp;
this.readyCallback({ tab, delta });
break;
}
}
}
async openTab(gBrowser, url) {
// Start the timer and the profiler right before the tab open on the parent side.
TalosParentProfiler.subtestStart(
"cpstartup: Begin Tab Open");
let startTime = Cu.now();
this.startStamp = Services.telemetry.msSystemNow();
let newDomainURL = url.replace(
/http:\/\/127\.0\.0\.1:[0-9]+/,
"http://domain_" + domainID++
);
this.tab = gBrowser.selectedTab = gBrowser.addTrustedTab(newDomainURL);
let { tab, delta } = await
this.whenTabReady();
TalosParentProfiler.subtestEnd(
"cpstartup: Tab Open", startTime);
await
this.removeTab(tab);
return delta;
}
whenTabReady() {
return new Promise(resolve => {
this.readyCallback = resolve;
});
}
removeTab(tab) {
return new Promise(resolve => {
let browser = tab.linkedBrowser;
let observer = subject => {
if (subject === browser) {
Services.obs.removeObserver(observer, BROWSER_FLUSH_TOPIC);
resolve();
}
};
Services.obs.addObserver(observer, BROWSER_FLUSH_TOPIC);
tab.ownerGlobal.gBrowser.removeTab(tab);
});
}
reportResults(results) {
Services.mm.broadcastAsyncMessage(
"CPStartup:FinalResults", results);
}
};