Files
eruda/src/Network/Network.es6
2016-05-19 21:21:27 +08:00

286 lines
7.6 KiB
JavaScript

import Tool from '../DevTools/Tool.es6'
import Request from './Request.es6'
import util from '../lib/util'
import config from '../lib/config.es6'
export default class Network extends Tool
{
constructor()
{
super();
require('./Network.scss');
this.name = 'network';
this._performanceTimingData = [];
this._requests = {};
this._tpl = require('./Network.hbs');
}
init($el, parent)
{
super.init($el);
this._parent = parent;
this._bindEvent();
this._initConfig();
}
show()
{
super.show();
this._getPerformanceTimingData();
this._render();
}
overrideXhr()
{
var winXhrProto = window.XMLHttpRequest.prototype;
var origSend = this._origSend = winXhrProto.send,
origOpen = this._origOpen = winXhrProto.open;
var self = this;
winXhrProto.open = function (method, url)
{
var xhr = this;
var req = xhr.erudaRequest = new Request(xhr, method, url);
req.on('send', (id, data) => self._addReq(id, data));
req.on('update', (id, data) => self._updateReq(id, data));
xhr.addEventListener('readystatechange', function ()
{
switch (xhr.readyState)
{
case 2: return req.handleHeadersReceived();
case 4: return req.handleDone();
}
});
origOpen.apply(this, arguments);
};
winXhrProto.send = function (...args)
{
var req = this.erudaRequest;
if (req) req.handleSend();
origSend.apply(this, args);
};
}
restoreXhr()
{
var winXhrProto = window.XMLHttpRequest.prototype;
if (this._origOpen) winXhrProto.open = this._origOpen;
if (this._origSend) winXhrProto.send = this._origSend;
}
destroy()
{
super.destroy();
this.restoreXhr();
}
_addReq(id, data)
{
util.defaults(data, {
name: '',
url: '',
status: 'pending',
type: 'unknown',
subType: 'unknown',
size: 0,
method: 'GET',
startTime: util.now(),
time: 0,
resHeaders: {},
resTxt: '',
xhr: {},
done: false
});
this._requests[id] = data;
this._render();
}
_updateReq(id, data)
{
var target = this._requests[id];
if (!target) return;
util.extend(target, data);
target.time = target.time - target.startTime;
target.displayTime = formatTime(target.time);
this._render();
}
_bindEvent()
{
var $el = this._$el,
parent = this._parent;
var self = this;
$el.on('click', '.eruda-performance-timing', function ()
{
$el.find('.eruda-performance-timing-data').show();
});
$el.on('click', '.eruda-request', function ()
{
var id = util.$(this).data('id'),
data = self._requests[id];
if (!data.done) return;
var sources = parent.get('sources');
if (!sources) return;
sources.set('http', {
url: data.url,
resTxt: data.resTxt,
type: data.type,
subType: data.subType,
resHeaders: data.resHeaders
});
parent.showTool('sources');
});
}
_getPerformanceTimingData()
{
var performance = window.webkitPerformance || window.performance;
if (!performance) return;
var timing = performance.timing;
var data = [];
var {
navigationStart,
unloadEventStart,
unloadEventEnd,
redirectStart,
redirectEnd,
fetchStart,
domainLookupStart,
domainLookupEnd,
connectStart,
connectEnd,
secureConnectionStart,
requestStart,
responseStart,
responseEnd,
domLoading,
domInteractive,
domContentLoadedEventStart,
domContentLoadedEventEnd,
domComplete,
loadEventStart,
loadEventEnd
} = timing;
var start = navigationStart,
end = loadEventEnd,
total = end - start;
function getData(name, startTime, endTime)
{
var duration = endTime - startTime;
return {
name: name,
start: (startTime - start) / total * 100,
duration: duration,
len: duration / total * 100
};
}
data.push(getData('Total', navigationStart, loadEventEnd));
data.push(getData('Network/Server', navigationStart, responseStart));
data.push(getData('App cache', fetchStart, domainLookupStart));
data.push(getData('DNS', domainLookupStart, domainLookupEnd));
data.push(getData('TCP', connectStart, connectEnd));
data.push(getData('Time to First Byte', requestStart, responseStart));
data.push(getData('Response', responseStart, responseEnd));
data.push(getData('Unload', unloadEventStart, unloadEventEnd));
data.push(getData('DOM Processing', domLoading, domComplete));
data.push(getData('DOM Construction', domLoading, domInteractive));
data.push(getData('DOM Content Loaded Event', domContentLoadedEventStart, domContentLoadedEventEnd));
data.push(getData('Load Event', loadEventStart, loadEventEnd));
this._performanceTimingData = data;
var performanceTiming = {};
[
'navigationStart',
'unloadEventStart',
'unloadEventEnd',
'redirectStart',
'redirectEnd',
'fetchStart',
'domainLookupStart',
'domainLookupEnd',
'connectStart',
'connectEnd',
'secureConnectionStart',
'requestStart',
'responseStart',
'responseEnd',
'domLoading',
'domInteractive',
'domContentLoadedEventStart',
'domContentLoadedEventEnd',
'domComplete',
'loadEventStart',
'loadEventEnd'
].forEach((val) =>
{
performanceTiming[val] = timing[val] === 0 ? 0 : timing[val] - start;
});
this._performanceTiming = performanceTiming;
}
_initConfig()
{
var cfg = this.config = config.create('eruda-network');
cfg.set(util.defaults(cfg.get(), {overrideXhr: true}));
if (cfg.get('overrideXhr')) this.overrideXhr();
cfg.on('change', (key, val) =>
{
switch (key)
{
case 'overrideXhr': return val ? this.overrideXhr() : this.restoreXhr();
}
});
var settings = this._parent.get('settings');
settings.text('Network')
.add(cfg, 'overrideXhr', 'Catch Ajax Requests')
.separator();
}
_render()
{
if (!this.active) return;
this._$el.html(this._tpl({
data: this._performanceTimingData,
timing: this._performanceTiming,
requests: this._requests
}));
}
}
function formatTime(time)
{
if (time < 1000) return time + 'ms';
return (time / 1000).toFixed(1) + 's';
}