Files
eruda/src/Network/Network.js
2018-01-03 20:12:56 +08:00

238 lines
5.7 KiB
JavaScript

import Tool from '../DevTools/Tool';
import XhrRequest from './XhrRequest';
import FetchRequest from './FetchRequest';
import Settings from '../Settings/Settings';
import {evalCss, isNative, defaults, now, extend, isEmpty, $} from '../lib/util';
export default class Network extends Tool
{
constructor()
{
super();
this._style = evalCss(require('./Network.scss'));
this.name = 'network';
this._requests = {};
this._tpl = require('./Network.hbs');
this._isFetchSupported = false;
if (window.fetch) this._isFetchSupported = isNative(window.fetch);
}
init($el, container)
{
super.init($el);
this._container = container;
this._bindEvent();
this._initCfg();
this.overrideXhr();
}
show()
{
super.show();
this._render();
}
clear()
{
this._requests = {};
this._render();
}
overrideXhr()
{
let winXhrProto = window.XMLHttpRequest.prototype;
let origSend = this._origSend = winXhrProto.send,
origOpen = this._origOpen = winXhrProto.open;
let self = this;
winXhrProto.open = function (method, url)
{
let xhr = this;
let req = xhr.erudaRequest = new XhrRequest(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 (data)
{
let req = this.erudaRequest;
if (req) req.handleSend(data);
origSend.apply(this, arguments);
};
}
restoreXhr()
{
let winXhrProto = window.XMLHttpRequest.prototype;
if (this._origOpen) winXhrProto.open = this._origOpen;
if (this._origSend) winXhrProto.send = this._origSend;
}
overrideFetch()
{
if (!this._isFetchSupported) return;
let origFetch = this._origFetch = window.fetch;
let self = this;
window.fetch = function (...args)
{
let req = new FetchRequest(...args);
req.on('send', (id, data) => self._addReq(id, data));
req.on('update', (id, data) => self._updateReq(id, data));
let fetchResult = origFetch(...args);
req.send(fetchResult);
return fetchResult;
};
}
restoreFetch()
{
if (!this._isFetchSupported) return;
if (this._origFetch) window.fetch = this._origFetch;
}
_addReq(id, data)
{
defaults(data, {
name: '',
url: '',
status: 'pending',
type: 'unknown',
subType: 'unknown',
size: 0,
data: '',
method: 'GET',
startTime: now(),
time: 0,
resHeaders: {},
resTxt: '',
done: false
});
this._requests[id] = data;
this._render();
}
_updateReq(id, data)
{
let target = this._requests[id];
if (!target) return;
extend(target, data);
target.time = target.time - target.startTime;
target.displayTime = formatTime(target.time);
if (target.done && (target.status < 200 || target >= 300)) target.hasErr = true;
this._render();
}
_bindEvent()
{
let $el = this._$el,
container = this._container;
let self = this;
$el.on('click', '.eruda-request', function ()
{
let id = $(this).data('id'),
data = self._requests[id];
if (!data.done) return;
showSources('http', {
url: data.url,
data: data.data,
resTxt: data.resTxt,
type: data.type,
subType: data.subType,
resHeaders: data.resHeaders
});
}).on('click', '.eruda-clear-request', () => this.clear());
function showSources(type, data)
{
let sources = container.get('sources');
if (!sources) return;
sources.set(type, data);
container.showTool('sources');
}
}
destroy()
{
super.destroy();
evalCss.remove(this._style);
this.restoreXhr();
this.restoreFetch();
}
_initCfg()
{
let cfg = this.config = Settings.createCfg('network', {
overrideFetch: true
});
if (cfg.get('overrideFetch')) this.overrideFetch();
cfg.on('change', (key, val) =>
{
switch (key)
{
case 'overrideFetch': return val ? this.overrideFetch() : this.restoreFetch();
}
});
let settings = this._container.get('settings');
settings.text('Network')
.switch(cfg, 'overrideFetch', 'Catch Fetch Requests')
.separator();
}
_render()
{
if (!this.active) return;
let renderData = {};
if (!isEmpty(this._requests)) renderData.requests = this._requests;
this._renderHtml(this._tpl(renderData));
}
_renderHtml(html)
{
if (html === this._lastHtml) return;
this._lastHtml = html;
this._$el.html(html);
}
}
function formatTime(time)
{
time = Math.round(time);
if (time < 1000) return time + 'ms';
return (time / 1000).toFixed(1) + 's';
}