comparison chrome/pageloader.js @ 208:3f290bcae11a

wtf
author Jeff Hammel <jhammel@mozilla.com>
date Mon, 13 Feb 2012 16:24:06 -0800
parents beca399c3a16
children
comparison
equal deleted inserted replaced
207:7bad4b7281f2 208:3f290bcae11a
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is tp.
16 *
17 * The Initial Developer of the Original Code is
18 * Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2007
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * Rob Helmer <rhelmer@mozilla.com>
24 * Vladimir Vukicevic <vladimir@mozilla.com>
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 try {
41 if (Cc === undefined) {
42 var Cc = Components.classes;
43 var Ci = Components.interfaces;
44 }
45 } catch (ex) {}
46
47 var NUM_CYCLES = 5;
48
49 var pageFilterRegexp = null;
50 var reportFormat = "js";
51 var useBrowser = true;
52 var winWidth = 1024;
53 var winHeight = 768;
54
55 var doRenderTest = false;
56
57 var pages;
58 var pageIndex;
59 var start_time;
60 var cycle;
61 var report;
62 var renderReport;
63 var noisy = false;
64 var timeout = -1;
65 var delay = 250;
66 var timeoutEvent = -1;
67 var running = false;
68 var forceCC = true;
69 var reportRSS = false;
70
71 var useMozAfterPaint = false;
72 var gPaintWindow = window;
73 var gPaintListener = false;
74
75 //when TEST_DOES_OWN_TIMING, we need to store the time from the page as MozAfterPaint can be slower than pageload
76 var gTime = -1;
77 var gStartTime = -1;
78
79 var content;
80
81 var TEST_DOES_OWN_TIMING = 1;
82
83 var browserWindow = null;
84
85 // the io service
86 var gIOS = null;
87
88 function plInit() {
89 if (running) {
90 return;
91 }
92 running = true;
93
94 cycle = 0;
95
96 try {
97 var args = window.arguments[0].wrappedJSObject;
98
99 var manifestURI = args.manifest;
100 var startIndex = 0;
101 var endIndex = -1;
102 if (args.startIndex) startIndex = parseInt(args.startIndex);
103 if (args.endIndex) endIndex = parseInt(args.endIndex);
104 if (args.numCycles) NUM_CYCLES = parseInt(args.numCycles);
105 if (args.format) reportFormat = args.format;
106 if (args.width) winWidth = parseInt(args.width);
107 if (args.height) winHeight = parseInt(args.height);
108 if (args.filter) pageFilterRegexp = new RegExp(args.filter);
109 if (args.noisy) noisy = true;
110 if (args.timeout) timeout = parseInt(args.timeout);
111 if (args.delay) delay = parseInt(args.delay);
112 if (args.mozafterpaint) useMozAfterPaint = true;
113 if (args.rss) reportRSS = true;
114
115 forceCC = !args.noForceCC;
116 doRenderTest = args.doRender;
117
118 if (forceCC &&
119 !window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
120 .getInterface(Components.interfaces.nsIDOMWindowUtils)
121 .garbageCollect) {
122 forceCC = false;
123 }
124
125 gIOS = Cc["@mozilla.org/network/io-service;1"]
126 .getService(Ci.nsIIOService);
127 if (args.offline)
128 gIOS.offline = true;
129 var fileURI = gIOS.newURI(manifestURI, null, null);
130 pages = plLoadURLsFromURI(fileURI);
131
132 if (!pages) {
133 dumpLine('tp: could not load URLs, quitting');
134 plStop(true);
135 }
136
137 if (pages.length == 0) {
138 dumpLine('tp: no pages to test, quitting');
139 plStop(true);
140 }
141
142 if (startIndex < 0)
143 startIndex = 0;
144 if (endIndex == -1 || endIndex >= pages.length)
145 endIndex = pages.length-1;
146 if (startIndex > endIndex) {
147 dumpLine("tp: error: startIndex >= endIndex");
148 plStop(true);
149 }
150
151 pages = pages.slice(startIndex,endIndex+1);
152 var pageUrls = pages.map(function(p) { return p.url.spec.toString(); });
153 report = new Report(pageUrls);
154
155 if (doRenderTest)
156 renderReport = new Report(pageUrls);
157
158 pageIndex = 0;
159
160 if (args.useBrowserChrome) {
161 var wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"]
162 .getService(Ci.nsIWindowWatcher);
163 var blank = Cc["@mozilla.org/supports-string;1"]
164 .createInstance(Ci.nsISupportsString);
165 blank.data = "about:blank";
166 browserWindow = wwatch.openWindow
167 (null, "chrome://browser/content/", "_blank",
168 "chrome,all,dialog=no,width=" + winWidth + ",height=" + winHeight, blank);
169
170 gPaintWindow = browserWindow;
171 // get our window out of the way
172 window.resizeTo(10,10);
173
174 var browserLoadFunc = function (ev) {
175 browserWindow.removeEventListener('load', browserLoadFunc, true);
176
177 // do this half a second after load, because we need to be
178 // able to resize the window and not have it get clobbered
179 // by the persisted values
180 setTimeout(function () {
181 browserWindow.resizeTo(winWidth, winHeight);
182 browserWindow.moveTo(0, 0);
183 browserWindow.focus();
184
185 content = browserWindow.getBrowser();
186
187 // Load the frame script for e10s / IPC message support
188 if (content.getAttribute("remote") == "true") {
189 let contentScript = "data:,function _contentLoadHandler(e) { " +
190 " if (e.originalTarget.defaultView == content) { " +
191 " content.wrappedJSObject.tpRecordTime = function(t, s) { sendAsyncMessage('PageLoader:RecordTime', { time: t, startTime: s }); }; ";
192 if (useMozAfterPaint) {
193 contentScript += "" +
194 "function _contentPaintHandler() { " +
195 " var utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils); " +
196 " if (utils.isMozAfterPaintPending) { " +
197 " addEventListener('MozAfterPaint', function(e) { " +
198 " removeEventListener('MozAfterPaint', arguments.callee, true); " +
199 " sendAsyncMessage('PageLoader:MozAfterPaint', {}); " +
200 " }, true); " +
201 " } else { " +
202 " sendAsyncMessage('PageLoader:MozAfterPaint', {}); " +
203 " } " +
204 "}; " +
205 "content.wrappedJSObject.setTimeout(_contentPaintHandler, 0); ";
206 } else {
207 contentScript += " sendAsyncMessage('PageLoader:Load', {}); ";
208 }
209 contentScript += "" +
210 " }" +
211 "} " +
212 "addEventListener('load', _contentLoadHandler, true); ";
213 content.messageManager.loadFrameScript(contentScript, false);
214 }
215 if (reportRSS) {
216 initializeMemoryCollector(plLoadPage, 100);
217 } else {
218 setTimeout(plLoadPage, 100);
219 }
220 }, 500);
221 };
222
223 browserWindow.addEventListener('load', browserLoadFunc, true);
224 } else {
225 gPaintWindow = window;
226 window.resizeTo(winWidth, winHeight);
227
228 content = document.getElementById('contentPageloader');
229
230 if (reportRSS) {
231 initializeMemoryCollector(plLoadPage, delay);
232 } else {
233 setTimeout(plLoadPage, delay);
234 }
235 }
236 } catch(e) {
237 dumpLine(e);
238 plStop(true);
239 }
240 }
241
242 function plPageFlags() {
243 return pages[pageIndex].flags;
244 }
245
246 // load the current page, start timing
247 var removeLastAddedListener = null;
248 var removeLastAddedMsgListener = null;
249 function plLoadPage() {
250 var pageName = pages[pageIndex].url.spec;
251
252 if (removeLastAddedListener)
253 removeLastAddedListener();
254
255 if (removeLastAddedMsgListener)
256 removeLastAddedMsgListener();
257
258 if (plPageFlags() & TEST_DOES_OWN_TIMING) {
259 // if the page does its own timing, use a capturing handler
260 // to make sure that we can set up the function for content to call
261
262 content.addEventListener('load', plLoadHandlerCapturing, true);
263 removeLastAddedListener = function() {
264 content.removeEventListener('load', plLoadHandlerCapturing, true);
265 if (useMozAfterPaint) {
266 content.removeEventListener("MozAfterPaint", plPaintedCapturing, true);
267 gPaintHandler = false;
268 }
269 };
270 } else {
271 // if the page doesn't do its own timing, use a bubbling handler
272 // to make sure that we're called after the page's own onload() handling
273
274 // XXX we use a capturing event here too -- load events don't bubble up
275 // to the <browser> element. See bug 390263.
276 content.addEventListener('load', plLoadHandler, true);
277 removeLastAddedListener = function() {
278 content.removeEventListener('load', plLoadHandler, true);
279 if (useMozAfterPaint) {
280 gPaintWindow.removeEventListener("MozAfterPaint", plPainted, true);
281 gPaintHandler = false;
282 }
283 };
284 }
285
286 // If the test browser is remote (e10s / IPC) we need to use messages to watch for page load
287 if (content.getAttribute("remote") == "true") {
288 content.messageManager.addMessageListener('PageLoader:Load', plLoadHandlerMessage);
289 content.messageManager.addMessageListener('PageLoader:RecordTime', plRecordTimeMessage);
290 if (useMozAfterPaint)
291 content.messageManager.addMessageListener('PageLoader:MozAfterPaint', plPaintHandler);
292 removeLastAddedMsgListener = function() {
293 content.messageManager.removeMessageListener('PageLoader:Load', plLoadHandlerMessage);
294 content.messageManager.removeMessageListener('PageLoader:RecordTime', plRecordTimeMessage);
295 if (useMozAfterPaint)
296 content.messageManager.removeMessageListener('PageLoader:MozAfterPaint', plPaintHandler);
297 };
298 }
299
300 if (timeout > 0) {
301 timeoutEvent = setTimeout('loadFail()', timeout);
302 }
303 if (reportRSS) {
304 collectMemory(startAndLoadURI, pageName);
305 } else {
306 startAndLoadURI(pageName);
307 }
308 }
309
310 function startAndLoadURI(pageName) {
311 start_time = Date.now();
312 content.loadURI(pageName);
313 }
314
315 function loadFail() {
316 var pageName = pages[pageIndex].url.spec;
317 dumpLine("__FAILTimeout exceeded on " + pageName + "__FAIL")
318 plStop(true);
319 }
320
321 function plNextPage() {
322 if (pageIndex < pages.length-1) {
323 pageIndex++;
324
325 if (forceCC) {
326 var tccstart = new Date();
327 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
328 .getInterface(Components.interfaces.nsIDOMWindowUtils)
329 .garbageCollect();
330 var tccend = new Date();
331 report.recordCCTime(tccend - tccstart);
332 }
333
334 setTimeout(plLoadPage, delay);
335 } else {
336 plStop(false);
337 }
338 }
339
340 function plRecordTime(time) {
341 var pageName = pages[pageIndex].url.spec;
342 var i = pageIndex
343 if (i < pages.length-1) {
344 i++;
345 } else {
346 i = 0;
347 }
348 var nextName = pages[i].url.spec;
349 report.recordTime(pageIndex, time);
350 if (noisy) {
351 dumpLine("Cycle " + (cycle+1) + ": loaded " + pageName + " (next: " + nextName + ")");
352 }
353 }
354
355 function plLoadHandlerCapturing(evt) {
356 // make sure we pick up the right load event
357 if (evt.type != 'load' ||
358 evt.originalTarget.defaultView.frameElement)
359 return;
360
361 //set the tpRecordTime function (called from test pages we load to store a global time.
362 content.contentWindow.wrappedJSObject.tpRecordTime = function (time, startTime) {
363 gTime = time;
364 gStartTime = startTime;
365 setTimeout(plWaitForPaintingCapturing, 0);
366 }
367
368 content.removeEventListener('load', plLoadHandlerCapturing, true);
369
370 setTimeout(plWaitForPaintingCapturing, 0);
371 }
372
373 function plWaitForPaintingCapturing() {
374 if (gPaintListener)
375 return;
376
377 var utils = gPaintWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
378 .getInterface(Components.interfaces.nsIDOMWindowUtils);
379
380 if (utils.isMozAfterPaintPending && useMozAfterPaint) {
381 if (gPaintListener == false)
382 gPaintWindow.addEventListener("MozAfterPaint", plPaintedCapturing, true);
383 gPaintListener = true;
384 return;
385 }
386
387 _loadHandlerCapturing();
388 }
389
390 function plPaintedCapturing() {
391 gPaintWindow.removeEventListener("MozAfterPaint", plPaintedCapturing, true);
392 gPaintListener = false;
393 _loadHandlerCapturing();
394 }
395
396 function _loadHandlerCapturing() {
397 if (timeout > 0) {
398 clearTimeout(timeoutEvent);
399 }
400
401 if (!(plPageFlags() & TEST_DOES_OWN_TIMING)) {
402 dumpLine("tp: Capturing onload handler used with page that doesn't do its own timing?");
403 plStop(true);
404 }
405
406 if (useMozAfterPaint) {
407 if (gStartTime != null && gStartTime >= 0) {
408 gTime = (new Date()) - gStartTime;
409 gStartTime = -1;
410 }
411 }
412
413 // set up the function for content to call
414 if (gTime >= 0) {
415 plRecordTime(gTime);
416 gTime = -1;
417 setTimeout(plNextPage, delay);
418 };
419 }
420
421 // the onload handler
422 function plLoadHandler(evt) {
423 // make sure we pick up the right load event
424 if (evt.type != 'load' ||
425 evt.originalTarget.defaultView.frameElement)
426 return;
427
428 content.removeEventListener('load', plLoadHandler, true);
429 setTimeout(waitForPainted, 0);
430 }
431
432 // This is called after we have received a load event, now we wait for painted
433 function waitForPainted() {
434
435 var utils = gPaintWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
436 .getInterface(Components.interfaces.nsIDOMWindowUtils);
437
438 if (!utils.isMozAfterPaintPending || !useMozAfterPaint) {
439 _loadHandler();
440 return;
441 }
442
443 if (gPaintListener == false)
444 gPaintWindow.addEventListener("MozAfterPaint", plPainted, true);
445 gPaintListener = true;
446 }
447
448 function plPainted() {
449 gPaintWindow.removeEventListener("MozAfterPaint", plPainted, true);
450 gPaintListener = false;
451 _loadHandler();
452 }
453
454 function _loadHandler() {
455 if (timeout > 0) {
456 clearTimeout(timeoutEvent);
457 }
458 var docElem;
459 if (browserWindow)
460 docElem = browserWindow.frames["content"].document.documentElement;
461 else
462 docElem = content.contentDocument.documentElement;
463 var width;
464 if ("getBoundingClientRect" in docElem) {
465 width = docElem.getBoundingClientRect().width;
466 } else if ("offsetWidth" in docElem) {
467 width = docElem.offsetWidth;
468 }
469
470 var end_time = Date.now();
471 var time = (end_time - start_time);
472
473 // does this page want to do its own timing?
474 // if so, we shouldn't be here
475 if (plPageFlags() & TEST_DOES_OWN_TIMING) {
476 dumpLine("tp: Bubbling onload handler used with page that does its own timing?");
477 plStop(true);
478 }
479
480 plRecordTime(time);
481
482 if (doRenderTest)
483 runRenderTest();
484
485 plNextPage();
486 }
487
488 // the onload handler used for remote (e10s) browser
489 function plLoadHandlerMessage(message) {
490 _loadHandlerMessage();
491 }
492
493 // the mozafterpaint handler for remote (e10s) browser
494 function plPaintHandler(message) {
495 _loadHandlerMessage();
496 }
497
498 // the core handler for remote (e10s) browser
499 function _loadHandlerMessage() {
500 if (timeout > 0) {
501 clearTimeout(timeoutEvent);
502 }
503
504 var time = -1;
505
506 // does this page want to do its own timing?
507 if ((plPageFlags() & TEST_DOES_OWN_TIMING)) {
508 if (typeof(gStartTime) != "number")
509 gStartTime = Date.parse(gStartTime);
510
511 if (gTime >= 0) {
512 if (useMozAfterPaint && gStartTime >= 0) {
513 gTime = Date.now() - gStartTime;
514 gStartTime = -1;
515 } else if (useMozAfterPaint) {
516 gTime = -1;
517 }
518 time = gTime;
519 gTime = -1;
520 }
521
522 } else {
523 var end_time = Date.now();
524 time = (end_time - start_time);
525 }
526
527 if (time >= 0) {
528 plRecordTime(time);
529 if (doRenderTest)
530 runRenderTest();
531
532 plNextPage();
533 }
534 }
535
536 // the record time handler used for remote (e10s) browser
537 function plRecordTimeMessage(message) {
538 gTime = message.json.time;
539 if (useMozAfterPaint) {
540 gStartTime = message.json.startTime;
541 }
542 _loadHandlerMessage();
543 }
544
545 function runRenderTest() {
546 const redrawsPerSample = 500;
547
548 if (!Ci.nsIDOMWindowUtils)
549 return;
550
551 var win;
552
553 if (browserWindow)
554 win = content.contentWindow;
555 else
556 win = window;
557 var wu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
558
559 var start = Date.now();
560 for (var j = 0; j < redrawsPerSample; j++)
561 wu.redraw();
562 var end = Date.now();
563
564 renderReport.recordTime(pageIndex, end - start);
565 }
566
567 function plStop(force) {
568 if (reportRSS) {
569 collectMemory(plStopAll, force);
570 } else {
571 plStopAll(force);
572 }
573 }
574
575 function plStopAll(force) {
576 try {
577 if (force == false) {
578 pageIndex = 0;
579 if (cycle < NUM_CYCLES-1) {
580 cycle++;
581 setTimeout(plLoadPage, delay);
582 return;
583 }
584
585 var formats = reportFormat.split(",");
586
587 if (!renderReport) {
588 for each (var fmt in formats)
589 dumpLine(report.getReport(fmt));
590 }
591 else {
592 dumpLine ("*************** Render report *******************");
593 for each (var fmt in formats)
594 dumpLine(renderReport.getReport(fmt));
595 }
596 }
597 } catch (e) {
598 dumpLine(e);
599 }
600
601 if (reportRSS) {
602 stopMemCollector();
603 }
604
605 if (content) {
606 content.removeEventListener('load', plLoadHandlerCapturing, true);
607 content.removeEventListener('load', plLoadHandler, true);
608 if (useMozAfterPaint)
609 content.removeEventListener("MozAfterPaint", plPaintedCapturing, true);
610 content.removeEventListener("MozAfterPaint", plPainted, true);
611
612 if (content.getAttribute("remote") == "true") {
613 content.messageManager.removeMessageListener('PageLoader:Load', plLoadHandlerMessage);
614 content.messageManager.removeMessageListener('PageLoader:RecordTime', plRecordTimeMessage);
615 if (useMozAfterPaint)
616 content.messageManager.removeMessageListener('PageLoader:MozAfterPaint', plPaintHandler);
617
618 content.messageManager.loadFrameScript("data:,removeEventListener('load', _contentLoadHandler, true);", false);
619 }
620 }
621
622 if (MozillaFileLogger)
623 MozillaFileLogger.close();
624
625 goQuitApplication();
626 }
627
628 /* Returns array */
629 function plLoadURLsFromURI(manifestUri) {
630 var fstream = Cc["@mozilla.org/network/file-input-stream;1"]
631 .createInstance(Ci.nsIFileInputStream);
632 var uriFile = manifestUri.QueryInterface(Ci.nsIFileURL);
633
634 fstream.init(uriFile.file, -1, 0, 0);
635 var lstream = fstream.QueryInterface(Ci.nsILineInputStream);
636
637 var d = [];
638
639 var lineNo = 0;
640 var line = {value:null};
641 var more;
642 do {
643 lineNo++;
644 more = lstream.readLine(line);
645 var s = line.value;
646
647 // strip comments
648 s = s.replace(/#.*/, '');
649
650 // strip leading and trailing whitespace
651 s = s.replace(/^\s*/, '').replace(/\s*$/, '');
652
653 if (!s)
654 continue;
655
656 var flags = 0;
657 var urlspec = s;
658
659 // split on whitespace, and figure out if we have any flags
660 var items = s.split(/\s+/);
661 if (items[0] == "include") {
662 if (items.length != 2) {
663 dumpLine("tp: Error on line " + lineNo + " in " + manifestUri.spec + ": include must be followed by the manifest to include!");
664 return null;
665 }
666
667 var subManifest = gIOS.newURI(items[1], null, manifestUri);
668 if (subManifest == null) {
669 dumpLine("tp: invalid URI on line " + manifestUri.spec + ":" + lineNo + " : '" + line.value + "'");
670 return null;
671 }
672
673 var subItems = plLoadURLsFromURI(subManifest);
674 if (subItems == null)
675 return null;
676 d = d.concat(subItems);
677 } else {
678 if (items.length == 2) {
679 if (items[0].indexOf("%") != -1)
680 flags |= TEST_DOES_OWN_TIMING;
681
682 urlspec = items[1];
683 } else if (items.length != 1) {
684 dumpLine("tp: Error on line " + lineNo + " in " + manifestUri.spec + ": whitespace must be %-escaped!");
685 return null;
686 }
687
688 var url = gIOS.newURI(urlspec, null, manifestUri);
689
690 if (pageFilterRegexp && !pageFilterRegexp.test(url.spec))
691 continue;
692
693 d.push({ url: url,
694 flags: flags });
695 }
696 } while (more);
697
698 return d;
699 }
700
701 function dumpLine(str) {
702 if (MozillaFileLogger)
703 MozillaFileLogger.log(str + "\n");
704 dump(str);
705 dump("\n");
706 }