Bug 673148 - (async-webconsole) Part 4 - Make network logging async; r=rcampbell

This commit is contained in:
Mihai Sucan 2012-05-29 15:48:05 +03:00
parent f61281cfb9
commit e7e699e7f7
18 changed files with 2379 additions and 1785 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ include $(DEPTH)/config/autoconf.mk
EXTRA_JS_MODULES = \ EXTRA_JS_MODULES = \
PropertyPanel.jsm \ PropertyPanel.jsm \
NetworkHelper.jsm \ NetworkHelper.jsm \
NetworkPanel.jsm \
AutocompletePopup.jsm \ AutocompletePopup.jsm \
WebConsoleUtils.jsm \ WebConsoleUtils.jsm \
$(NULL) $(NULL)

View File

@ -49,6 +49,7 @@
* Austin Andrews * Austin Andrews
* Christoph Dorn * Christoph Dorn
* Steven Roussey (AppCenter Inc, Network54) * Steven Roussey (AppCenter Inc, Network54)
* Mihai Sucan (Mozilla Corp.)
*/ */
const Cc = Components.classes; const Cc = Components.classes;
@ -68,7 +69,7 @@ var EXPORTED_SYMBOLS = ["NetworkHelper"];
/** /**
* Helper object for networking stuff. * Helper object for networking stuff.
* *
* All of the following functions have been taken from the Firebug source. They * Most of the following functions have been taken from the Firebug source. They
* have been modified to match the Firefox coding rules. * have been modified to match the Firefox coding rules.
*/ */
@ -128,12 +129,13 @@ var NetworkHelper =
* Reads the posted text from aRequest. * Reads the posted text from aRequest.
* *
* @param nsIHttpChannel aRequest * @param nsIHttpChannel aRequest
* @param nsIDOMNode aBrowser * @param string aCharset
* The content document charset, used when reading the POSTed data.
* @returns string or null * @returns string or null
* Returns the posted string if it was possible to read from aRequest * Returns the posted string if it was possible to read from aRequest
* otherwise null. * otherwise null.
*/ */
readPostTextFromRequest: function NH_readPostTextFromRequest(aRequest, aBrowser) readPostTextFromRequest: function NH_readPostTextFromRequest(aRequest, aCharset)
{ {
if (aRequest instanceof Ci.nsIUploadChannel) { if (aRequest instanceof Ci.nsIUploadChannel) {
let iStream = aRequest.uploadStream; let iStream = aRequest.uploadStream;
@ -150,8 +152,7 @@ var NetworkHelper =
} }
// Read data from the stream. // Read data from the stream.
let charset = aBrowser.contentWindow.document.characterSet; let text = this.readAndConvertFromStream(iStream, aCharset);
let text = this.readAndConvertFromStream(iStream, charset);
// Seek locks the file, so seek to the beginning only if necko hasn't // 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 // read it yet, since necko doesn't seek to 0 before reading (at lest
@ -167,14 +168,15 @@ var NetworkHelper =
/** /**
* Reads the posted text from the page's cache. * Reads the posted text from the page's cache.
* *
* @param nsIDOMNode aBrowser * @param nsIDocShell aDocShell
* @param string aCharset
* @returns string or null * @returns string or null
* Returns the posted string if it was possible to read from aBrowser * Returns the posted string if it was possible to read from
* otherwise null. * aDocShell otherwise null.
*/ */
readPostTextFromPage: function NH_readPostTextFromPage(aBrowser) readPostTextFromPage: function NH_readPostTextFromPage(aDocShell, aCharset)
{ {
let webNav = aBrowser.webNavigation; let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation);
if (webNav instanceof Ci.nsIWebPageDescriptor) { if (webNav instanceof Ci.nsIWebPageDescriptor) {
let descriptor = webNav.currentDescriptor; let descriptor = webNav.currentDescriptor;
@ -182,8 +184,7 @@ var NetworkHelper =
descriptor instanceof Ci.nsISeekableStream) { descriptor instanceof Ci.nsISeekableStream) {
descriptor.seek(NS_SEEK_SET, 0); descriptor.seek(NS_SEEK_SET, 0);
let charset = browser.contentWindow.document.characterSet; return this.readAndConvertFromStream(descriptor, aCharset);
return this.readAndConvertFromStream(descriptor, charset);
} }
} }
return null; return null;
@ -266,6 +267,81 @@ var NetworkHelper =
}); });
}, },
/**
* Parse a raw Cookie header value.
*
* @param string aHeader
* The raw Cookie header value.
* @return array
* Array holding an object for each cookie. Each object holds the
* following properties: name and value.
*/
parseCookieHeader: function NH_parseCookieHeader(aHeader)
{
let cookies = aHeader.split(";");
let result = [];
cookies.forEach(function(aCookie) {
let [name, value] = aCookie.split("=");
result.push({name: unescape(name.trim()),
value: unescape(value.trim())});
});
return result;
},
/**
* Parse a raw Set-Cookie header value.
*
* @param string aHeader
* The raw Set-Cookie header value.
* @return array
* Array holding an object for each cookie. Each object holds the
* following properties: name, value, secure (boolean), httpOnly
* (boolean), path, domain and expires (ISO date string).
*/
parseSetCookieHeader: function NH_parseSetCookieHeader(aHeader)
{
let rawCookies = aHeader.split(/\r\n|\n|\r/);
let cookies = [];
rawCookies.forEach(function(aCookie) {
let name = unescape(aCookie.substr(0, aCookie.indexOf("=")).trim());
let parts = aCookie.substr(aCookie.indexOf("=") + 1).split(";");
let value = unescape(parts.shift().trim());
let cookie = {name: name, value: value};
parts.forEach(function(aPart) {
let part = aPart.trim();
if (part.toLowerCase() == "secure") {
cookie.secure = true;
}
else if (part.toLowerCase() == "httponly") {
cookie.httpOnly = true;
}
else if (part.indexOf("=") > -1) {
let pair = part.split("=");
pair[0] = pair[0].toLowerCase();
if (pair[0] == "path" || pair[0] == "domain") {
cookie[pair[0]] = pair[1];
}
else if (pair[0] == "expires") {
try {
pair[1] = pair[1].replace(/-/g, ' ');
cookie.expires = new Date(pair[1]).toISOString();
}
catch (ex) { }
}
}
});
cookies.push(cookie);
});
return cookies;
},
// This is a list of all the mime category maps jviereck could find in the // This is a list of all the mime category maps jviereck could find in the
// firebug code base. // firebug code base.
mimeCategoryMap: { mimeCategoryMap: {
@ -333,6 +409,7 @@ var NetworkHelper =
"audio/x-wav": "media", "audio/x-wav": "media",
"text/json": "json", "text/json": "json",
"application/x-json": "json", "application/x-json": "json",
"application/json-rpc": "json" "application/json-rpc": "json",
"application/x-web-app-manifest+json": "json",
} }
} }

View File

