Bug 1336389 - Talos test for content process startup time. r=mconley
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Wed, 27 Sep 2017 18:31:53 +0200
changeset 434077 be61a3dc120a6ebdbc213525fea45eedfded57ef
parent 434076 da1f2099ff448dfc2f0d533c6e89c6223996c49b
child 434078 f5d781a4d148f7e81bcc1e5d84f817315c2f78ac
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1336389
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1336389 - Talos test for content process startup time. r=mconley
testing/moz.build
testing/talos/talos.json
testing/talos/talos/test.py
testing/talos/talos/tests/cpstartup/bootstrap.js
testing/talos/talos/tests/cpstartup/chrome.manifest
testing/talos/talos/tests/cpstartup/content/cpstartup.html
testing/talos/talos/tests/cpstartup/content/target.html
testing/talos/talos/tests/cpstartup/cpstartup-signed.xpi
testing/talos/talos/tests/cpstartup/cpstartup.manifest
testing/talos/talos/tests/cpstartup/install.rdf
toolkit/content/browser-child.js
--- a/testing/moz.build
+++ b/testing/moz.build
@@ -41,16 +41,19 @@ with Files("talos/talos/tests/svg*"):
     BUG_COMPONENT = ("Core", "SVG")
 
 with Files("talos/talos/tests/scroll/**"):
     BUG_COMPONENT = ("Core", "Graphics")
 
 with Files("talos/talos/tests/tabpaint/**"):
     BUG_COMPONENT = ("Firefox", "Tabbed Browser")
 
+with Files("talos/talos/tests/cpstartup/**"):
+    BUG_COMPONENT = ("Firefox", "Tabbed Browser")
+
 with Files("talos/talos/tests/tart/**"):
     BUG_COMPONENT = ("Firefox", "Tabbed Browser")
 
 with Files("talos/talos/tests/tabswitch/**"):
     BUG_COMPONENT = ("Firefox", "Tabbed Browser")
 
 with Files("talos/talos/tests/video/**"):
     BUG_COMPONENT = ("Core", "Audio/Video: Playback")
