зеркало из https://github.com/mozilla/gecko-dev.git
Bug 568634 - Update networking log entries with subsequent http transactions, r=sdwilsh, a=blocking2.0, beta5
This commit is contained in:
Родитель
e688979fe2
Коммит
ec21282fad
|
@ -63,6 +63,12 @@ XPCOMUtils.defineLazyServiceGetter(this, "sss",
|
|||
"@mozilla.org/content/style-sheet-service;1",
|
||||
"nsIStyleSheetService");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "NetUtil", function () {
|
||||
var obj = {};
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", obj);
|
||||
return obj.NetUtil;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PropertyPanel", function () {
|
||||
var obj = {};
|
||||
try {
|
||||
|
@ -107,6 +113,143 @@ const ERRORS = { LOG_MESSAGE_MISSING_ARGS:
|
|||
LOG_OUTPUT_FAILED: "Log Failure: Could not append messageNode to outputNode",
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements the nsIStreamListener and nsIRequestObserver interface. Used
|
||||
* within the HS_httpObserverFactory function to get the response body of
|
||||
* requests.
|
||||
*
|
||||
* The code is mostly based on code listings from:
|
||||
*
|
||||
* http://www.softwareishard.com/blog/firebug/
|
||||
* nsitraceablechannel-intercept-http-traffic/
|
||||
*
|
||||
* @param object aHttpActivity
|
||||
* HttpActivity object associated with this request (see
|
||||
* HS_httpObserverFactory). As the response is done, the response header,
|
||||
* body and status is stored on aHttpActivity.
|
||||
*/
|
||||
function ResponseListener(aHttpActivity) {
|
||||
this.receivedData = "";
|
||||
this.httpActivity = aHttpActivity;
|
||||
}
|
||||
|
||||
ResponseListener.prototype =
|
||||
{
|
||||
/**
|
||||
* The original listener for this request.
|
||||
*/
|
||||
originalListener: null,
|
||||
|
||||
/**
|
||||
* The HttpActivity object associated with this response.
|
||||
*/
|
||||
httpActivity: null,
|
||||
|
||||
/**
|
||||
* Stores the received data as a string.
|
||||
*/
|
||||
receivedData: null,
|
||||
|
||||
/**
|
||||
* Sets the httpActivity object's response header if it isn't set already.
|
||||
*
|
||||
* @param nsIRequest aRequest
|
||||
*/
|
||||
setResponseHeader: function RL_setResponseHeader(aRequest)
|
||||
{
|
||||
let httpActivity = this.httpActivity;
|
||||
// Check if the header isn't set yet.
|
||||
if (!httpActivity.response.header) {
|
||||
httpActivity.response.header = {};
|
||||
if (aRequest instanceof Ci.nsIHttpChannel) {
|
||||
aRequest.visitResponseHeaders({
|
||||
visitHeader: function(aName, aValue) {
|
||||
httpActivity.response.header[aName] = aValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* See documention at
|
||||
* https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIStreamListener
|
||||
*
|
||||
* Grabs a copy of the original data and passes it on to the original listener.
|
||||
*
|
||||
* @param nsIRequest aRequest
|
||||
* @param nsISupports aContext
|
||||
* @param nsIInputStream aInputStream
|
||||
* @param unsigned long aOffset
|
||||
* @param unsigned long aCount
|
||||
*/
|
||||
onDataAvailable: function RL_onDataAvailable(aRequest, aContext, aInputStream,
|
||||
aOffset, aCount)
|
||||
{
|
||||
this.setResponseHeader(aRequest);
|
||||
|
||||
let StorageStream = Components.Constructor("@mozilla.org/storagestream;1",
|
||||
"nsIStorageStream",
|
||||
"init");
|
||||
let BinaryOutputStream = Components.Constructor("@mozilla.org/binaryoutputstream;1",
|
||||
"nsIBinaryOutputStream",
|
||||
"setOutputStream");
|
||||
|
||||
storageStream = new StorageStream(8192, aCount, null);
|
||||
binaryOutputStream = new BinaryOutputStream(storageStream.getOutputStream(0));
|
||||
|
||||
let data = NetUtil.readInputStreamToString(aInputStream, aCount);
|
||||
this.receivedData += data;
|
||||
binaryOutputStream.writeBytes(data, aCount);
|
||||
|
||||
this.originalListener.onDataAvailable(aRequest, aContext,
|
||||
storageStream.newInputStream(0), aOffset, aCount);
|
||||
},
|
||||
|
||||
/**
|
||||
* See documentation at
|
||||
* https://developer.mozilla.org/En/NsIRequestObserver
|
||||
*
|
||||
* @param nsIRequest aRequest
|
||||
* @param nsISupports aContext
|
||||
*/
|
||||
onStartRequest: function RL_onStartRequest(aRequest, aContext)
|
||||
{
|
||||
this.originalListener.onStartRequest(aRequest, aContext);
|
||||
},
|
||||
|
||||
/**
|
||||
* See documentation at
|
||||
* https://developer.mozilla.org/En/NsIRequestObserver
|
||||
*
|
||||
* If aRequest is an nsIHttpChannel then the response header is stored on the
|
||||
* httpActivity object. Also, the response body is set on the httpActivity
|
||||
* object and the HUDService.lastFinishedRequestCallback is called if there
|
||||
* is one.
|
||||
*
|
||||
* @param nsIRequest aRequest
|
||||
* @param nsISupports aContext
|
||||
* @param nsresult aStatusCode
|
||||
*/
|
||||
onStopRequest: function RL_onStopRequest(aRequest, aContext, aStatusCode)
|
||||
{
|
||||
this.originalListener.onStopRequest(aRequest, aContext, aStatusCode);
|
||||
|
||||
this.setResponseHeader(aRequest);
|
||||
this.httpActivity.response.body = this.receivedData;
|
||||
|
||||
if (HUDService.lastFinishedRequestCallback) {
|
||||
HUDService.lastFinishedRequestCallback(this.httpActivity);
|
||||
}
|
||||
this.httpActivity = null;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIStreamListener,
|
||||
Ci.nsISupports
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper object for networking stuff.
|
||||
*
|
||||
|
@ -169,6 +312,109 @@ const ERRORS = { LOG_MESSAGE_MISSING_ARGS:
|
|||
*/
|
||||
var NetworkHelper =
|
||||
{
|
||||
/**
|
||||
* Converts aText with a given aCharset to unicode.
|
||||
*
|
||||
* @param string aText
|
||||
* Text to convert.
|
||||
* @param string aCharset
|
||||
* Charset to convert the text to.
|
||||
* @returns string
|
||||
* Converted text.
|
||||
*/
|
||||
convertToUnicode: function NH_convertToUnicode(aText, aCharset)
|
||||
{
|
||||
let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
conv.charset = aCharset || "UTF-8";
|
||||
return conv.ConvertToUnicode(aText);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads all available bytes from aStream and converts them to aCharset.
|
||||
*
|
||||
* @param nsIInputStream aStream
|
||||
* @param string aCharset
|
||||
* @returns string
|
||||
* UTF-16 encoded string based on the content of aStream and aCharset.
|
||||
*/
|
||||
readAndConvertFromStream: function NH_readAndConvertFromStream(aStream, aCharset)
|
||||
{
|
||||
let text = null;
|
||||
try {
|
||||
text = NetUtil.readInputStreamToString(aStream, aStream.available())
|
||||
return this.convertToUnicode(text, aCharset);
|
||||
}
|
||||
catch (err) {
|
||||
return text;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads the posted text from aRequest.
|
||||
*
|
||||
* @param nsIHttpChannel aRequest
|
||||
* @param nsIDOMNode aBrowser
|
||||
* @returns string or null
|
||||
* Returns the posted string if it was possible to read from aRequest
|
||||
* otherwise null.
|
||||
*/
|
||||
readPostTextFromRequest: function NH_readPostTextFromRequest(aRequest, aBrowser)
|
||||
{
|
||||
if (aRequest instanceof Ci.nsIUploadChannel) {
|
||||
let iStream = aRequest.uploadStream;
|
||||
|
||||
let isSeekableStream = false;
|
||||
if (iStream instanceof Ci.nsISeekableStream) {
|
||||
isSeekableStream = true;
|
||||
}
|
||||
|
||||
let prevOffset;
|
||||
if (isSeekableStream) {
|
||||
prevOffset = iStream.tell();
|
||||
iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
|
||||
}
|
||||
|
||||
// Read data from the stream.
|
||||
let charset = aBrowser.contentWindow.document.characterSet;
|
||||
let text = this.readAndConvertFromStream(iStream, charset);
|
||||
|
||||
// Seek locks the file, so seek to the beginning only if necko hasn't
|
||||
// read it yet, since necko doesn't seek to 0 before reading (at lest
|
||||
// not till 459384 is fixed).
|
||||
if (isSeekableStream && prevOffset == 0) {
|
||||
iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads the posted text from the page's cache.
|
||||
*
|
||||
* @param nsIDOMNode aBrowser
|
||||
* @returns string or null
|
||||
* Returns the posted string if it was possible to read from aBrowser
|
||||
* otherwise null.
|
||||
*/
|
||||
readPostTextFromPage: function NH_readPostTextFromPage(aBrowser)
|
||||
{
|
||||
let webNav = aBrowser.webNavigation;
|
||||
if (webNav instanceof Ci.nsIWebPageDescriptor) {
|
||||
let descriptor = webNav.currentDescriptor;
|
||||
|
||||
if (descriptor instanceof Ci.nsISHEntry && descriptor.postData &&
|
||||
descriptor instanceof Ci.nsISeekableStream) {
|
||||
descriptor.seek(NS_SEEK_SET, 0);
|
||||
|
||||
let charset = browser.contentWindow.document.characterSet;
|
||||
return this.readAndConvertFromStream(descriptor, charset);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the nsIDOMWindow that is associated with aRequest.
|
||||
*
|
||||
|
@ -1109,6 +1355,17 @@ HUD_SERVICE.prototype =
|
|||
return win;
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests that haven't finished yet.
|
||||
*/
|
||||
openRequests: {},
|
||||
|
||||
/**
|
||||
* Assign a function to this property to listen for finished httpRequests.
|
||||
* Used by unit tests.
|
||||
*/
|
||||
lastFinishedRequestCallback: null,
|
||||
|
||||
/**
|
||||
* Begin observing HTTP traffic that we care about,
|
||||
* namely traffic that originates inside any context that a Heads Up Display
|
||||
|
@ -1123,13 +1380,15 @@ HUD_SERVICE.prototype =
|
|||
function (aChannel, aActivityType, aActivitySubtype,
|
||||
aTimestamp, aExtraSizeData, aExtraStringData)
|
||||
{
|
||||
var loadGroup;
|
||||
if (aActivityType ==
|
||||
activityDistributor.ACTIVITY_TYPE_HTTP_TRANSACTION) {
|
||||
activityDistributor.ACTIVITY_TYPE_HTTP_TRANSACTION ||
|
||||
aActivityType ==
|
||||
activityDistributor.ACTIVITY_TYPE_SOCKET_TRANSPORT) {
|
||||
|
||||
aChannel = aChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
var transCodes = this.httpTransactionCodes;
|
||||
let transCodes = this.httpTransactionCodes;
|
||||
let hudId;
|
||||
|
||||
if (aActivitySubtype ==
|
||||
activityDistributor.ACTIVITY_SUBTYPE_REQUEST_HEADER ) {
|
||||
|
@ -1140,29 +1399,158 @@ HUD_SERVICE.prototype =
|
|||
}
|
||||
|
||||
// Try to get the hudId that is associated to the window.
|
||||
let hudId = self.getHudIdByWindow(win);
|
||||
hudId = self.getHudIdByWindow(win);
|
||||
if (!hudId) {
|
||||
return;
|
||||
}
|
||||
|
||||
var httpActivity = {
|
||||
// The httpActivity object will hold all information concerning
|
||||
// this request and later response.
|
||||
let httpActivity = {
|
||||
id: self.sequenceId(),
|
||||
hudId: hudId,
|
||||
url: aChannel.URI.spec,
|
||||
method: aChannel.requestMethod,
|
||||
channel: aChannel,
|
||||
type: aActivityType,
|
||||
subType: aActivitySubtype,
|
||||
timestamp: aTimestamp,
|
||||
extraSizeData: aExtraSizeData,
|
||||
extraStringData: aExtraStringData,
|
||||
stage: transCodes[aActivitySubtype],
|
||||
hudId: hudId
|
||||
|
||||
request: {
|
||||
header: { }
|
||||
},
|
||||
response: {
|
||||
header: null
|
||||
},
|
||||
timing: {
|
||||
"REQUEST_HEADER": aTimestamp
|
||||
}
|
||||
};
|
||||
|
||||
// create a unique ID to track this transaction and be able to
|
||||
// update the logged node with subsequent http transactions
|
||||
httpActivity.httpId = self.sequenceId();
|
||||
// Add a new output entry.
|
||||
let loggedNode =
|
||||
self.logActivity("network", aChannel.URI, httpActivity);
|
||||
self.httpTransactions[aChannel] =
|
||||
new Number(httpActivity.httpId);
|
||||
|
||||
// In some cases loggedNode can be undefined (e.g. if an image was
|
||||
// requested). Don't continue in such a case.
|
||||
if (!loggedNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add listener for the response body.
|
||||
let newListener = new ResponseListener(httpActivity);
|
||||
aChannel.QueryInterface(Ci.nsITraceableChannel);
|
||||
newListener.originalListener = aChannel.setNewListener(newListener);
|
||||
httpActivity.response.listener = newListener;
|
||||
|
||||
// Copy the request header data.
|
||||
aChannel.visitRequestHeaders({
|
||||
visitHeader: function(aName, aValue) {
|
||||
httpActivity.request.header[aName] = aValue;
|
||||
}
|
||||
});
|
||||
|
||||
// Store the loggedNode and the httpActivity object for later reuse.
|
||||
httpActivity.messageObject = loggedNode;
|
||||
self.openRequests[httpActivity.id] = httpActivity;
|
||||
}
|
||||
else {
|
||||
// Iterate over all currently ongoing requests. If aChannel can't
|
||||
// be found within them, then exit this function.
|
||||
let httpActivity = null;
|
||||
for each (var item in self.openRequests) {
|
||||
if (item.channel !== aChannel) {
|
||||
continue;
|
||||
}
|
||||
httpActivity = item;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!httpActivity) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msgObject;
|
||||
let data, textNode;
|
||||
// Store the time information for this activity subtype.
|
||||
httpActivity.timing[transCodes[aActivitySubtype]] = aTimestamp;
|
||||
|
||||
switch (aActivitySubtype) {
|
||||
case activityDistributor.ACTIVITY_SUBTYPE_REQUEST_BODY_SENT:
|
||||
let gBrowser = HUDService.currentContext().gBrowser;
|
||||
|
||||
let sentBody = NetworkHelper.readPostTextFromRequest(
|
||||
aChannel, gBrowser);
|
||||
if (!sentBody) {
|
||||
// If the request URL is the same as the current page url, then
|
||||
// we can try to get the posted text from the page directly.
|
||||
// This is necessary as otherwise the
|
||||
// NetworkHelper.readPostTextFromPage
|
||||
// function is called for image requests as well but these
|
||||
// are not web pages and as such don't store the posted text
|
||||
// in the cache of the webpage.
|
||||
if (httpActivity.url == gBrowser.contentWindow.location.href) {
|
||||
sentBody = NetworkHelper.readPostTextFromPage(gBrowser);
|
||||
}
|
||||
if (!sentBody) {
|
||||
sentBody = "";
|
||||
}
|
||||
}
|
||||
httpActivity.request.body = sentBody;
|
||||
break;
|
||||
|
||||
case activityDistributor.ACTIVITY_SUBTYPE_RESPONSE_HEADER:
|
||||
msgObject = httpActivity.messageObject;
|
||||
|
||||
// aExtraStringData contains the response header. The first line
|
||||
// contains the response status (e.g. HTTP/1.1 200 OK).
|
||||
//
|
||||
// Note: The response header is not saved here. Calling the
|
||||
// aChannel.visitResponseHeaders at this point sometimes
|
||||
// causes an NS_ERROR_NOT_AVAILABLE exception. Therefore,
|
||||
// the response header and response body is stored on the
|
||||
// httpActivity object within the RL_onStopRequest function.
|
||||
httpActivity.response.status =
|
||||
aExtraStringData.split(/\r\n|\n|\r/)[0];
|
||||
|
||||
// Remove the textNode from the messageNode and add a new one
|
||||
// that contains the respond http status.
|
||||
textNode = msgObject.messageNode.firstChild;
|
||||
textNode.parentNode.removeChild(textNode);
|
||||
|
||||
data = [ httpActivity.url,
|
||||
httpActivity.response.status ];
|
||||
|
||||
msgObject.messageNode.appendChild(
|
||||
msgObject.textFactory(
|
||||
msgObject.prefix +
|
||||
self.getFormatStr("networkUrlWithStatus", data)));
|
||||
|
||||
break;
|
||||
|
||||
case activityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE:
|
||||
msgObject = httpActivity.messageObject;
|
||||
|
||||
|
||||
let timing = httpActivity.timing;
|
||||
let requestDuration =
|
||||
Math.round((timing.RESPONSE_COMPLETE -
|
||||
timing.REQUEST_HEADER) / 1000);
|
||||
|
||||
// Remove the textNode from the messageNode and add a new one
|
||||
// that contains the request duration.
|
||||
textNode = msgObject.messageNode.firstChild;
|
||||
textNode.parentNode.removeChild(textNode);
|
||||
|
||||
data = [ httpActivity.url,
|
||||
httpActivity.response.status,
|
||||
requestDuration ];
|
||||
|
||||
msgObject.messageNode.appendChild(
|
||||
msgObject.textFactory(
|
||||
msgObject.prefix +
|
||||
self.getFormatStr("networkUrlWithStatusAndDuration", data)));
|
||||
|
||||
delete self.openRequests[item.id];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1174,16 +1562,19 @@ HUD_SERVICE.prototype =
|
|||
0x5004: "RESPONSE_HEADER",
|
||||
0x5005: "RESPONSE_COMPLETE",
|
||||
0x5006: "TRANSACTION_CLOSE",
|
||||
|
||||
0x804b0003: "STATUS_RESOLVING",
|
||||
0x804b0007: "STATUS_CONNECTING_TO",
|
||||
0x804b0004: "STATUS_CONNECTED_TO",
|
||||
0x804b0005: "STATUS_SENDING_TO",
|
||||
0x804b000a: "STATUS_WAITING_FOR",
|
||||
0x804b0006: "STATUS_RECEIVING_FROM"
|
||||
}
|
||||
};
|
||||
|
||||
activityDistributor.addObserver(httpObserver);
|
||||
},
|
||||
|
||||
// keep tracked of trasactions where the request header was logged
|
||||
// update logged transactions thereafter.
|
||||
httpTransactions: {},
|
||||
|
||||
/**
|
||||
* Logs network activity
|
||||
*
|
||||
|
@ -1211,13 +1602,20 @@ HUD_SERVICE.prototype =
|
|||
};
|
||||
var msgType = this.getStr("typeNetwork");
|
||||
var msg = msgType + " " +
|
||||
aActivityObject.channel.requestMethod +
|
||||
aActivityObject.method +
|
||||
" " +
|
||||
aURI.spec;
|
||||
aActivityObject.url;
|
||||
message.message = msg;
|
||||
|
||||
var messageObject =
|
||||
this.messageFactory(message, aType, outputNode, aActivityObject);
|
||||
this.messageFactory(message, aType, outputNode, aActivityObject);
|
||||
|
||||
var timestampedMessage = messageObject.timestampedMessage;
|
||||
var urlIdx = timestampedMessage.indexOf(aActivityObject.url);
|
||||
messageObject.prefix = timestampedMessage.substring(0, urlIdx);
|
||||
|
||||
this.logMessage(messageObject.messageObject, outputNode, messageObject.messageNode);
|
||||
return messageObject;
|
||||
}
|
||||
catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
|
@ -1307,7 +1705,7 @@ HUD_SERVICE.prototype =
|
|||
var displayNode, outputNode, hudId;
|
||||
|
||||
if (aType == "network") {
|
||||
var result = this.logNetActivity(aType, aURI, aActivityObject);
|
||||
return this.logNetActivity(aType, aURI, aActivityObject);
|
||||
}
|
||||
else if (aType == "console-listener") {
|
||||
this.logConsoleActivity(aURI, aActivityObject);
|
||||
|
@ -3145,9 +3543,9 @@ LogMessage.prototype = {
|
|||
this.messageNode = this.xulElementFactory("label");
|
||||
|
||||
var ts = ConsoleUtils.timestamp();
|
||||
var timestampedMessage = ConsoleUtils.timestampString(ts) + ": " +
|
||||
this.timestampedMessage = ConsoleUtils.timestampString(ts) + ": " +
|
||||
this.message.message;
|
||||
var messageTxtNode = this.textFactory(timestampedMessage);
|
||||
var messageTxtNode = this.textFactory(this.timestampedMessage);
|
||||
|
||||
this.messageNode.appendChild(messageTxtNode);
|
||||
|
||||
|
|
|
@ -45,11 +45,13 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
_BROWSER_TEST_FILES = \
|
||||
browser_HUDServiceTestsAll.js \
|
||||
browser_webconsole_netlogging.js \
|
||||
$(NULL)
|
||||
|
||||
_BROWSER_TEST_PAGES = \
|
||||
test-console.html \
|
||||
test-network.html \
|
||||
test-network-request.html \
|
||||
test-mutation.html \
|
||||
testscript.js \
|
||||
test-filter.html \
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* Contributor(s):
|
||||
* Julian Viereck <jviereck@mozilla.com>
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/HUDService.jsm");
|
||||
|
||||
const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-network-request.html";
|
||||
|
||||
const TEST_DATA_JSON_CONTENT =
|
||||
'{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }';
|
||||
|
||||
var hud;
|
||||
var hudId;
|
||||
|
||||
function testOpenWebConsole()
|
||||
{
|
||||
HUDService.activateHUDForContext(gBrowser.selectedTab);
|
||||
is(HUDService.displaysIndex().length, 1, "WebConsole was opened");
|
||||
|
||||
hudId = HUDService.displaysIndex()[0];
|
||||
hud = HUDService.hudWeakReferences[hudId].get();
|
||||
|
||||
testNetworkLogging();
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
hud = null;
|
||||
hudId = null;
|
||||
|
||||
let tab = gBrowser.selectedTab;
|
||||
HUDService.deactivateHUDForContext(tab);
|
||||
executeSoon(function() {
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function testNetworkLogging()
|
||||
{
|
||||
var lastFinishedRequest = null;
|
||||
HUDService.lastFinishedRequestCallback =
|
||||
function requestDoneCallback(aHttpRequest)
|
||||
{
|
||||
lastFinishedRequest = aHttpRequest;
|
||||
}
|
||||
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let loggingGen;
|
||||
// This generator function is used to step through the individual, async tests.
|
||||
function loggingGeneratorFunc() {
|
||||
browser.addEventListener("load", function onLoad () {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
loggingGen.next();
|
||||
}, true);
|
||||
content.location = TEST_NETWORK_REQUEST_URI;
|
||||
yield;
|
||||
|
||||
// Check if page load was logged correctly.
|
||||
let httpActivity = lastFinishedRequest;
|
||||
isnot(httpActivity, null, "Page load was logged");
|
||||
is(httpActivity.url, TEST_NETWORK_REQUEST_URI,
|
||||
"Logged network entry is page load");
|
||||
is(httpActivity.method, "GET", "Method is correct");
|
||||
is(httpActivity.request.body, undefined, "No request body sent");
|
||||
|
||||
// TODO: Figure out why the following test is failing on linux (bug 588533).
|
||||
//
|
||||
// If not linux, then run the test. On Linux it always fails.
|
||||
if (navigator.platform.indexOf("Linux") != 0) {
|
||||
ok(httpActivity.response.body.indexOf("<!DOCTYPE HTML>") == 0,
|
||||
"Response body's beginning is okay");
|
||||
}
|
||||
|
||||
// Start xhr-get test.
|
||||
browser.contentWindow.wrappedJSObject.testXhrGet(loggingGen);
|
||||
yield;
|
||||
|
||||
// Use executeSoon here as the xhr callback calls loggingGen.next() before
|
||||
// the network observer detected that the request is completly done and the
|
||||
// HUDService.lastFinishedRequest is set. executeSoon solves that problem.
|
||||
executeSoon(function() {
|
||||
// Check if xhr-get test was successful.
|
||||
httpActivity = lastFinishedRequest;
|
||||
isnot(httpActivity, null, "testXhrGet() was logged");
|
||||
is(httpActivity.method, "GET", "Method is correct");
|
||||
is(httpActivity.request.body, undefined, "No request body was sent");
|
||||
is(httpActivity.response.body, TEST_DATA_JSON_CONTENT,
|
||||
"Response is correct");
|
||||
lastFinishedRequest = null;
|
||||
loggingGen.next();
|
||||
});
|
||||
yield;
|
||||
|
||||
// Start xhr-post test.
|
||||
browser.contentWindow.wrappedJSObject.testXhrPost(loggingGen);
|
||||
yield;
|
||||
|
||||
executeSoon(function() {
|
||||
// Check if xhr-post test was successful.
|
||||
httpActivity = lastFinishedRequest;
|
||||
isnot(httpActivity, null, "testXhrPost() was logged");
|
||||
is(httpActivity.method, "POST", "Method is correct");
|
||||
is(httpActivity.request.body, "Hello world!",
|
||||
"Request body was logged");
|
||||
is(httpActivity.response.body, TEST_DATA_JSON_CONTENT,
|
||||
"Response is correct");
|
||||
lastFinishedRequest = null
|
||||
loggingGen.next();
|
||||
});
|
||||
yield;
|
||||
|
||||
// Start submit-form test. As the form is submitted, the page is loaded
|
||||
// again. Bind to the DOMContentLoaded event to catch when this is done.
|
||||
browser.addEventListener("load", function onLoad () {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
loggingGen.next();
|
||||
}, true);
|
||||
browser.contentWindow.wrappedJSObject.testSubmitForm();
|
||||
yield;
|
||||
|
||||
// Check if submitting the form was logged successful.
|
||||
httpActivity = lastFinishedRequest;
|
||||
isnot(httpActivity, null, "testSubmitForm() was logged");
|
||||
is(httpActivity.method, "POST", "Method is correct");
|
||||
isnot(httpActivity.request.body.indexOf(
|
||||
"Content-Type: application/x-www-form-urlencoded"), -1,
|
||||
"Content-Type is correct");
|
||||
isnot(httpActivity.request.body.indexOf(
|
||||
"Content-Length: 20"), -1, "Content-length is correct");
|
||||
isnot(httpActivity.request.body.indexOf(
|
||||
"name=foo+bar&age=144"), -1, "Form data is correct");
|
||||
ok(httpActivity.response.body.indexOf("<!DOCTYPE HTML>") == 0,
|
||||
"Response body's beginning is okay");
|
||||
|
||||
lastFinishedRequest = null
|
||||
|
||||
// All tests are done. Shutdown.
|
||||
browser = null;
|
||||
lastFinishedRequest = null;
|
||||
HUDService.lastFinishedRequestCallback = null;
|
||||
finishTest();
|
||||
}
|
||||
|
||||
loggingGen = loggingGeneratorFunc();
|
||||
loggingGen.next();
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
waitForFocus(testOpenWebConsole, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,WebConsole network logging tests";
|
||||
}
|
|
@ -1 +1 @@
|
|||
{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }
|
||||
{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
|
||||
<title>Console HTTP test page</title>
|
||||
<script type="text/javascript">
|
||||
function makeXhr(aMethod, aUrl, aRequestBody, aTestGenerator) {
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.open(aMethod, aUrl, true);
|
||||
xmlhttp.onreadystatechange = function (aEvt) {
|
||||
if (xmlhttp.readyState == 4) {
|
||||
aTestGenerator.next();
|
||||
}
|
||||
};
|
||||
xmlhttp.send(aRequestBody);
|
||||
}
|
||||
|
||||
function testXhrGet(aTestGenerator) {
|
||||
makeXhr('get', 'test-data.json', null, aTestGenerator);
|
||||
}
|
||||
|
||||
function testXhrPost(aTestGenerator) {
|
||||
makeXhr('post', 'test-data.json', "Hello world!", aTestGenerator);
|
||||
}
|
||||
|
||||
function testSubmitForm() {
|
||||
document.getElementsByTagName("form")[0].submit();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Heads Up Display HTTP Logging Testpage</h1>
|
||||
<h2>This page is used to test the HTTP logging.</h2>
|
||||
|
||||
<form action="http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-network-request.html" method="post">
|
||||
<input name="name" type="text" value="foo bar"><br>
|
||||
<input name="age" type="text" value="144"><br>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -63,3 +63,24 @@ jsPropertyTitle=Object Inspector
|
|||
jsPropertyInspectTitle=Inspect: %S
|
||||
copyCmd.label=Copy
|
||||
copyCmd.accesskey=C
|
||||
# LOCALIZATION NOTE (networkUrlWithStatus):
|
||||
#
|
||||
# When the HTTP request is started only the URL of the request is printed to the
|
||||
# WebConsole. As the response status of the HTTP request arrives, the URL string
|
||||
# is replaced by this string (the response status can look like `HTTP/1.1 200 OK`).
|
||||
# The bracket is not closed to mark that this request is not done by now. As the
|
||||
# request is finished (the HTTP connection is closed) this string is replaced
|
||||
# by `networkUrlWithStatusAndDuration` which has a closing the braket.
|
||||
#
|
||||
# %1$S = URL of network request
|
||||
# %2$S = response status code from the server (e.g. `HTTP/1.1 200 OK`)
|
||||
networkUrlWithStatus=%1$S [%2$S
|
||||
# LOCALIZATION NOTE (networkUrlWithStatusAndDuration):
|
||||
#
|
||||
# When the HTTP request is finished (the HTTP connection is closed) this string
|
||||
# replaces the former `networkUrlWithStatus` string in the WebConsole.
|
||||
#
|
||||
# %1$S = URL of network request
|
||||
# %2$S = response status code from the server (e.g. `HTTP/1.1 200 OK`)
|
||||
# %3$S = duration for the complete network request in milliseconds
|
||||
networkUrlWithStatusAndDuration=%1$S [%2$S %3$Sms]
|
||||
|
|
Загрузка…
Ссылка в новой задаче