@ -0,0 +1,668 @@
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "mimeService", "@mozilla.org/mime;1",
"nsIMIMEService");
XPCOMUtils.defineLazyModuleGetter(this, "NetworkHelper",
"resource:///modules/NetworkHelper.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
"resource:///modules/WebConsoleUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "l10n", function() {
return WebConsoleUtils.l10n;
});
var EXPORTED_SYMBOLS = ["NetworkPanel"];
/**
* Creates a new NetworkPanel.
*
* @param nsIDOMNode aParent
* Parent node to append the created panel to.
* @param object aHttpActivity
* HttpActivity to display in the panel.
*/
function NetworkPanel(aParent, aHttpActivity)
{
let doc = aParent.ownerDocument;
this.httpActivity = aHttpActivity;
// Create the underlaying panel
this.panel = createElement(doc, "panel", {
label: l10n.getStr("NetworkPanel.label"),
titlebar: "normal",
noautofocus: "true",
noautohide: "true",
close: "true"
});
// Create the iframe that displays the NetworkPanel XHTML.
this.iframe = createAndAppendElement(this.panel, "iframe", {
src: "chrome://browser/content/NetworkPanel.xhtml",
type: "content",
flex: "1"
});
let self = this;
// Destroy the panel when it's closed.
this.panel.addEventListener("popuphidden", function onPopupHide() {
self.panel.removeEventListener("popuphidden", onPopupHide, false);
self.panel.parentNode.removeChild(self.panel);
self.panel = null;
self.iframe = null;
self.document = null;
self.httpActivity = null;
if (self.linkNode) {
self.linkNode._panelOpen = false;
self.linkNode = null;
}
}, false);
// Set the document object and update the content once the panel is loaded.
this.panel.addEventListener("load", function onLoad() {
self.panel.removeEventListener("load", onLoad, true);
self.document = self.iframe.contentWindow.document;
self.update();
}, true);
// Create the footer.
let footer = createElement(doc, "hbox", { align: "end" });
createAndAppendElement(footer, "spacer", { flex: 1 });
createAndAppendElement(footer, "resizer", { dir: "bottomend" });
this.panel.appendChild(footer);
aParent.appendChild(this.panel);
}
NetworkPanel.prototype =
{
/**
* Callback is called once the NetworkPanel is processed completely. Used by
* unit tests.
*/
isDoneCallback: null,
/**
* The current state of the output.
*/
_state: 0,
/**
* State variables.
*/
_INIT: 0,
_DISPLAYED_REQUEST_HEADER: 1,
_DISPLAYED_REQUEST_BODY: 2,
_DISPLAYED_RESPONSE_HEADER: 3,
_TRANSITION_CLOSED: 4,
_fromDataRegExp: /Content-Type\:\s*application\/x-www-form-urlencoded/,
_contentType: null,
/**
* Small helper function that is nearly equal to l10n.getFormatStr
* except that it prefixes aName with "NetworkPanel.".
*
* @param string aName
* The name of an i10n string to format. This string is prefixed with
* "NetworkPanel." before calling the HUDService.getFormatStr function.
* @param array aArray
* Values used as placeholder for the i10n string.
* @returns string
* The i10n formated string.
*/
_format: function NP_format(aName, aArray)
{
return l10n.getFormatStr("NetworkPanel." + aName, aArray);
},
/**
* Returns the content type of the response body. This is based on the
* response.content.mimeType property. If this value is not available, then
* the content type is guessed by the file extension of the request URL.
*
* @return string
* Content type or empty string if no content type could be figured
* out.
*/
get contentType()
{
if (this._contentType) {
return this._contentType;
}
let entry = this.httpActivity.log.entries[0];
let request = entry.request;
let response = entry.response;
let contentType = "";
let types = response.content ?
(response.content.mimeType || "").split(/,|;/) : [];
for (let i = 0; i < types.length; i++) {
if (types[i] in NetworkHelper.mimeCategoryMap) {
contentType = types[i];
break;
}
}
if (contentType) {
this._contentType = contentType;
return contentType;
}
// Try to get the content type from the request file extension.
let uri = NetUtil.newURI(request.url);
if ((uri instanceof Ci.nsIURL) && uri.fileExtension) {
try {
contentType = mimeService.getTypeFromExtension(uri.fileExtension);
}
catch(ex) {
// Added to prevent failures on OS X 64. No Flash?
Cu.reportError(ex);
}
}
this._contentType = contentType;
return contentType;
},
/**
*
* @returns boolean
* True if the response is an image, false otherwise.
*/
get _responseIsImage()
{
return this.contentType &&
NetworkHelper.mimeCategoryMap[this.contentType] == "image";
},
/**
*
* @returns boolean
* True if the response body contains text, false otherwise.
*/
get _isResponseBodyTextData()
{
let contentType = this.contentType;
if (!contentType)
return false;
if (contentType.indexOf("text/") == 0) {
return true;
}
switch (NetworkHelper.mimeCategoryMap[contentType]) {
case "txt":
case "js":
case "json":
case "css":
case "html":
case "svg":
case "xml":
return true;
default:
return false;
}
},
/**
* Tells if the server response is cached.
*
* @returns boolean
* Returns true if the server responded that the request is already
* in the browser's cache, false otherwise.
*/
get _isResponseCached()
{
return this.httpActivity.log.entries[0].response.status == 304;
},
/**
* Tells if the request body includes form data.
*
* @returns boolean
* Returns true if the posted body contains form data.
*/
get _isRequestBodyFormData()
{
let requestBody = this.httpActivity.log.entries[0].request.postData.text;
return this._fromDataRegExp.test(requestBody);
},
/**
* Appends the node with id=aId by the text aValue.
*
* @param string aId
* @param string aValue
* @returns void
*/
_appendTextNode: function NP_appendTextNode(aId, aValue)
{
let textNode = this.document.createTextNode(aValue);
this.document.getElementById(aId).appendChild(textNode);
},
/**
* Generates some HTML to display the key-value pair of the aList data. The
* generated HTML is added to node with id=aParentId.
*
* @param string aParentId
* Id of the parent node to append the list to.
* @oaram array aList
* Array that holds the objects you want to display. Each object must
* have two properties: name and value.
* @param boolean aIgnoreCookie
* If true, the key-value named "Cookie" is not added to the list.
* @returns void
*/
_appendList: function NP_appendList(aParentId, aList, aIgnoreCookie)
{
let parent = this.document.getElementById(aParentId);
let doc = this.document;
aList.sort(function(a, b) {
return a.name.toLowerCase() < b.name.toLowerCase();
});
aList.forEach(function(aItem) {
let name = aItem.name;
let value = aItem.value;
if (aIgnoreCookie && name == "Cookie") {
return;
}
/**
* The following code creates the HTML:
* <tr>
* <th scope="row" class="property-name">${line}:</th>
* <td class="property-value">${aList[line]}</td>
* </tr>
* and adds it to parent.
*/
let row = doc.createElement("tr");
let textNode = doc.createTextNode(name + ":");
let th = doc.createElement("th");
th.setAttribute("scope", "row");
th.setAttribute("class", "property-name");
th.appendChild(textNode);
row.appendChild(th);
textNode = doc.createTextNode(value);
let td = doc.createElement("td");
td.setAttribute("class", "property-value");
td.appendChild(textNode);
row.appendChild(td);
parent.appendChild(row);
});
},
/**
* Displays the node with id=aId.
*
* @param string aId
* @returns void
*/
_displayNode: function NP_displayNode(aId)
{
this.document.getElementById(aId).style.display = "block";
},
/**
* Sets the request URL, request method, the timing information when the
* request started and the request header content on the NetworkPanel.
* If the request header contains cookie data, a list of sent cookies is
* generated and a special sent cookie section is displayed + the cookie list
* added to it.
*
* @returns void
*/
_displayRequestHeader: function NP__displayRequestHeader()
{
let entry = this.httpActivity.log.entries[0];
let request = entry.request;
let requestTime = new Date(entry.startedDateTime);
this._appendTextNode("headUrl", request.url);
this._appendTextNode("headMethod", request.method);
this._appendTextNode("requestHeadersInfo",
l10n.timestampString(requestTime));
this._appendList("requestHeadersContent", request.headers, true);
if (request.cookies.length > 0) {
this._displayNode("requestCookie");
this._appendList("requestCookieContent", request.cookies);
}
},
/**
* Displays the request body section of the NetworkPanel and set the request
* body content on the NetworkPanel.
*
* @returns void
*/
_displayRequestBody: function NP__displayRequestBody() {
let postData = this.httpActivity.log.entries[0].request.postData;
this._displayNode("requestBody");
this._appendTextNode("requestBodyContent", postData.text);
},
/*
* Displays the `sent form data` section. Parses the request header for the
* submitted form data displays it inside of the `sent form data` section.
*
* @returns void
*/
_displayRequestForm: function NP__processRequestForm() {
let postData = this.httpActivity.log.entries[0].request.postData.text;
let requestBodyLines = postData.split("\n");
let formData = requestBodyLines[requestBodyLines.length - 1].
replace(/\+/g, " ").split("&");
function unescapeText(aText)
{
try {
return decodeURIComponent(aText);
}
catch (ex) {
return decodeURIComponent(unescape(aText));
}
}
let formDataArray = [];
for (let i = 0; i < formData.length; i++) {
let data = formData[i];
let idx = data.indexOf("=");
let key = data.substring(0, idx);
let value = data.substring(idx + 1);
formDataArray.push({
name: unescapeText(key),
value: unescapeText(value)
});
}
this._appendList("requestFormDataContent", formDataArray);
this._displayNode("requestFormData");
},
/**
* Displays the response section of the NetworkPanel, sets the response status,
* the duration between the start of the request and the receiving of the
* response header as well as the response header content on the the NetworkPanel.
*
* @returns void
*/
_displayResponseHeader: function NP__displayResponseHeader()
{
let entry = this.httpActivity.log.entries[0];
let timing = entry.timings;
let response = entry.response;
this._appendTextNode("headStatus",
[response.httpVersion, response.status,
response.statusText].join(" "));
// Calculate how much time it took from the request start, until the
// response started to be received.
let deltaDuration = 0;
["dns", "connect", "send", "wait"].forEach(function (aValue) {
let ms = timing[aValue];
if (ms > -1) {
deltaDuration += ms;
}
});
this._appendTextNode("responseHeadersInfo",
this._format("durationMS", [deltaDuration]));
this._displayNode("responseContainer");
this._appendList("responseHeadersContent", response.headers);
},
/**
* Displays the respones image section, sets the source of the image displayed
* in the image response section to the request URL and the duration between
* the receiving of the response header and the end of the request. Once the
* image is loaded, the size of the requested image is set.
*
* @returns void
*/
_displayResponseImage: function NP__displayResponseImage()
{
let self = this;
let entry = this.httpActivity.log.entries[0];
let timing = entry.timings;
let request = entry.request;
let cached = "";
if (this._isResponseCached) {
cached = "Cached";
}
let imageNode = this.document.getElementById("responseImage" + cached +"Node");
imageNode.setAttribute("src", request.url);
// This function is called to set the imageInfo.
function setImageInfo() {
self._appendTextNode("responseImage" + cached + "Info",
self._format("imageSizeDeltaDurationMS",
[ imageNode.width, imageNode.height, timing.receive ]
)
);
}
// Check if the image is already loaded.
if (imageNode.width != 0) {
setImageInfo();
}
else {
// Image is not loaded yet therefore add a load event.
imageNode.addEventListener("load", function imageNodeLoad() {
imageNode.removeEventListener("load", imageNodeLoad, false);
setImageInfo();
}, false);
}
this._displayNode("responseImage" + cached);
},
/**
* Displays the response body section, sets the the duration between
* the receiving of the response header and the end of the request as well as
* the content of the response body on the NetworkPanel.
*
* @returns void
*/
_displayResponseBody: function NP__displayResponseBody()
{
let entry = this.httpActivity.log.entries[0];
let timing = entry.timings;
let response = entry.response;
let cached = this._isResponseCached ? "Cached" : "";
this._appendTextNode("responseBody" + cached + "Info",
this._format("durationMS", [timing.receive]));
this._displayNode("responseBody" + cached);
this._appendTextNode("responseBody" + cached + "Content",
response.content.text);
},
/**
* Displays the `Unknown Content-Type hint` and sets the duration between the
* receiving of the response header on the NetworkPanel.
*
* @returns void
*/
_displayResponseBodyUnknownType: function NP__displayResponseBodyUnknownType()
{
let timing = this.httpActivity.log.entries[0].timings;
this._displayNode("responseBodyUnknownType");
this._appendTextNode("responseBodyUnknownTypeInfo",
this._format("durationMS", [timing.receive]));
this._appendTextNode("responseBodyUnknownTypeContent",
this._format("responseBodyUnableToDisplay.content", [this.contentType]));
},
/**
* Displays the `no response body` section and sets the the duration between
* the receiving of the response header and the end of the request.
*
* @returns void
*/
_displayNoResponseBody: function NP_displayNoResponseBody()
{
let timing = this.httpActivity.log.entries[0].timings;
this._displayNode("responseNoBody");
this._appendTextNode("responseNoBodyInfo",
this._format("durationMS", [timing.receive]));
},
/**
* Updates the content of the NetworkPanel's iframe.
*
* @returns void
*/
update: function NP_update()
{
// After the iframe's contentWindow is ready, the document object is set.
// If the document object is not available yet nothing needs to be updated.
if (!this.document) {
return;
}
let stages = this.httpActivity.meta.stages;
let entry = this.httpActivity.log.entries[0];
let timing = entry.timings;
let request = entry.request;
let response = entry.response;
switch (this._state) {
case this._INIT:
this._displayRequestHeader();
this._state = this._DISPLAYED_REQUEST_HEADER;
// FALL THROUGH
case this._DISPLAYED_REQUEST_HEADER:
// Process the request body if there is one.
if (!this.httpActivity.meta.discardRequestBody && request.postData) {
// Check if we send some form data. If so, display the form data special.
if (this._isRequestBodyFormData) {
this._displayRequestForm();
}
else {
this._displayRequestBody();
}
this._state = this._DISPLAYED_REQUEST_BODY;
}
// FALL THROUGH
case this._DISPLAYED_REQUEST_BODY:
// There is always a response header. Therefore we can skip here if
// we don't have a response header yet and don't have to try updating
// anything else in the NetworkPanel.
if (!response.headers.length || !Object.keys(timing).length) {
break;
}
this._displayResponseHeader();
this._state = this._DISPLAYED_RESPONSE_HEADER;
// FALL THROUGH
case this._DISPLAYED_RESPONSE_HEADER:
if (stages.indexOf("REQUEST_STOP") == -1 ||
stages.indexOf("TRANSACTION_CLOSE") == -1) {
break;
}
this._state = this._TRANSITION_CLOSED;
if (this.httpActivity.meta.discardResponseBody) {
break;
}
if (!response.content || !response.content.text) {
this._displayNoResponseBody();
}
else if (this._responseIsImage) {
this._displayResponseImage();
}
else if (!this._isResponseBodyTextData) {
this._displayResponseBodyUnknownType();
}
else if (response.content.text) {
this._displayResponseBody();
}
break;
}
}
}
/**
* Creates a DOMNode and sets all the attributes of aAttributes on the created
* element.
*
* @param nsIDOMDocument aDocument
* Document to create the new DOMNode.
* @param string aTag
* Name of the tag for the DOMNode.
* @param object aAttributes
* Attributes set on the created DOMNode.
*
* @returns nsIDOMNode
*/
function createElement(aDocument, aTag, aAttributes)
{
let node = aDocument.createElement(aTag);
if (aAttributes) {
for (let attr in aAttributes) {
node.setAttribute(attr, aAttributes[attr]);
}
}
return node;
}
/**
* Creates a new DOMNode and appends it to aParent.
*
* @param nsIDOMNode aParent
* A parent node to append the created element.
* @param string aTag
* Name of the tag for the DOMNode.
* @param object aAttributes
* Attributes set on the created DOMNode.
*
* @returns nsIDOMNode
*/
function createAndAppendElement(aParent, aTag, aAttributes)
{
let node = createElement(aParent.ownerDocument, aTag, aAttributes);
aParent.appendChild(node);
return node;
}

