blob: 94e25f49b08b94369015a4acb60305e440c57bca [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Code for beaconing client page load time back to the server.
* This javascript is part of the AddInstrumentationFilter.
*
* @author jud@google.com (Jud Porter)
*/
// Exporting functions using quoted attributes to prevent js compiler from
// renaming them.
// See http://code.google.com/closure/compiler/docs/api-tutorial3.html#dangers
window['pagespeed'] = window['pagespeed'] || {};
var pagespeed = window['pagespeed'];
/**
* @constructor
* @param {string} beaconUrlPrefix The prefix portion of the beacon url.
* @param {string} event Event to trigger on, either 'load' or 'beforeunload'.
* @param {string} extraParams Additional parameters to be added to the beacon.
* @param {string} htmlUrl Url of the page the beacon is being inserted on.
*/
pagespeed.AddInstrumentation = function(beaconUrlPrefix, event, extraParams,
htmlUrl) {
this.beaconUrlPrefix_ = beaconUrlPrefix;
this.event_ = event;
this.extraParams_ = extraParams;
this.htmlUrl_ = htmlUrl;
};
pagespeed['beaconUrl'] = '';
/**
* Create beacon URL and send request to server.
*/
pagespeed.AddInstrumentation.prototype.sendBeacon = function() {
var url = this.beaconUrlPrefix_;
var oldStartTime = window['mod_pagespeed_start'];
var currentTime = Number(new Date());
var traditionalPLT = (currentTime - oldStartTime);
// Handle a beacon url that already has query params.
url += (url.indexOf('?') == -1) ? '?' : '&';
url += 'ets=';
url += (this.event_ == 'load') ? 'load:' : 'unload:';
url += traditionalPLT;
// We use navigation timing api for getting accurate start time. This api is
// available in Internet Explorer 9+, Google Chrome 6+ and Firefox 7+.
// If not present, we set the start time to when the rendering started.
// TODO(satyanarayana): Remove the oldStartTime usages once
// devconsole code has been updated to use the new "rload" param value.
if (this.event_ == 'beforeunload' && window['mod_pagespeed_loaded']) {
return;
}
url += '&r' + this.event_ + '=';
if (window['performance']) {
var timingApi = window['performance']['timing'];
var navStartTime = timingApi['navigationStart'];
var requestStartTime = timingApi['requestStart'];
url += (timingApi[this.event_ + 'EventStart'] - navStartTime);
url += '&nav=' + (timingApi['fetchStart'] - navStartTime);
url += '&dns=' + (
timingApi['domainLookupEnd'] - timingApi['domainLookupStart']);
url += '&connect=' + (
timingApi['connectEnd'] - timingApi['connectStart']);
url += '&req_start=' + (requestStartTime - navStartTime);
url += '&ttfb=' + (
timingApi['responseStart'] - requestStartTime);
url += '&dwld=' + (
timingApi['responseEnd'] - timingApi['responseStart']);
url += '&dom_c=' + (timingApi['domContentLoadedEventStart'] - navStartTime);
if (window['performance']['navigation']) {
url += '&nt=' + window['performance']['navigation']['type'];
}
var firstPaintTime = -1;
if (timingApi['msFirstPaint']) {
// IE.
firstPaintTime = timingApi['msFirstPaint'];
} else if (window['chrome'] && window['chrome']['loadTimes']) {
// Chrome. Note that window.chrome.loadTimes returns a time in seconds.
firstPaintTime = Math.floor(
window['chrome']['loadTimes']()['firstPaintTime'] * 1000);
}
firstPaintTime = firstPaintTime - requestStartTime;
if (firstPaintTime >= 0) {
url += '&fp=' + firstPaintTime;
}
} else {
url += traditionalPLT;
}
if (pagespeed['getResourceTimingData'] && window.parent == window) {
url += pagespeed.getResourceTimingData();
}
url += (window.parent != window) ? '&ifr=1' : '&ifr=0';
if (this.event_ == 'load') {
window['mod_pagespeed_loaded'] = true;
var numPrefetchedResources =
window['mod_pagespeed_num_resources_prefetched'];
if (numPrefetchedResources) {
url += '&nrp=' + numPrefetchedResources;
}
var prefetchStartTime = window['mod_pagespeed_prefetch_start'];
if (prefetchStartTime) {
url += '&htmlAt=' + (oldStartTime - prefetchStartTime);
}
}
if (pagespeed['panelLoader']) {
var bcsi = pagespeed['panelLoader']['getCsiTimingsString']();
if (bcsi != '') {
url += '&b_csi=' + bcsi;
}
}
if (pagespeed['criticalCss']) {
var cc = pagespeed['criticalCss'];
url += '&ccis=' + cc['total_critical_inlined_size'] +
'&cces=' + cc['total_original_external_size'] +
'&ccos=' + cc['total_overhead_size'] +
'&ccrl=' + cc['num_replaced_links'] +
'&ccul=' + cc['num_unreplaced_links'];
}
// Collect devicePixelRatios to find common values.
// Note: This may append =undefined for old browsers.
url += '&dpr=' + window.devicePixelRatio;
if (this.extraParams_ != '') {
url += this.extraParams_;
}
if (document.referrer) {
url += '&ref=' + encodeURIComponent(document.referrer);
}
url += '&url=' + encodeURIComponent(this.htmlUrl_);
pagespeed['beaconUrl'] = url;
new Image().src = url;
};
/**
* Initialize instrumentation beacon.
* @param {string} beaconUrl Url of beacon.
* @param {string} event Event to trigger on, either 'load' or 'beforeunload'.
* @param {string} extraParams Additional parameters to be added to the beacon.
* @param {string} htmlUrl Url of the page the beacon is being inserted on.
*/
pagespeed.addInstrumentationInit = function(beaconUrl, event, extraParams,
htmlUrl) {
var temp = new pagespeed.AddInstrumentation(beaconUrl, event, extraParams,
htmlUrl);
if (window.addEventListener) {
window.addEventListener(event, function() { temp.sendBeacon() }, false);
} else {
window.attachEvent('on' + event, function() { temp.sendBeacon() });
}
};
pagespeed['addInstrumentationInit'] = pagespeed.addInstrumentationInit;