Mercurial > hg > config
view chrome/pageloader.js @ 208:3f290bcae11a
wtf
author | Jeff Hammel <jhammel@mozilla.com> |
---|---|
date | Mon, 13 Feb 2012 16:24:06 -0800 |
parents | beca399c3a16 |
children |
line wrap: on
line source
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is tp. * * The Initial Developer of the Original Code is * Mozilla Corporation. * Portions created by the Initial Developer are Copyright (C) 2007 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Rob Helmer <rhelmer@mozilla.com> * Vladimir Vukicevic <vladimir@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ try { if (Cc === undefined) { var Cc = Components.classes; var Ci = Components.interfaces; } } catch (ex) {} var NUM_CYCLES = 5; var pageFilterRegexp = null; var reportFormat = "js"; var useBrowser = true; var winWidth = 1024; var winHeight = 768; var doRenderTest = false; var pages; var pageIndex; var start_time; var cycle; var report; var renderReport; var noisy = false; var timeout = -1; var delay = 250; var timeoutEvent = -1; var running = false; var forceCC = true; var reportRSS = false; var useMozAfterPaint = false; var gPaintWindow = window; var gPaintListener = false; //when TEST_DOES_OWN_TIMING, we need to store the time from the page as MozAfterPaint can be slower than pageload var gTime = -1; var gStartTime = -1; var content; var TEST_DOES_OWN_TIMING = 1; var browserWindow = null; // the io service var gIOS = null; function plInit() { if (running) { return; } running = true; cycle = 0; try { var args = window.arguments[0].wrappedJSObject; var manifestURI = args.manifest; var startIndex = 0; var endIndex = -1; if (args.startIndex) startIndex = parseInt(args.startIndex); if (args.endIndex) endIndex = parseInt(args.endIndex); if (args.numCycles) NUM_CYCLES = parseInt(args.numCycles); if (args.format) reportFormat = args.format; if (args.width) winWidth = parseInt(args.width); if (args.height) winHeight = parseInt(args.height); if (args.filter) pageFilterRegexp = new RegExp(args.filter); if (args.noisy) noisy = true; if (args.timeout) timeout = parseInt(args.timeout); if (args.delay) delay = parseInt(args.delay); if (args.mozafterpaint) useMozAfterPaint = true; if (args.rss) reportRSS = true; forceCC = !args.noForceCC; doRenderTest = args.doRender; if (forceCC && !window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils) .garbageCollect) { forceCC = false; } gIOS = Cc["@mozilla.org/network/io-service;1"] .getService(Ci.nsIIOService); if (args.offline) gIOS.offline = true; var fileURI = gIOS.newURI(manifestURI, null, null); pages = plLoadURLsFromURI(fileURI); if (!pages) { dumpLine('tp: could not load URLs, quitting'); plStop(true); } if (pages.length == 0) { dumpLine('tp: no pages to test, quitting'); plStop(true); } if (startIndex < 0) startIndex = 0; if (endIndex == -1 || endIndex >= pages.length) endIndex = pages.length-1; if (startIndex > endIndex) { dumpLine("tp: error: startIndex >= endIndex"); plStop(true); } pages = pages.slice(startIndex,endIndex+1); var pageUrls = pages.map(function(p) { return p.url.spec.toString(); }); report = new Report(pageUrls); if (doRenderTest) renderReport = new Report(pageUrls); pageIndex = 0; if (args.useBrowserChrome) { var wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"] .getService(Ci.nsIWindowWatcher); var blank = Cc["@mozilla.org/supports-string;1"] .createInstance(Ci.nsISupportsString); blank.data = "about:blank"; browserWindow = wwatch.openWindow (null, "chrome://browser/content/", "_blank", "chrome,all,dialog=no,width=" + winWidth + ",height=" + winHeight, blank); gPaintWindow = browserWindow; // get our window out of the way window.resizeTo(10,10); var browserLoadFunc = function (ev) { browserWindow.removeEventListener('load', browserLoadFunc, true); // do this half a second after load, because we need to be // able to resize the window and not have it get clobbered // by the persisted values setTimeout(function () { browserWindow.resizeTo(winWidth, winHeight); browserWindow.moveTo(0, 0); browserWindow.focus(); content = browserWindow.getBrowser(); // Load the frame script for e10s / IPC message support if (content.getAttribute("remote") == "true") { let contentScript = "data:,function _contentLoadHandler(e) { " + " if (e.originalTarget.defaultView == content) { " + " content.wrappedJSObject.tpRecordTime = function(t, s) { sendAsyncMessage('PageLoader:RecordTime', { time: t, startTime: s }); }; "; if (useMozAfterPaint) { contentScript += "" + "function _contentPaintHandler() { " + " var utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils); " + " if (utils.isMozAfterPaintPending) { " + " addEventListener('MozAfterPaint', function(e) { " + " removeEventListener('MozAfterPaint', arguments.callee, true); " + " sendAsyncMessage('PageLoader:MozAfterPaint', {}); " + " }, true); " + " } else { " + " sendAsyncMessage('PageLoader:MozAfterPaint', {}); " + " } " + "}; " + "content.wrappedJSObject.setTimeout(_contentPaintHandler, 0); "; } else { contentScript += " sendAsyncMessage('PageLoader:Load', {}); "; } contentScript += "" + " }" + "} " + "addEventListener('load', _contentLoadHandler, true); "; content.messageManager.loadFrameScript(contentScript, false); } if (reportRSS) { initializeMemoryCollector(plLoadPage, 100); } else { setTimeout(plLoadPage, 100); } }, 500); }; browserWindow.addEventListener('load', browserLoadFunc, true); } else { gPaintWindow = window; window.resizeTo(winWidth, winHeight); content = document.getElementById('contentPageloader'); if (reportRSS) { initializeMemoryCollector(plLoadPage, delay); } else { setTimeout(plLoadPage, delay); } } } catch(e) { dumpLine(e); plStop(true); } } function plPageFlags() { return pages[pageIndex].flags; } // load the current page, start timing var removeLastAddedListener = null; var removeLastAddedMsgListener = null; function plLoadPage() { var pageName = pages[pageIndex].url.spec; if (removeLastAddedListener) removeLastAddedListener(); if (removeLastAddedMsgListener) removeLastAddedMsgListener(); if (plPageFlags() & TEST_DOES_OWN_TIMING) { // if the page does its own timing, use a capturing handler // to make sure that we can set up the function for content to call content.addEventListener('load', plLoadHandlerCapturing, true); removeLastAddedListener = function() { content.removeEventListener('load', plLoadHandlerCapturing, true); if (useMozAfterPaint) { content.removeEventListener("MozAfterPaint", plPaintedCapturing, true); gPaintHandler = false; } }; } else { // if the page doesn't do its own timing, use a bubbling handler // to make sure that we're called after the page's own onload() handling // XXX we use a capturing event here too -- load events don't bubble up // to the <browser> element. See bug 390263. content.addEventListener('load', plLoadHandler, true); removeLastAddedListener = function() { content.removeEventListener('load', plLoadHandler, true); if (useMozAfterPaint) { gPaintWindow.removeEventListener("MozAfterPaint", plPainted, true); gPaintHandler = false; } }; } // If the test browser is remote (e10s / IPC) we need to use messages to watch for page load if (content.getAttribute("remote") == "true") { content.messageManager.addMessageListener('PageLoader:Load', plLoadHandlerMessage); content.messageManager.addMessageListener('PageLoader:RecordTime', plRecordTimeMessage); if (useMozAfterPaint) content.messageManager.addMessageListener('PageLoader:MozAfterPaint', plPaintHandler); removeLastAddedMsgListener = function() { content.messageManager.removeMessageListener('PageLoader:Load', plLoadHandlerMessage); content.messageManager.removeMessageListener('PageLoader:RecordTime', plRecordTimeMessage); if (useMozAfterPaint) content.messageManager.removeMessageListener('PageLoader:MozAfterPaint', plPaintHandler); }; } if (timeout > 0) { timeoutEvent = setTimeout('loadFail()', timeout); } if (reportRSS) { collectMemory(startAndLoadURI, pageName); } else { startAndLoadURI(pageName); } } function startAndLoadURI(pageName) { start_time = Date.now(); content.loadURI(pageName); } function loadFail() { var pageName = pages[pageIndex].url.spec; dumpLine("__FAILTimeout exceeded on " + pageName + "__FAIL") plStop(true); } function plNextPage() { if (pageIndex < pages.length-1) { pageIndex++; if (forceCC) { var tccstart = new Date(); window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils) .garbageCollect(); var tccend = new Date(); report.recordCCTime(tccend - tccstart); } setTimeout(plLoadPage, delay); } else { plStop(false); } } function plRecordTime(time) { var pageName = pages[pageIndex].url.spec; var i = pageIndex if (i < pages.length-1) { i++; } else { i = 0; } var nextName = pages[i].url.spec; report.recordTime(pageIndex, time); if (noisy) { dumpLine("Cycle " + (cycle+1) + ": loaded " + pageName + " (next: " + nextName + ")"); } } function plLoadHandlerCapturing(evt) { // make sure we pick up the right load event if (evt.type != 'load' || evt.originalTarget.defaultView.frameElement) return; //set the tpRecordTime function (called from test pages we load to store a global time. content.contentWindow.wrappedJSObject.tpRecordTime = function (time, startTime) { gTime = time; gStartTime = startTime; setTimeout(plWaitForPaintingCapturing, 0); } content.removeEventListener('load', plLoadHandlerCapturing, true); setTimeout(plWaitForPaintingCapturing, 0); } function plWaitForPaintingCapturing() { if (gPaintListener) return; var utils = gPaintWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); if (utils.isMozAfterPaintPending && useMozAfterPaint) { if (gPaintListener == false) gPaintWindow.addEventListener("MozAfterPaint", plPaintedCapturing, true); gPaintListener = true; return; } _loadHandlerCapturing(); } function plPaintedCapturing() { gPaintWindow.removeEventListener("MozAfterPaint", plPaintedCapturing, true); gPaintListener = false; _loadHandlerCapturing(); } function _loadHandlerCapturing() { if (timeout > 0) { clearTimeout(timeoutEvent); } if (!(plPageFlags() & TEST_DOES_OWN_TIMING)) { dumpLine("tp: Capturing onload handler used with page that doesn't do its own timing?"); plStop(true); } if (useMozAfterPaint) { if (gStartTime != null && gStartTime >= 0) { gTime = (new Date()) - gStartTime; gStartTime = -1; } } // set up the function for content to call if (gTime >= 0) { plRecordTime(gTime); gTime = -1; setTimeout(plNextPage, delay); }; } // the onload handler function plLoadHandler(evt) { // make sure we pick up the right load event if (evt.type != 'load' || evt.originalTarget.defaultView.frameElement) return; content.removeEventListener('load', plLoadHandler, true); setTimeout(waitForPainted, 0); } // This is called after we have received a load event, now we wait for painted function waitForPainted() { var utils = gPaintWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); if (!utils.isMozAfterPaintPending || !useMozAfterPaint) { _loadHandler(); return; } if (gPaintListener == false) gPaintWindow.addEventListener("MozAfterPaint", plPainted, true); gPaintListener = true; } function plPainted() { gPaintWindow.removeEventListener("MozAfterPaint", plPainted, true); gPaintListener = false; _loadHandler(); } function _loadHandler() { if (timeout > 0) { clearTimeout(timeoutEvent); } var docElem; if (browserWindow) docElem = browserWindow.frames["content"].document.documentElement; else docElem = content.contentDocument.documentElement; var width; if ("getBoundingClientRect" in docElem) { width = docElem.getBoundingClientRect().width; } else if ("offsetWidth" in docElem) { width = docElem.offsetWidth; } var end_time = Date.now(); var time = (end_time - start_time); // does this page want to do its own timing? // if so, we shouldn't be here if (plPageFlags() & TEST_DOES_OWN_TIMING) { dumpLine("tp: Bubbling onload handler used with page that does its own timing?"); plStop(true); } plRecordTime(time); if (doRenderTest) runRenderTest(); plNextPage(); } // the onload handler used for remote (e10s) browser function plLoadHandlerMessage(message) { _loadHandlerMessage(); } // the mozafterpaint handler for remote (e10s) browser function plPaintHandler(message) { _loadHandlerMessage(); } // the core handler for remote (e10s) browser function _loadHandlerMessage() { if (timeout > 0) { clearTimeout(timeoutEvent); } var time = -1; // does this page want to do its own timing? if ((plPageFlags() & TEST_DOES_OWN_TIMING)) { if (typeof(gStartTime) != "number") gStartTime = Date.parse(gStartTime); if (gTime >= 0) { if (useMozAfterPaint && gStartTime >= 0) { gTime = Date.now() - gStartTime; gStartTime = -1; } else if (useMozAfterPaint) { gTime = -1; } time = gTime; gTime = -1; } } else { var end_time = Date.now(); time = (end_time - start_time); } if (time >= 0) { plRecordTime(time); if (doRenderTest) runRenderTest(); plNextPage(); } } // the record time handler used for remote (e10s) browser function plRecordTimeMessage(message) { gTime = message.json.time; if (useMozAfterPaint) { gStartTime = message.json.startTime; } _loadHandlerMessage(); } function runRenderTest() { const redrawsPerSample = 500; if (!Ci.nsIDOMWindowUtils) return; var win; if (browserWindow) win = content.contentWindow; else win = window; var wu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); var start = Date.now(); for (var j = 0; j < redrawsPerSample; j++) wu.redraw(); var end = Date.now(); renderReport.recordTime(pageIndex, end - start); } function plStop(force) { if (reportRSS) { collectMemory(plStopAll, force); } else { plStopAll(force); } } function plStopAll(force) { try { if (force == false) { pageIndex = 0; if (cycle < NUM_CYCLES-1) { cycle++; setTimeout(plLoadPage, delay); return; } var formats = reportFormat.split(","); if (!renderReport) { for each (var fmt in formats) dumpLine(report.getReport(fmt)); } else { dumpLine ("*************** Render report *******************"); for each (var fmt in formats) dumpLine(renderReport.getReport(fmt)); } } } catch (e) { dumpLine(e); } if (reportRSS) { stopMemCollector(); } if (content) { content.removeEventListener('load', plLoadHandlerCapturing, true); content.removeEventListener('load', plLoadHandler, true); if (useMozAfterPaint) content.removeEventListener("MozAfterPaint", plPaintedCapturing, true); content.removeEventListener("MozAfterPaint", plPainted, true); if (content.getAttribute("remote") == "true") { content.messageManager.removeMessageListener('PageLoader:Load', plLoadHandlerMessage); content.messageManager.removeMessageListener('PageLoader:RecordTime', plRecordTimeMessage); if (useMozAfterPaint) content.messageManager.removeMessageListener('PageLoader:MozAfterPaint', plPaintHandler); content.messageManager.loadFrameScript("data:,removeEventListener('load', _contentLoadHandler, true);", false); } } if (MozillaFileLogger) MozillaFileLogger.close(); goQuitApplication(); } /* Returns array */ function plLoadURLsFromURI(manifestUri) { var fstream = Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(Ci.nsIFileInputStream); var uriFile = manifestUri.QueryInterface(Ci.nsIFileURL); fstream.init(uriFile.file, -1, 0, 0); var lstream = fstream.QueryInterface(Ci.nsILineInputStream); var d = []; var lineNo = 0; var line = {value:null}; var more; do { lineNo++; more = lstream.readLine(line); var s = line.value; // strip comments s = s.replace(/#.*/, ''); // strip leading and trailing whitespace s = s.replace(/^\s*/, '').replace(/\s*$/, ''); if (!s) continue; var flags = 0; var urlspec = s; // split on whitespace, and figure out if we have any flags var items = s.split(/\s+/); if (items[0] == "include") { if (items.length != 2) { dumpLine("tp: Error on line " + lineNo + " in " + manifestUri.spec + ": include must be followed by the manifest to include!"); return null; } var subManifest = gIOS.newURI(items[1], null, manifestUri); if (subManifest == null) { dumpLine("tp: invalid URI on line " + manifestUri.spec + ":" + lineNo + " : '" + line.value + "'"); return null; } var subItems = plLoadURLsFromURI(subManifest); if (subItems == null) return null; d = d.concat(subItems); } else { if (items.length == 2) { if (items[0].indexOf("%") != -1) flags |= TEST_DOES_OWN_TIMING; urlspec = items[1]; } else if (items.length != 1) { dumpLine("tp: Error on line " + lineNo + " in " + manifestUri.spec + ": whitespace must be %-escaped!"); return null; } var url = gIOS.newURI(urlspec, null, manifestUri); if (pageFilterRegexp && !pageFilterRegexp.test(url.spec)) continue; d.push({ url: url, flags: flags }); } } while (more); return d; } function dumpLine(str) { if (MozillaFileLogger) MozillaFileLogger.log(str + "\n"); dump(str); dump("\n"); }