View File

@ -11,17 +11,12 @@ const Ci = Components.interfaces;
const Cu = Components.utils; const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "WebConsoleUtils", function () { XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
let obj = {}; "resource:///modules/WebConsoleUtils.jsm");
Cu.import("resource:///modules/WebConsoleUtils.jsm", obj);
return obj.WebConsoleUtils;
});
var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView"]; var EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView"];
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
//// PropertyTreeView. //// PropertyTreeView.

View File

@ -11,7 +11,9 @@ const Ci = Components.interfaces;
const Cu = Components.utils; const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
var EXPORTED_SYMBOLS = ["WebConsoleUtils", "JSPropertyProvider"]; var EXPORTED_SYMBOLS = ["WebConsoleUtils", "JSPropertyProvider"];

View File

@ -5,31 +5,17 @@
const TEST_FILE = "test-network.html"; const TEST_FILE = "test-network.html";
function tabLoad(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true);
openConsole();
let hudId = HUDService.getHudIdByWindow(content);
hud = HUDService.hudReferences[hudId];
browser.addEventListener("load", tabReload, true);
content.location.reload();
}
function tabReload(aEvent) { function tabReload(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true); browser.removeEventListener(aEvent.type, tabReload, true);
let textContent = hud.outputNode.textContent; outputNode = hud.outputNode;
isnot(textContent.indexOf("test-network.html"), -1, findLogEntry("test-network.html");
"found test-network.html"); findLogEntry("test-image.png");
isnot(textContent.indexOf("test-image.png"), -1, "found test-image.png"); findLogEntry("testscript.js");
isnot(textContent.indexOf("testscript.js"), -1, "found testscript.js"); isnot(outputNode.textContent.indexOf("running network console logging tests"), -1,
isnot(textContent.indexOf("running network console logging tests"), -1,
"found the console.log() message from testscript.js"); "found the console.log() message from testscript.js");
finishTest(); executeSoon(finishTest);
} }
function test() { function test() {
@ -42,5 +28,12 @@ function test() {
let uri = Services.io.newFileURI(dir); let uri = Services.io.newFileURI(dir);
addTab(uri.spec); addTab(uri.spec);
browser.addEventListener("load", tabLoad, true); browser.addEventListener("load", function tabLoad() {
browser.removeEventListener("load", tabLoad, true);
openConsole(null, function(aHud) {
hud = aHud;
browser.addEventListener("load", tabReload, true);
content.location.reload();
});
}, true);
} }

View File

@ -10,25 +10,27 @@
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-599725-response-headers.sjs"; const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-599725-response-headers.sjs";
let lastFinishedRequest = null; function performTest(lastFinishedRequest)
function requestDoneCallback(aHttpRequest)
{
lastFinishedRequest = aHttpRequest;
}
function performTest()
{ {
ok(lastFinishedRequest, "page load was logged"); ok(lastFinishedRequest, "page load was logged");
let headers = lastFinishedRequest.response.header; function readHeader(aName)
ok(headers, "we have the response headers"); {
ok(!headers["Content-Type"], "we do not have the Content-Type header"); for (let header of headers) {
ok(headers["Content-Length"] != 60, "Content-Length != 60"); if (header.name == aName) {
return header.value;
}
}
return null;
}
let headers = lastFinishedRequest.log.entries[0].response.headers;
ok(headers, "we have the response headers");
ok(!readHeader("Content-Type"), "we do not have the Content-Type header");
isnot(readHeader("Content-Length"), 60, "Content-Length != 60");
lastFinishedRequest = null;
HUDService.lastFinishedRequestCallback = null; HUDService.lastFinishedRequestCallback = null;
finishTest(); executeSoon(finishTest);
} }
function test() function test()
@ -37,15 +39,15 @@ function test()
let initialLoad = true; let initialLoad = true;
browser.addEventListener("load", function () { browser.addEventListener("load", function onLoad() {
if (initialLoad) { if (initialLoad) {
openConsole(); openConsole(null, function() {
HUDService.lastFinishedRequestCallback = requestDoneCallback; HUDService.lastFinishedRequestCallback = performTest;
content.location.reload(); content.location.reload();
});
initialLoad = false; initialLoad = false;
} else { } else {
browser.removeEventListener("load", arguments.callee, true); browser.removeEventListener("load", onLoad, true);
performTest();
} }
}, true); }, true);
} }

