Bug 1244227 - properly report throttled network timing; r=Honza

MozReview-Commit-ID: BCJLSRGS0vE

--HG--
extra : transplant_source : y%95%21%B8%DC%C3%7BM%F7%5D%88c9%B9%D0r%19%D8%3C%E4
This commit is contained in:
Tom Tromey 2016-02-04 06:40:25 -07:00
parent becfb61023
commit 397c46c617
2 changed files with 138 additions and 35 deletions

View File

@ -563,8 +563,6 @@ NetworkResponseListener.prototype = {
this.httpActivity.discardResponseBody
);
this.httpActivity.channel = null;
this.httpActivity.owner = null;
this.httpActivity = null;
this.sink = null;
this.inputStream = null;
@ -680,6 +678,13 @@ NetworkMonitor.prototype = {
0x804b0006: "STATUS_RECEIVING_FROM"
},
httpDownloadActivities: [
gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_START,
gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_HEADER,
gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
gActivityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE
],
// Network response bodies are piped through a buffer of the given size (in
// bytes).
responsePipeSegmentSize: null,
@ -864,6 +869,44 @@ NetworkMonitor.prototype = {
}
},
/**
* A helper function for observeActivity. This does whatever work
* is required by a particular http activity event. Arguments are
* the same as for observeActivity.
*/
_dispatchActivity: function (httpActivity, channel, activityType,
activitySubtype, timestamp, extraSizeData,
extraStringData) {
let transCodes = this.httpTransactionCodes;
// Store the time information for this activity subtype.
if (activitySubtype in transCodes) {
let stage = transCodes[activitySubtype];
if (stage in httpActivity.timings) {
httpActivity.timings[stage].last = timestamp;
} else {
httpActivity.timings[stage] = {
first: timestamp,
last: timestamp,
};
}
}
switch (activitySubtype) {
case gActivityDistributor.ACTIVITY_SUBTYPE_REQUEST_BODY_SENT:
this._onRequestBodySent(httpActivity);
break;
case gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_HEADER:
this._onResponseHeader(httpActivity, extraStringData);
break;
case gActivityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE:
this._onTransactionClose(httpActivity);
break;
default:
break;
}
},
/**
* Begin observing HTTP traffic that originates inside the current tab.
*
@ -913,33 +956,20 @@ NetworkMonitor.prototype = {
return;
}
let transCodes = this.httpTransactionCodes;
// Store the time information for this activity subtype.
if (activitySubtype in transCodes) {
let stage = transCodes[activitySubtype];
if (stage in httpActivity.timings) {
httpActivity.timings[stage].last = timestamp;
} else {
httpActivity.timings[stage] = {
first: timestamp,
last: timestamp,
};
}
}
switch (activitySubtype) {
case gActivityDistributor.ACTIVITY_SUBTYPE_REQUEST_BODY_SENT:
this._onRequestBodySent(httpActivity);
break;
case gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_HEADER:
this._onResponseHeader(httpActivity, extraStringData);
break;
case gActivityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE:
this._onTransactionClose(httpActivity);
break;
default:
break;
// If we're throttling, we must not report events as they arrive
// from platform, but instead let the throttler emit the events
// after some time has elapsed.
if (httpActivity.downloadThrottle &&
this.httpDownloadActivities.indexOf(activitySubtype) >= 0) {
let callback = this._dispatchActivity.bind(this);
httpActivity.downloadThrottle
.addActivityCallback(callback, httpActivity, channel, activityType,
activitySubtype, timestamp, extraSizeData,
extraStringData);
} else {
this._dispatchActivity(httpActivity, channel, activityType,
activitySubtype, timestamp, extraSizeData,
extraStringData);
}
}),
@ -1297,12 +1327,10 @@ NetworkMonitor.prototype = {
harTimings.connect = -1;
}
if ((timings.STATUS_WAITING_FOR || timings.STATUS_RECEIVING_FROM) &&
(timings.STATUS_CONNECTED_TO || timings.STATUS_SENDING_TO)) {
harTimings.send = (timings.STATUS_WAITING_FOR ||
timings.STATUS_RECEIVING_FROM).first -
(timings.STATUS_CONNECTED_TO ||
timings.STATUS_SENDING_TO).last;
if (timings.STATUS_SENDING_TO) {
harTimings.send = timings.STATUS_SENDING_TO.last - timings.STATUS_SENDING_TO.first;
} else if (timings.REQUEST_HEADER && timings.REQUEST_BODY_SENT) {
harTimings.send = timings.REQUEST_BODY_SENT.last - timings.REQUEST_HEADER.first;
} else {
harTimings.send = -1;
}

View File

@ -13,6 +13,10 @@ const ArrayBufferInputStream = CC("@mozilla.org/io/arraybuffer-input-stream;1",
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream", "setInputStream");
loader.lazyServiceGetter(this, "gActivityDistributor",
"@mozilla.org/network/http-activity-distributor;1",
"nsIHttpActivityDistributor");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {setTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
@ -32,6 +36,8 @@ function NetworkThrottleListener(queue) {
this.pendingData = [];
this.pendingException = null;
this.offset = 0;
this.responseStarted = false;
this.activities = {};
}
NetworkThrottleListener.prototype = {
@ -133,6 +139,9 @@ NetworkThrottleListener.prototype = {
}
this.offset += bytesPermitted;
// Maybe our state has changed enough to emit an event.
this.maybeEmitEvents();
return {length: bytesPermitted, done};
},
@ -143,6 +152,71 @@ NetworkThrottleListener.prototype = {
pendingCount: function () {
return this.pendingData.length;
},
/**
* This is called when an http activity event is delivered. This
* object delays the event until the appropriate moment.
*/
addActivityCallback: function (callback, httpActivity, channel, activityType,
activitySubtype, timestamp, extraSizeData,
extraStringData) {
let datum = {callback, httpActivity, channel, activityType,
activitySubtype, extraSizeData,
extraStringData};
this.activities[activitySubtype] = datum;
if (activitySubtype ===
gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE) {
this.totalSize = extraSizeData;
}
this.maybeEmitEvents();
},
/**
* This is called for a download throttler when the latency timeout
* has ended.
*/
responseStart: function () {
this.responseStarted = true;
this.maybeEmitEvents();
},
/**
* Check our internal state and emit any http activity events as
* needed. Note that we wait until both our internal state has
* changed and we've received the real http activity event from
* platform. This approach ensures we can both pass on the correct
* data from the original event, and update the reported time to be
* consistent with the delay we're introducing.
*/
maybeEmitEvents: function () {
if (this.responseStarted) {
this.maybeEmit(gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_START);
this.maybeEmit(gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_HEADER);
}
if (this.totalSize !== undefined && this.offset >= this.totalSize) {
this.maybeEmit(gActivityDistributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE);
this.maybeEmit(gActivityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE);
}
},
/**
* Emit an event for |code|, if the appropriate entry in
* |activities| is defined.
*/
maybeEmit: function (code) {
if (this.activities[code] !== undefined) {
let {callback, httpActivity, channel, activityType,
activitySubtype, extraSizeData,
extraStringData} = this.activities[code];
let now = Date.now() * 1000;
callback(httpActivity, channel, activityType, activitySubtype,
now, extraSizeData, extraStringData);
this.activities[code] = undefined;
}
},
};
/**
@ -183,6 +257,7 @@ NetworkThrottleQueue.prototype = {
* listener has elapsed.
*/
allowDataFrom: function (throttleListener) {
throttleListener.responseStart();
this.pendingRequests.delete(throttleListener);
const count = throttleListener.pendingCount();
for (let i = 0; i < count; ++i) {