Source code

Revision control

Copy as Markdown

Other Tools

/* 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, {
});
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);
}
};