View File

@ -10,28 +10,19 @@
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-600183-charset.html"; const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-600183-charset.html";
let lastFinishedRequest = null; function performTest(lastFinishedRequest)
function requestDoneCallback(aHttpRequest)
{
lastFinishedRequest = aHttpRequest;
}
function performTest()
{ {
ok(lastFinishedRequest, "charset test page was loaded and logged"); ok(lastFinishedRequest, "charset test page was loaded and logged");
let body = lastFinishedRequest.response.body; let body = lastFinishedRequest.log.entries[0].response.content.text;
ok(body, "we have the response body"); ok(body, "we have the response body");
let chars = "\u7684\u95ee\u5019!"; // 的问候! let chars = "\u7684\u95ee\u5019!"; // 的问候!
isnot(body.indexOf("<p>" + chars + "</p>"), -1, isnot(body.indexOf("<p>" + chars + "</p>"), -1,
"found the chinese simplified string"); "found the chinese simplified string");
lastFinishedRequest = null;
HUDService.saveRequestAndResponseBodies = false;
HUDService.lastFinishedRequestCallback = null; HUDService.lastFinishedRequestCallback = null;
finishTest(); executeSoon(finishTest);
} }
function test() function test()
@ -40,20 +31,18 @@ function test()
let initialLoad = true; let initialLoad = true;
browser.addEventListener("load", function () { browser.addEventListener("load", function onLoad() {
if (initialLoad) { if (initialLoad) {
waitForFocus(function() { openConsole(null, function(hud) {
openConsole();
HUDService.saveRequestAndResponseBodies = true; hud.saveRequestAndResponseBodies = true;
HUDService.lastFinishedRequestCallback = requestDoneCallback; HUDService.lastFinishedRequestCallback = performTest;
content.location = TEST_URI; content.location = TEST_URI;
}, content); });
initialLoad = false; initialLoad = false;
} else { } else {
browser.removeEventListener("load", arguments.callee, true); browser.removeEventListener("load", onLoad, true);
performTest();
} }
}, true); }, true);
} }

View File