--- a/testing/talos/talos.json
+++ b/testing/talos/talos.json
@@ -10,21 +10,21 @@
         "dromaeojs-e10s": {
             "tests": ["dromaeo_css", "kraken"]
         },
         "dromaeojs-stylo-disabled-e10s": {
             "talos_options": ["--disable-stylo"],
             "tests": ["dromaeo_css", "kraken"]
         },
         "other-e10s": {
-            "tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint"]
+            "tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup"]
         },
         "other-stylo-disabled-e10s": {
             "talos_options": ["--disable-stylo"],
-            "tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint"]
+            "tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup"]
         },
         "g1-e10s": {
             "tests": ["tp5o_scroll", "glterrain"],
             "pagesets_name": "tp5n.zip"
         },
         "g1-stylo-disabled-e10s": {
             "tests": ["tp5o_scroll", "glterrain"],
             "talos_options": ["--disable-stylo"],
--- a/testing/talos/talos/test.py
+++ b/testing/talos/talos/test.py
@@ -277,16 +277,40 @@ class tpaint(PageloaderTest):
     gecko_profile_entries = 2000000
     tpmozafterpaint = True
     filters = filter.ignore_first.prepare(5) + filter.median.prepare()
     unit = 'ms'
     preferences = {'security.data_uri.block_toplevel_data_uri_navigations': False}
 
 
 @register_test()
+class cpstartup(PageloaderTest):
+    """
+    Tests the amount of time it takes to start up a new content process and
+    initialize it to the point where it can start processing incoming URLs
+    to load.
+    """
+    extensions = '${talos}/tests/cpstartup'
+    tpmanifest = '${talos}/tests/cpstartup/cpstartup.manifest'
+    tppagecycles = 20
+    gecko_profile_entries = 1000000
+    tploadnocache = True
+    unit = 'ms'
+    preferences = {
+        # By default, Talos is configured to open links from
+        # content in new windows. We're overriding them so that
+        # they open in new tabs instead.
+        # See http://kb.mozillazine.org/Browser.link.open_newwindow
+        # and http://kb.mozillazine.org/Browser.link.open_newwindow.restriction
+        'browser.link.open_newwindow': 3,
+        'browser.link.open_newwindow.restriction': 2,
+    }
+
+
+@register_test()
 class tabpaint(PageloaderTest):
     """
     Tests the amount of time it takes to open new tabs, triggered from
     both the parent process and the content process.
     """
     extensions = '${talos}/tests/tabpaint'
     tpmanifest = '${talos}/tests/tabpaint/tabpaint.manifest'
     tppagecycles = 20
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/cpstartup/bootstrap.js
@@ -0,0 +1,144 @@
+/* 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/. */
+
+var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+const PREALLOCATED_PREF = "dom.ipc.processPrelaunch.enabled";
+
+const TARGET_URI = "chrome://cpstartup/content/target.html";
+
+/**
+ * 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.
+ */
+var CPStartup = {
+  MESSAGES: [
+    "CPStartup:Go",
+    "Content:BrowserChildReady",
+  ],
+
+  readyCallback: null,
+
+  startStamp: null,
+
+  tab: null,
+  /**
+   * Shortcut to getting at the TalosParentProfiler.
+   */
+  get Profiler() {
+    delete this.Profiler;
+    let context = {};
+    Services.scriptloader.loadSubScript("chrome://talos-powers-content/content/TalosParentProfiler.js", context);
+    return this.Profiler = context.TalosParentProfiler;
+  },
+
+  init() {
+    for (let msgName of this.MESSAGES) {
+      Services.mm.addMessageListener(msgName, this);
+    }
+
+    this.originalPreallocatedEnabled = Services.prefs.getBoolPref(PREALLOCATED_PREF);
+    Services.prefs.setBoolPref(PREALLOCATED_PREF, false);
+  },
+
+  uninit() {
+    for (let msgName of this.MESSAGES) {
+      Services.mm.removeMessageListener(msgName, this);
+    }
+
+    Services.prefs.setBoolPref(PREALLOCATED_PREF, this.originalPreallocatedEnabled);
+  },
+
+  receiveMessage(msg) {
+    let browser = msg.target;
+    let gBrowser = browser.ownerGlobal.gBrowser;
+
+    switch (msg.name) {
+      case "CPStartup:Go": {
+        this.openTab(gBrowser).then(results =>
+          this.reportResults(results));
+        break;
+      }
+
+      case "Content: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;
+      }
+    }
+  },
+
+  openTab(gBrowser) {
+    return new Promise((resolve) => {
+      // Start the timer and the profiler right before the tab open on the parent side.
+      this.Profiler.resume("tab opening starts");
+      this.startStamp = Services.telemetry.msSystemNow();
+      this.tab = gBrowser.selectedTab = gBrowser.addTab(TARGET_URI, {
+        triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+      });
+
+      this.whenTabReady().then(({tab, delta}) => {
+        this.Profiler.pause("tab opening end");
+        this.removeTab(tab).then(() => {
+          resolve(delta);
+        });
+      });
+    });
+  },
+
+  whenTabReady() {
+    return new Promise((resolve) => {
+      this.readyCallback = resolve;
+    });
+  },
+
+  removeTab(tab) {
+    return new Promise((resolve) => {
+      let {messageManager: mm, frameLoader} = tab.linkedBrowser;
+      mm.addMessageListener("SessionStore:update", function onMessage(msg) {
+        if (msg.targetFrameLoader == frameLoader && msg.data.isFinal) {
+          mm.removeMessageListener("SessionStore:update", onMessage);
+          resolve();
+        }
+      }, true);
+
+      tab.ownerGlobal.gBrowser.removeTab(tab);
+    });
+  },
+
+  reportResults(results) {
+    Services.mm.broadcastAsyncMessage("CPStartup:FinalResults", results);
+  },
+};
+
+function install(aData, aReason) {}
+
+function startup(aData, aReason) {
+  CPStartup.init();
+}
+
+function shutdown(aData, aReason) {
+  CPStartup.uninit();
+}
+
+function uninstall(aData, aReason) {}
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/cpstartup/chrome.manifest
@@ -0,0 +1,1 @@
+content cpstartup content/ remoteenabled=yes contentaccessible=yes
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/cpstartup/content/cpstartup.html
@@ -0,0 +1,31 @@
+<html>
+  <head>
+    <script>
+      const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+      function init() {
+        if (document.location.hash.indexOf("#auto") == 0) {
+          let mm = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIWebNavigation)
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIContentFrameMessageManager);
+
+          mm.addMessageListener("CPStartup:FinalResults", function onResults(msg) {
+            mm.removeMessageListener("CPStartup:FinalResults", onResults);
+            let results = msg.data;
+
+            tpRecordTime(results, 0, "content-process-startup");
+          });
+
+          mm.sendAsyncMessage("CPStartup:Go");
+        }
+      }
+
+    </script>
+  </head>
+  <body onload="init();">
+    Hello, Talos!
+
+    <a href="#" id="target" target="_blank">I'll open a new tab</a>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/cpstartup/content/target.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Content Process Startup</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
+</head>
+<body>
+<p>Content Process Startup</p>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..03b22a000895f010d76e8445d5c8477763c3350d
GIT binary patch
literal 3792
zc$|$_cQoAF79K5XNYtn?CdwEl>L6-Fi(Y~th%&m-W+E89M2Ox65j9-BL`y`8a1n!O
z(TP3;(L;E1-+lLS-}BabXRZA^|D5mp&iZ|8pS`!X8UZ0K03QGV2-~x%0WK^OTv)jv
zkZ=U@w)VeHkpLnQK($gAd(s9x0HBrt0HD8c%F5LPY2l7UxkBxb&Q7&@YAzE36wS+q
zu4@*Qvv;KH+yYwV>Ht_a`#@G=r2;Dp@25GFd^|s9xH~o1kS+acs92TfCo(c0y;xtE
zsRNcoExwZKSg-2ScoWIp>A}mAbFk)W4wEm)c49YwEw#cM3gel2&c6<P)|U3ks4CEf
zi$HbQm@%<>KrhsqOsicwpqL$;>7-GEsYw`1gNwRnf=vcrmFsgrE3Rs)12J<B>*U$s
z0Kqs9=BO#y@pSwQf6M@9XgA+&t)CHobst-gR!QbNx?!|t_$S$W4Cn_h?>U|A_%i=&
z>mX-FNHaBpY$p3z$Lo(*i(00HxZ0~&_+%i_nhP{K=q9R_dlQdlcq6?PC)Zjyd=U}V
z?QqMd9%i!y#oXW6R`*)C-`$wGQH)LAG`H;GFf_1isb&GlS97eQkFqH^gHdFLV3Mz+
zb5+~WQ>o^KEYpn2^|K<<1b*4AzHVTk+w9G$F&|lptY5c!vy?4lBsIIx8{MYrI+A=I
z182+312H|^gX8&I0pr_Ec!aO_A1uzx2SiZDiNAd)=f5<+Y@GXxlfLJ`IM}fZKPcZB
zZH}=W)AM{0d7olVpwu7l+-$-ipqhcgn^9k!$?tJNbHQw!)3$KrKb=NJ-z95jr&TzR
z2(4$R3oU+WmfdgGl5S3MYhdk0`h?+tvthfy&a&@fWvPvB*7_qNas#2dir^;|K4j7E
zHdJFF)Jf~xd9WZjTX>KRTteg~!@QT$d@rg{Vuk!GiQ%$yH=*ekJp&@qi3W9_atqRH
zn{@b!)x>AW`s^q1`EAE7h1K?8Z_^vE002(c0RRfzZ7p3~kRC{P3s<OvN2#HS%LI(p
z@1R_K?;eD>Olp1LE?J(vF0D!v`;(ht7Cd&(-+ooiQPSP6YCH3t0fLR(Gw6txh)weP
z_9P84eRd7{$@GnaX?c)LwZ?&hTa#aH_VjI{Gq~!K7V^OStF^M|p|dwR*N~$nWJysg
zFA(}%5J=%}>|puoZLsgdglzTE7*@;_|3*l@s$_Q(N~x5I+hZJDM-*7aXH!bnb!aM<
z_&j2_n>6&5YP@R=sw@j*1Mkmts7|}PL@{my&it&DOKh<s2@kC>7?&`G>*><1n??4%
z8ct*?B=d8!(_+}FD*?W9-$#oyI_QSxy8S+;g2fAb8x_}|Di&H(9V$ZQ`lZ0!St2Wd
z^Ggy6>FMw5<#RkSy!hki6ax!_-75ntq>oA-Q3`IKW;6;f=zd#N^07GfsNTxlZ2H3o
zGruGBS>RDs=>R9B&jB0arXfv`791taA~u+BVo2XBEh%aBF*)EZLcO$<LIs4>lrQ`7
z8j@cT6(@r)nl0z5nB=8P8Iz(upc28P*99NA$@}hlS}=x>KcedKqir3ZN)nnLKTX;x
z9s;X(-WGDLLBtBMErvVCN;%!XcYR<Q!#MCZfVd5SiiAcV3m;1`uYy|{l3euTsvl#K
zQz?aQ$F<k<Z@pyHuLgJJv+9N$Tnm0EpkAt|{_E&8xqQ~hOyv{)V(aNLQ*+%@GUIAD
z6MbjCdN?;wSXVqBqH3VDSt4?a+(<}d+WP4i-3se|qKC7OpxhzxB;NI^S1Ji~YbI1#
zJo~d6lxRCZ<|d35zq#`elq#nR4z~AKgn@mXEyZ_(MNGi%nnWg%rbLDuOUkX*6gJI`
zW#8$6Wh%p)pq!Y|$d1KhY)8gv%X7QQ>%BABZINjr`<*(x7zpKvG_9<6kZd6c%`9Wy
znxE9w{JF>>qHX4wBCxCKyD9$1eT9Xd0C5$QA;idGiE^g+rlu<jOy;~P)#s_?5hGY+
zn?A=}Gc?Ubvq`TLoh`BY8`#-A>sYLw>hNUaMu>0yhVea>DtU53(Bdn_YU9JE1?`FG
z4KGt{rpP|ZWO`#0Z1rJ$yJq+fDN__Oswj-UEn6iqE4B^oLzP{eh<&7}mL2k*cVmfW
z^!_@BJ@aI8#WLKefCD4TAr@;}XSY;^|5T@Cs9IV00Ce_^$RQV2Oih#y<R}#m-1lc4
z2l8!(<ff0n6dz7Ie6GPR+<%tI@|x}8x__m*JwF+Qo0v9z?X0D>i#lnSM*AdVKo-Aj
zIVLXcK_=Kq-citJ_(?AL{_U&P3$X()>!z?OscWn(Ugj%FJcIjtUaqY<KwA?_I(s59
zsxE8%4ZAU-rKuj+RR<&f4qp3EO<lwSDVLgF_J;Yp)h<wQoK*5=`*AXYT4xbMR*!a!
zxxUnwY}=$YQ}Oi$2k>Weezwk%#Y3Kh?l8*Xn{+qOskHXo@}*#}>l+)Q)%Uf?j|qg)
zG4EkYFBiOaD#+66S?xtc1)btTY8Vf$jd^OZXk-a7n5s7<28iu9J{{03V3TFWJh~0V
zUwF#KL>YkK{FV(%iWV-SNc+^w#*t;@SW{nd`^RK*Ih%E=I14hZQ2Kdr_8zWw!6k=&
zDU|!Nv+E{x^|NaEzWA0Yew|i!<JYIaG4)`h-Ye<NC+f6DdL#@dUBli{t=*0~sd+5u
ztpWmVLF$a~#RPJ0-P;GK7OX&F-?QIk!i|5hzEKa(`?Yg4;rFb{PZuf(bpyCUU$>`p
zcZWFMW1xD|87XGEJ6l%%i7mv<%W|k~QqKQtj$#*z**KaFg76HVjvvesN|gc|!odk{
zJzZbKN<5#5NXr+`>|~n9XLzZrH9b|J;g?-C6<Uz$b|6ot5N%{Jvw{j5KP&Yf-;{Ml
zg08-3YBwx!(CLk_J&?DF()qrf`zCg>**i#)RZS@EZDo2!%ug<NgnZNO*<1Uy7X8O@
z5}zvQqojkbshNK0wNl;wXu;i}ni~&l&0v$Eqb{$T0XHu1+t0iN-Xr!C`jW*Zg-=8J
zs+g&9JE6Fq{X^#|4S{H3RYsWp*+{wh2^Ce&7kT{|>uHS&_SxMl!e+Dhza6!`zP6+h
zbth%)-N@nclBc(6W@_2V+^JXUZ(_utmH8YOn)hfgJNEABtp_(@<F<wowoX{}wT{DU
zzi2>~G5KvGRVfWED?=5ssx^Aor9@M5#eT7p?kFwX==?q?V_-yX;i9o6u5fp&h>lL*
zqLU2;)IP+#SXcchDJRYu_@K5iXv8;$=kyq@N*I_qB2wbNoQt>o)=i@S5YMqmk}~aR
z-w1n3bYA2BXwu?`NXKBf+9r<+sy`~Sox6)O9O`U=u(yGGAl3RXko%t@Ix0{e=zTt3
zFu%4+SojKrHh*a;C9hzKlWQ@rV9cO^zCDjRAwe%PQaG@UT?g=YiFqCWG6;@Z5Es;c
zNc_9A`%6VnU!{+y8>3M>II8uRP`}on(s#pG;E4-MJlx623m$kEsOgi~)O4Jrg!yok
zLbzbMlp1Oce*$-Mas7WKcHA#&JV<3z!`90`8pSoJ6odc(IZneKfzxnug1TGVWa{;}
ze-WUaT84x)St-SRBQ*A5Nl_-IZz<3l>)A);6!F>TImU<0&BWcTv@(XRi!5h(m@A9D
zmmY~fA;g9lOgyfo2%d0?o1XFIkjb8eje*ibD%<BOq)Jl8zmM@oThS}=MZDpMtYy*u
z0O^0{T@fW65;iK;l2@I!&lQ*ycw;iBAInd2f3t<o;KuGADFzihkzL8lsPJ5yvLUf%
zN%g^O1&tTu&~u*Zw!=3apR{)?JsFa8M}(DNGK^h0l;5+g<hGd#6!J8ND(|4JuR^*{
z!jdf*WI-U#Q6^Sngiy^@WCkgd(V~dU{?&R%KJg$Q_A5^LrLU?1Igr6U&``9iKJ{xt
zGH6E;5iN_#Yq}tL%MxgdgsW*o&!kmr8@{r0HO8+g8C^NQa00C~%m@ZGA#OzgvC5{*
z(HtoP8oM!vIa*s0y7O0k6`V=(8G4qV5Fbw80F%koiq)OHi28mSG|%<u*{~>oPVAS@
z6B>%&qzD_0cSK`=&1N%BewYll&TDI(vzV8kACD2imVbO>^q->Po}J|K8L>S}FiXcW
z9=S|ROt@~ig)0j-MN>t^!pq{MU5i+Kw#>H`ttLWzOoQhn5B<*eWU0-H8NfRSS^TFs
z|MBo?0sniD`Ey<S*M2_M{Dr!Fu=!)*0oZY0uKzjaT;#Z{Uq0uc!N0V7zDRIc3wut`
zivRb9_9F7Khv6KVPx@ca#D9YSGv&|0BxILm=|%iyd3}zzx)lFcmS5z#EO*a&I?4W)
V$=Yf}SN^n@1h9hxbpG>a{R6_gQ{w;t
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/cpstartup/cpstartup.manifest
@@ -0,0 +1,1 @@
+% chrome://cpstartup/content/cpstartup.html#auto
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/cpstartup/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>cpstartup-test@mozilla.org</em:id>
+    <em:type>2</em:type>
+    <em:name>cpstartup test</em:name>
+    <em:version>1.0.0</em:version>
+    <em:bootstrap>true</em:bootstrap>
+    <em:description>Measures the performance of starting and initializing new content processes</em:description>
+    <em:creator>Gabor Krizsanits</em:creator>
+    <em:multiprocessCompatible>true</em:multiprocessCompatible>
+
+    <!-- Desktop -->
+    <em:targetApplication>
+      <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>55.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+  <em:description>https://wiki.mozilla.org/Buildbot/Talos/Tests</em:description>
+  <em:homepageURL>https://wiki.mozilla.org/Buildbot/Talos/Tests</em:homepageURL>
+</RDF>
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -255,16 +255,18 @@ var WebNavigation =  {
   init() {
     addMessageListener("WebNavigation:GoBack", this);
     addMessageListener("WebNavigation:GoForward", this);
     addMessageListener("WebNavigation:GotoIndex", this);
     addMessageListener("WebNavigation:LoadURI", this);
     addMessageListener("WebNavigation:SetOriginAttributes", this);
     addMessageListener("WebNavigation:Reload", this);
     addMessageListener("WebNavigation:Stop", this);
+    // This message is used for measuring content process startup performance.
+    sendAsyncMessage("Content:BrowserChildReady", { time: Services.telemetry.msSystemNow() });
   },
 
   get webNavigation() {
     return docShell.QueryInterface(Ci.nsIWebNavigation);
   },
 
   _inLoadURI: false,