@ -57,10 +57,10 @@ function onpopupshown2(aEvent)
isnot(menuitems[1].getAttribute("checked"), "true", isnot(menuitems[1].getAttribute("checked"), "true",
"menuitems[1] is not checked"); "menuitems[1] is not checked");
ok(!HUDService.saveRequestAndResponseBodies, "bodies are not logged"); ok(!huds[1].saveRequestAndResponseBodies, "bodies are not logged");
// Enable body logging. // Enable body logging.
HUDService.saveRequestAndResponseBodies = true; huds[1].saveRequestAndResponseBodies = true;
menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) { menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
menupopups[1].removeEventListener(aEvent.type, _onhidden, false); menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
@ -103,11 +103,12 @@ function onpopupshown1(aEvent)
{ {
menupopups[0].removeEventListener(aEvent.type, onpopupshown1, false); menupopups[0].removeEventListener(aEvent.type, onpopupshown1, false);
// The menuitem checkbox must be in sync with the other tabs. // The menuitem checkbox must not be in sync with the other tabs.
is(menuitems[0].getAttribute("checked"), "true", "menuitems[0] is checked"); isnot(menuitems[0].getAttribute("checked"), "true",
"menuitems[0] is not checked");
// Disable body logging. // Enable body logging for tab 1 as well.
HUDService.saveRequestAndResponseBodies = false; huds[0].saveRequestAndResponseBodies = true;
// Close the menu, and switch back to tab 2. // Close the menu, and switch back to tab 2.
menupopups[0].addEventListener("popuphidden", function _onhidden(aEvent) { menupopups[0].addEventListener("popuphidden", function _onhidden(aEvent) {
@ -127,8 +128,7 @@ function onpopupshown2c(aEvent)
{ {
menupopups[1].removeEventListener(aEvent.type, onpopupshown2c, false); menupopups[1].removeEventListener(aEvent.type, onpopupshown2c, false);
isnot(menuitems[1].getAttribute("checked"), "true", is(menuitems[1].getAttribute("checked"), "true", "menuitems[1] is checked");
"menuitems[1] is not checked");
menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) { menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
menupopups[1].removeEventListener(aEvent.type, _onhidden, false); menupopups[1].removeEventListener(aEvent.type, _onhidden, false);

View File

@ -41,14 +41,11 @@ let TestObserver = {
function tabLoad(aEvent) { function tabLoad(aEvent) {
browser.removeEventListener(aEvent.type, tabLoad, true); browser.removeEventListener(aEvent.type, tabLoad, true);
openConsole(); openConsole(null, function(aHud) {
hud = aHud;
let hudId = HUDService.getHudIdByWindow(content); Services.console.registerListener(TestObserver);
hud = HUDService.hudReferences[hudId]; content.location = TEST_URI;
});
Services.console.registerListener(TestObserver);
content.location = TEST_URI;
} }
function performTest() { function performTest() {

View File

@ -92,8 +92,8 @@ function tabLoaded() {
successFn: function() successFn: function()
{ {
let jstermMessage = HUD.outputNode.querySelector(".webconsole-msg-output"); let jstermMessage = HUD.outputNode.querySelector(".webconsole-msg-output");
EventUtils.synthesizeMouse(networkLink, 2, 2, {});
EventUtils.synthesizeMouse(jstermMessage, 2, 2, {}); EventUtils.synthesizeMouse(jstermMessage, 2, 2, {});
EventUtils.synthesizeMouse(networkLink, 2, 2, {});
}, },
failureFn: finishTest, failureFn: finishTest,
}); });

View File

@ -118,8 +118,8 @@ function tabLoaded() {
successFn: function() successFn: function()
{ {
let jstermMessage = HUD.outputNode.querySelector(".webconsole-msg-output"); let jstermMessage = HUD.outputNode.querySelector(".webconsole-msg-output");
EventUtils.synthesizeMouse(networkLink, 2, 2, {});
EventUtils.synthesizeMouse(jstermMessage, 2, 2, {}); EventUtils.synthesizeMouse(jstermMessage, 2, 2, {});
EventUtils.synthesizeMouse(networkLink, 2, 2, {});
}, },
failureFn: finishTest, failureFn: finishTest,
}); });

View File

@ -13,57 +13,63 @@ let lastFinishedRequests = {};
function requestDoneCallback(aHttpRequest) function requestDoneCallback(aHttpRequest)
{ {
let status = aHttpRequest.response.status. let status = aHttpRequest.log.entries[0].response.status;
replace(/^HTTP\/\d\.\d (\d+).+$/, "$1");
lastFinishedRequests[status] = aHttpRequest; lastFinishedRequests[status] = aHttpRequest;
} }
function performTest(aEvent) function performTest(aEvent)
{ {
HUDService.saveRequestAndResponseBodies = false;
HUDService.lastFinishedRequestCallback = null; HUDService.lastFinishedRequestCallback = null;
ok("301" in lastFinishedRequests, "request 1: 301 Moved Permanently"); ok("301" in lastFinishedRequests, "request 1: 301 Moved Permanently");
ok("404" in lastFinishedRequests, "request 2: 404 Not found"); ok("404" in lastFinishedRequests, "request 2: 404 Not found");
let headers0 = lastFinishedRequests["301"].response.header; function readHeader(aName)
is(headers0["Content-Type"], "text/html", {
for (let header of headers) {
if (header.name == aName) {
return header.value;
}
}
return null;
}
let headers = lastFinishedRequests["301"].log.entries[0].response.headers;
is(readHeader("Content-Type"), "text/html",
"we do have the Content-Type header"); "we do have the Content-Type header");
is(headers0["Content-Length"], 71, "Content-Length is correct"); is(readHeader("Content-Length"), 71, "Content-Length is correct");
is(headers0["Location"], "/redirect-from-bug-630733", is(readHeader("Location"), "/redirect-from-bug-630733",
"Content-Length is correct"); "Content-Length is correct");
is(headers0["x-foobar-bug630733"], "bazbaz", is(readHeader("x-foobar-bug630733"), "bazbaz",
"X-Foobar-bug630733 is correct"); "X-Foobar-bug630733 is correct");
let body = lastFinishedRequests["301"].response.body; let body = lastFinishedRequests["301"].log.entries[0].response.content;
ok(!body, "body discarded for request 1"); ok(!body.text, "body discarded for request 1");
let headers1 = lastFinishedRequests["404"].response.header; headers = lastFinishedRequests["404"].log.entries[0].response.headers;
ok(!headers1["Location"], "no Location header"); ok(!readHeader("Location"), "no Location header");
ok(!headers1["x-foobar-bug630733"], "no X-Foobar-bug630733 header"); ok(!readHeader("x-foobar-bug630733"), "no X-Foobar-bug630733 header");
body = lastFinishedRequests["404"].response.body; body = lastFinishedRequests["404"].log.entries[0].response.content.text;
isnot(body.indexOf("404"), -1, isnot(body.indexOf("404"), -1,
"body is correct for request 2"); "body is correct for request 2");
lastFinishedRequests = null; lastFinishedRequests = null;
finishTest(); executeSoon(finishTest);
} }
function test() function test()
{ {
addTab("data:text/html;charset=utf-8,<p>Web Console test for bug 630733"); addTab("data:text/html;charset=utf-8,<p>Web Console test for bug 630733");
browser.addEventListener("load", function(aEvent) { browser.addEventListener("load", function onLoad1(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true); browser.removeEventListener(aEvent.type, onLoad1, true);
executeSoon(function() { openConsole(null, function(hud) {
openConsole(); hud.saveRequestAndResponseBodies = true;
HUDService.saveRequestAndResponseBodies = true;
HUDService.lastFinishedRequestCallback = requestDoneCallback; HUDService.lastFinishedRequestCallback = requestDoneCallback;
browser.addEventListener("load", function(aEvent) { browser.addEventListener("load", function onLoad2(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true); browser.removeEventListener(aEvent.type, onLoad2, true);
executeSoon(performTest); executeSoon(performTest);
}, true); }, true);

View File

@ -18,38 +18,36 @@ function test()
{ {
addTab("data:text/html;charset=utf-8,Web Console network logging tests"); addTab("data:text/html;charset=utf-8,Web Console network logging tests");
browser.addEventListener("load", function() { browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", arguments.callee, true); browser.removeEventListener("load", onLoad, true);
openConsole(); openConsole(null, function(aHud) {
hud = aHud;
hud = HUDService.getHudByWindow(content); HUDService.lastFinishedRequestCallback = function(aRequest) {
ok(hud, "Web Console is now open"); lastRequest = aRequest.log.entries[0];
if (requestCallback) {
requestCallback();
}
};
HUDService.lastFinishedRequestCallback = function(aRequest) { executeSoon(testPageLoad);
lastRequest = aRequest; });
if (requestCallback) {
requestCallback();
}
};
executeSoon(testPageLoad);
}, true); }, true);
} }
function testPageLoad() function testPageLoad()
{ {
browser.addEventListener("load", function(aEvent) { requestCallback = function() {
browser.removeEventListener(aEvent.type, arguments.callee, true);
// Check if page load was logged correctly. // Check if page load was logged correctly.
ok(lastRequest, "Page load was logged"); ok(lastRequest, "Page load was logged");
is(lastRequest.url, TEST_NETWORK_REQUEST_URI, is(lastRequest.request.url, TEST_NETWORK_REQUEST_URI,
"Logged network entry is page load"); "Logged network entry is page load");
is(lastRequest.method, "GET", "Method is correct"); is(lastRequest.request.method, "GET", "Method is correct");
lastRequest = null; lastRequest = null;
requestCallback = null;
executeSoon(testPageLoadBody); executeSoon(testPageLoadBody);
}, true); };
content.location = TEST_NETWORK_REQUEST_URI; content.location = TEST_NETWORK_REQUEST_URI;
} }
@ -57,12 +55,12 @@ function testPageLoad()
function testPageLoadBody() function testPageLoadBody()
{ {
// Turn off logging of request bodies and check again. // Turn off logging of request bodies and check again.
browser.addEventListener("load", function(aEvent) { requestCallback = function() {
browser.removeEventListener(aEvent.type, arguments.callee, true);
ok(lastRequest, "Page load was logged again"); ok(lastRequest, "Page load was logged again");
lastRequest = null; lastRequest = null;
requestCallback = null;
executeSoon(testXhrGet); executeSoon(testXhrGet);
}, true); };
content.location.reload(); content.location.reload();
} }
@ -71,7 +69,7 @@ function testXhrGet()
{ {
requestCallback = function() { requestCallback = function() {
ok(lastRequest, "testXhrGet() was logged"); ok(lastRequest, "testXhrGet() was logged");
is(lastRequest.method, "GET", "Method is correct"); is(lastRequest.request.method, "GET", "Method is correct");
lastRequest = null; lastRequest = null;
requestCallback = null; requestCallback = null;
executeSoon(testXhrPost); executeSoon(testXhrPost);
@ -85,7 +83,7 @@ function testXhrPost()
{ {
requestCallback = function() { requestCallback = function() {
ok(lastRequest, "testXhrPost() was logged"); ok(lastRequest, "testXhrPost() was logged");
is(lastRequest.method, "POST", "Method is correct"); is(lastRequest.request.method, "POST", "Method is correct");
lastRequest = null; lastRequest = null;
requestCallback = null; requestCallback = null;
executeSoon(testFormSubmission); executeSoon(testFormSubmission);
@ -99,12 +97,11 @@ function testFormSubmission()
{ {
// Start the form submission test. As the form is submitted, the page is // Start the form submission test. As the form is submitted, the page is
// loaded again. Bind to the load event to catch when this is done. // loaded again. Bind to the load event to catch when this is done.
browser.addEventListener("load", function(aEvent) { requestCallback = function() {
browser.removeEventListener(aEvent.type, arguments.callee, true);
ok(lastRequest, "testFormSubmission() was logged"); ok(lastRequest, "testFormSubmission() was logged");
is(lastRequest.method, "POST", "Method is correct"); is(lastRequest.request.method, "POST", "Method is correct");
executeSoon(testLiveFilteringOnSearchStrings); executeSoon(testLiveFilteringOnSearchStrings);
}, true); };
let form = content.document.querySelector("form"); let form = content.document.querySelector("form");
ok(form, "we have the HTML form"); ok(form, "we have the HTML form");
@ -112,9 +109,6 @@ function testFormSubmission()
} }
function testLiveFilteringOnSearchStrings() { function testLiveFilteringOnSearchStrings() {
browser.removeEventListener("DOMContentLoaded",
testLiveFilteringOnSearchStrings, false);
setStringFilter("http"); setStringFilter("http");
isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " + isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " +
"search string is set to \"http\""); "search string is set to \"http\"");
@ -146,6 +140,9 @@ function testLiveFilteringOnSearchStrings() {
is(countMessageNodes(), 0, "the log nodes are hidden when searching for " + is(countMessageNodes(), 0, "the log nodes are hidden when searching for " +
"the string \"foo\"bar'baz\"boo'\""); "the string \"foo\"bar'baz\"boo'\"");
HUDService.lastFinishedRequestCallback = null;
lastRequest = null;
requestCallback = null;
finishTest(); finishTest();
} }

View File

@ -21,47 +21,47 @@ const TEST_DATA_JSON_CONTENT =
let lastRequest = null; let lastRequest = null;
let requestCallback = null; let requestCallback = null;
let lastActivity = null;
function test() function test()
{ {
addTab("data:text/html;charset=utf-8,Web Console network logging tests"); addTab("data:text/html;charset=utf-8,Web Console network logging tests");
browser.addEventListener("load", function() { browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", arguments.callee, true); browser.removeEventListener("load", onLoad, true);
openConsole(); openConsole(null, function(aHud) {
hud = aHud;
hud = HUDService.getHudByWindow(content); HUDService.lastFinishedRequestCallback = function(aRequest) {
ok(hud, "Web Console is now open"); lastRequest = aRequest.log.entries[0];
lastActivity = aRequest;
if (requestCallback) {
requestCallback();
}
};
HUDService.lastFinishedRequestCallback = function(aRequest) { executeSoon(testPageLoad);
lastRequest = aRequest; });
if (requestCallback) {
requestCallback();
}
};
executeSoon(testPageLoad);
}, true); }, true);
} }
function testPageLoad() function testPageLoad()
{ {
browser.addEventListener("load", function(aEvent) { requestCallback = function() {
browser.removeEventListener(aEvent.type, arguments.callee, true);
// Check if page load was logged correctly. // Check if page load was logged correctly.
ok(lastRequest, "Page load was logged"); ok(lastRequest, "Page load was logged");
is(lastRequest.url, TEST_NETWORK_REQUEST_URI,
is(lastRequest.request.url, TEST_NETWORK_REQUEST_URI,
"Logged network entry is page load"); "Logged network entry is page load");
is(lastRequest.method, "GET", "Method is correct"); is(lastRequest.request.method, "GET", "Method is correct");
ok(!("body" in lastRequest.request), "No request body was stored"); ok(!lastRequest.request.postData, "No request body was stored");
ok(!("body" in lastRequest.response), "No response body was stored"); ok(!lastRequest.response.content.text, "No response body was stored");
ok(!lastRequest.response.listener, "No response listener is stored");
lastRequest = null; lastRequest = null;
requestCallback = null;
executeSoon(testPageLoadBody); executeSoon(testPageLoadBody);
}, true); };
content.location = TEST_NETWORK_REQUEST_URI; content.location = TEST_NETWORK_REQUEST_URI;
} }
@ -69,17 +69,16 @@ function testPageLoad()
function testPageLoadBody() function testPageLoadBody()
{ {
// Turn on logging of request bodies and check again. // Turn on logging of request bodies and check again.
HUDService.saveRequestAndResponseBodies = true; hud.saveRequestAndResponseBodies = true;
browser.addEventListener("load", function(aEvent) { requestCallback = function() {
browser.removeEventListener(aEvent.type, arguments.callee, true);
ok(lastRequest, "Page load was logged again"); ok(lastRequest, "Page load was logged again");
is(lastRequest.response.body.indexOf("<!DOCTYPE HTML>"), 0, is(lastRequest.response.content.text.indexOf("<!DOCTYPE HTML>"), 0,
"Response body's beginning is okay"); "Response body's beginning is okay");
lastRequest = null; lastRequest = null;
requestCallback = null;
executeSoon(testXhrGet); executeSoon(testXhrGet);
}, true); };
content.location.reload(); content.location.reload();
} }
@ -88,9 +87,9 @@ function testXhrGet()
{ {
requestCallback = function() { requestCallback = function() {
ok(lastRequest, "testXhrGet() was logged"); ok(lastRequest, "testXhrGet() was logged");
is(lastRequest.method, "GET", "Method is correct"); is(lastRequest.request.method, "GET", "Method is correct");
is(lastRequest.request.body, null, "No request body was sent"); ok(!lastRequest.request.postData, "No request body was sent");
is(lastRequest.response.body, TEST_DATA_JSON_CONTENT, is(lastRequest.response.content.text, TEST_DATA_JSON_CONTENT,
"Response is correct"); "Response is correct");
lastRequest = null; lastRequest = null;
@ -106,10 +105,10 @@ function testXhrPost()
{ {
requestCallback = function() { requestCallback = function() {
ok(lastRequest, "testXhrPost() was logged"); ok(lastRequest, "testXhrPost() was logged");
is(lastRequest.method, "POST", "Method is correct"); is(lastRequest.request.method, "POST", "Method is correct");
is(lastRequest.request.body, "Hello world!", is(lastRequest.request.postData.text, "Hello world!",
"Request body was logged"); "Request body was logged");
is(lastRequest.response.body, TEST_DATA_JSON_CONTENT, is(lastRequest.response.content.text, TEST_DATA_JSON_CONTENT,
"Response is correct"); "Response is correct");
lastRequest = null; lastRequest = null;
@ -125,23 +124,21 @@ function testFormSubmission()
{ {
// Start the form submission test. As the form is submitted, the page is // Start the form submission test. As the form is submitted, the page is
// loaded again. Bind to the load event to catch when this is done. // loaded again. Bind to the load event to catch when this is done.
browser.addEventListener("load", function(aEvent) { requestCallback = function() {
browser.removeEventListener(aEvent.type, arguments.callee, true);
ok(lastRequest, "testFormSubmission() was logged"); ok(lastRequest, "testFormSubmission() was logged");
is(lastRequest.method, "POST", "Method is correct"); is(lastRequest.request.method, "POST", "Method is correct");
isnot(lastRequest.request.body. isnot(lastRequest.request.postData.text.
indexOf("Content-Type: application/x-www-form-urlencoded"), -1, indexOf("Content-Type: application/x-www-form-urlencoded"), -1,
"Content-Type is correct"); "Content-Type is correct");
isnot(lastRequest.request.body. isnot(lastRequest.request.postData.text.
indexOf("Content-Length: 20"), -1, "Content-length is correct"); indexOf("Content-Length: 20"), -1, "Content-length is correct");
isnot(lastRequest.request.body. isnot(lastRequest.request.postData.text.
indexOf("name=foo+bar&age=144"), -1, "Form data is correct"); indexOf("name=foo+bar&age=144"), -1, "Form data is correct");
ok(lastRequest.response.body.indexOf("<!DOCTYPE HTML>") == 0, is(lastRequest.response.content.text.indexOf("<!DOCTYPE HTML>"), 0,
"Response body's beginning is okay"); "Response body's beginning is okay");
executeSoon(testNetworkPanel); executeSoon(testNetworkPanel);
}, true); };
let form = content.document.querySelector("form"); let form = content.document.querySelector("form");
ok(form, "we have the HTML form"); ok(form, "we have the HTML form");
@ -152,19 +149,19 @@ function testNetworkPanel()
{ {
// Open the NetworkPanel. The functionality of the NetworkPanel is tested // Open the NetworkPanel. The functionality of the NetworkPanel is tested
// within separate test files. // within separate test files.
let networkPanel = HUDService.openNetworkPanel(hud.filterBox, lastRequest); let networkPanel = HUDService.openNetworkPanel(hud.filterBox, lastActivity);
is(networkPanel, lastRequest.panels[0].get(), is(networkPanel, hud.filterBox._netPanel,
"Network panel stored on lastRequest object"); "Network panel stored on anchor node");
networkPanel.panel.addEventListener("load", function(aEvent) { networkPanel.panel.addEventListener("load", function onLoad(aEvent) {
networkPanel.panel.removeEventListener(aEvent.type, arguments.callee, networkPanel.panel.removeEventListener(aEvent.type, onLoad, true);
true);
ok(true, "NetworkPanel was opened"); ok(true, "NetworkPanel was opened");
// All tests are done. Shutdown. // All tests are done. Shutdown.
networkPanel.panel.hidePopup(); networkPanel.panel.hidePopup();
lastRequest = null; lastRequest = null;
lastActivity = null;
HUDService.lastFinishedRequestCallback = null; HUDService.lastFinishedRequestCallback = null;
executeSoon(finishTest); executeSoon(finishTest);
}, true); }, true);

View File

@ -68,25 +68,39 @@ function testGen() {
let l10n = tempScope.WebConsoleUtils.l10n; let l10n = tempScope.WebConsoleUtils.l10n;
tempScope = null; tempScope = null;
var httpActivity = { let httpActivity = {
url: "http://www.testpage.com", meta: {
method: "GET", stages: [],
discardRequestBody: true,
panels: [], discardResponseBody: true,
request: { },
header: { log: {
foo: "bar" entries: [{
} startedDateTime: (new Date()).toISOString(),
request: {
url: "http://www.testpage.com",
method: "GET",
cookies: [],
headers: [
{ name: "foo", value: "bar" },
],
},
response: {
headers: [],
content: {},
},
timings: {},
}],
}, },
response: { },
timing: {
"REQUEST_HEADER": 0
}
}; };
let entry = httpActivity.log.entries[0];
let networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity); let networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
is (networkPanel, httpActivity.panels[0].get(), "Network panel stored on httpActivity object"); is(filterBox._netPanel, networkPanel,
"Network panel stored on the anchor object");
networkPanel.panel.addEventListener("load", function onLoad() { networkPanel.panel.addEventListener("load", function onLoad() {
networkPanel.panel.removeEventListener("load", onLoad, true); networkPanel.panel.removeEventListener("load", onLoad, true);
testDriver.next(); testDriver.next();
@ -94,6 +108,8 @@ function testGen() {
yield; yield;
info("test 1");
checkIsVisible(networkPanel, { checkIsVisible(networkPanel, {
requestCookie: false, requestCookie: false,
requestFormData: false, requestFormData: false,
@ -110,8 +126,11 @@ function testGen() {
checkNodeKeyValue(networkPanel, "requestHeadersContent", "foo", "bar"); checkNodeKeyValue(networkPanel, "requestHeadersContent", "foo", "bar");
// Test request body. // Test request body.
httpActivity.request.body = "hello world"; info("test 2: request body");
httpActivity.meta.discardRequestBody = false;
entry.request.postData = { text: "hello world" };
networkPanel.update(); networkPanel.update();
checkIsVisible(networkPanel, { checkIsVisible(networkPanel, {
requestBody: true, requestBody: true,
requestFormData: false, requestFormData: false,
@ -125,13 +144,19 @@ function testGen() {
checkNodeContent(networkPanel, "requestBodyContent", "hello world"); checkNodeContent(networkPanel, "requestBodyContent", "hello world");
// Test response header. // Test response header.
httpActivity.timing.RESPONSE_HEADER = 1000; info("test 3: response header");
httpActivity.response.status = "999 earthquake win"; entry.timings.wait = 10;
httpActivity.response.header = { entry.response.httpVersion = "HTTP/3.14";
"Content-Type": "text/html", entry.response.status = 999;
leaveHouses: "true" entry.response.statusText = "earthquake win";
} entry.response.content.mimeType = "text/html";
entry.response.headers.push(
{ name: "Content-Type", value: "text/html" },
{ name: "leaveHouses", value: "true" }
);
networkPanel.update(); networkPanel.update();
checkIsVisible(networkPanel, { checkIsVisible(networkPanel, {
requestBody: true, requestBody: true,
requestFormData: false, requestFormData: false,
@ -143,13 +168,14 @@ function testGen() {
responseImageCached: false responseImageCached: false
}); });
checkNodeContent(networkPanel, "header", "999 earthquake win"); checkNodeContent(networkPanel, "header", "HTTP/3.14 999 earthquake win");
checkNodeKeyValue(networkPanel, "responseHeadersContent", "leaveHouses", "true"); checkNodeKeyValue(networkPanel, "responseHeadersContent", "leaveHouses", "true");
checkNodeContent(networkPanel, "responseHeadersInfo", "1ms"); checkNodeContent(networkPanel, "responseHeadersInfo", "10ms");
httpActivity.timing.RESPONSE_COMPLETE = 2500; info("test 4");
// This is necessary to show that the request is done.
httpActivity.timing.TRANSACTION_CLOSE = 2500; httpActivity.meta.discardResponseBody = false;
entry.timings.receive = 2;
networkPanel.update(); networkPanel.update();
checkIsVisible(networkPanel, { checkIsVisible(networkPanel, {
@ -163,7 +189,9 @@ function testGen() {
responseImageCached: false responseImageCached: false
}); });
httpActivity.response.isDone = true; info("test 5");
httpActivity.meta.stages.push("REQUEST_STOP", "TRANSACTION_CLOSE");
networkPanel.update(); networkPanel.update();
checkNodeContent(networkPanel, "responseNoBodyInfo", "2ms"); checkNodeContent(networkPanel, "responseNoBodyInfo", "2ms");
@ -180,11 +208,17 @@ function testGen() {
networkPanel.panel.hidePopup(); networkPanel.panel.hidePopup();
// Second run: Test for cookies and response body. // Second run: Test for cookies and response body.
httpActivity.request.header.Cookie = "foo=bar; hello=world"; info("test 6: cookies and response body");
httpActivity.response.body = "get out here"; entry.request.cookies.push(
{ name: "foo", value: "bar" },
{ name: "hello", value: "world" }
);
entry.response.content.text = "get out here";
networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity); networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
is (networkPanel, httpActivity.panels[1].get(), "Network panel stored on httpActivity object"); is(filterBox._netPanel, networkPanel,
"Network panel stored on httpActivity object");
networkPanel.panel.addEventListener("load", function onLoad() { networkPanel.panel.addEventListener("load", function onLoad() {
networkPanel.panel.removeEventListener("load", onLoad, true); networkPanel.panel.removeEventListener("load", onLoad, true);
testDriver.next(); testDriver.next();
@ -192,7 +226,6 @@ function testGen() {
yield; yield;
checkIsVisible(networkPanel, { checkIsVisible(networkPanel, {
requestBody: true, requestBody: true,
requestFormData: false, requestFormData: false,
@ -212,8 +245,10 @@ function testGen() {
networkPanel.panel.hidePopup(); networkPanel.panel.hidePopup();
// Check image request. // Check image request.
httpActivity.response.header["Content-Type"] = "image/png"; info("test 7: image request");
httpActivity.url = TEST_IMG; entry.response.headers[1].value = "image/png";
entry.response.content.mimeType = "image/png";
entry.request.url = TEST_IMG;
networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity); networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
networkPanel.panel.addEventListener("load", function onLoad() { networkPanel.panel.addEventListener("load", function onLoad() {
@ -259,7 +294,10 @@ function testGen() {
} }
// Check cached image request. // Check cached image request.
httpActivity.response.status = "HTTP/1.1 304 Not Modified"; info("test 8: cached image request");
entry.response.httpVersion = "HTTP/1.1";
entry.response.status = 304;
entry.response.statusText = "Not Modified";
networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity); networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
networkPanel.panel.addEventListener("load", function onLoad() { networkPanel.panel.addEventListener("load", function onLoad() {
@ -286,11 +324,12 @@ function testGen() {
networkPanel.panel.hidePopup(); networkPanel.panel.hidePopup();
// Test sent form data. // Test sent form data.
httpActivity.request.body = [ info("test 9: sent form data");
"Content-Type: application/x-www-form-urlencoded\n" + entry.request.postData.text = [
"Content-Length: 59\n" + "Content-Type: application/x-www-form-urlencoded",
"Content-Length: 59",
"name=rob&age=20" "name=rob&age=20"
].join(""); ].join("\n");
networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity); networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
networkPanel.panel.addEventListener("load", function onLoad() { networkPanel.panel.addEventListener("load", function onLoad() {
@ -316,7 +355,8 @@ function testGen() {
networkPanel.panel.hidePopup(); networkPanel.panel.hidePopup();
// Test no space after Content-Type: // Test no space after Content-Type:
httpActivity.request.body = "Content-Type:application/x-www-form-urlencoded\n"; info("test 10: no space after Content-Type header in post data");
entry.request.postData.text = "Content-Type:application/x-www-form-urlencoded\n";
networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity); networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
networkPanel.panel.addEventListener("load", function onLoad() { networkPanel.panel.addEventListener("load", function onLoad() {
@ -341,25 +381,18 @@ function testGen() {
// Test cached data. // Test cached data.
// Load a Latin-1 encoded page. info("test 11: cached data");
browser.addEventListener("load", function onLoad () {
browser.removeEventListener("load", onLoad, true);
httpActivity.charset = content.document.characterSet;
testDriver.next();
}, true);
browser.contentWindow.wrappedJSObject.document.location = TEST_ENCODING_ISO_8859_1;
yield; entry.request.url = TEST_ENCODING_ISO_8859_1;
entry.response.headers[1].value = "application/json";
httpActivity.url = TEST_ENCODING_ISO_8859_1; entry.response.content.mimeType = "application/json";
httpActivity.response.header["Content-Type"] = "application/json"; entry.response.content.text = "my cached data is here!";
httpActivity.response.body = "";
networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity); networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
networkPanel.isDoneCallback = function NP_doneCallback() { networkPanel.panel.addEventListener("load", function onLoad() {
networkPanel.isDoneCallback = null; networkPanel.panel.removeEventListener("load", onLoad, true);
testDriver.next(); testDriver.next();
} }, true);
yield; yield;
@ -375,21 +408,22 @@ function testGen() {
responseImageCached: false responseImageCached: false
}); });
checkNodeContent(networkPanel, "responseBodyCachedContent", "<body>\u00fc\u00f6\u00E4</body>"); checkNodeContent(networkPanel, "responseBodyCachedContent",
"my cached data is here!");
networkPanel.panel.hidePopup(); networkPanel.panel.hidePopup();
// Test a response with a content type that can't be displayed in the // Test a response with a content type that can't be displayed in the
// NetworkPanel. // NetworkPanel.
httpActivity.response.header["Content-Type"] = "application/x-shockwave-flash"; info("test 12: unknown content type");
entry.response.headers[1].value = "application/x-shockwave-flash";
entry.response.content.mimeType = "application/x-shockwave-flash";
networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity); networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
networkPanel.isDoneCallback = function NP_doneCallback() { networkPanel.panel.addEventListener("load", function onLoad() {
networkPanel.isDoneCallback = null; networkPanel.panel.removeEventListener("load", onLoad, true);
try { testDriver.next();
testDriver.next(); }, true);
} catch (e if e instanceof StopIteration) {
}
}
yield; yield;
@ -453,4 +487,6 @@ function testGen() {
// All done! // All done!
testDriver = null; testDriver = null;
executeSoon(finishTest); executeSoon(finishTest);
yield;
} }