mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-10 01:08:21 +00:00
Bug 859980 - [netmonitor] JSON request not parsed, r=rcampbell
This commit is contained in:
parent
24334a783b
commit
130b184f53
@ -466,6 +466,43 @@ NetworkEventsHandler.prototype = {
|
||||
eventTimings: aResponse
|
||||
});
|
||||
window.emit("NetMonitor:NetworkEventUpdated:EventTimings");
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches the full text of a LongString.
|
||||
*
|
||||
* @param object | string aStringGrip
|
||||
* The long string grip containing the corresponding actor.
|
||||
* If you pass in a plain string (by accident or because you're lazy),
|
||||
* then a promise of the same string is simply returned.
|
||||
* @return object Promise
|
||||
* A promise that is resolved when the full string contents
|
||||
* are available, or rejected if something goes wrong.
|
||||
*/
|
||||
getString: function NEH_getString(aStringGrip) {
|
||||
// Make sure this is a long string.
|
||||
if (typeof aStringGrip != "object" || aStringGrip.type != "longString") {
|
||||
return Promise.resolve(aStringGrip); // Go home string, you're drunk.
|
||||
}
|
||||
// Fetch the long string only once.
|
||||
if (aStringGrip._fullText) {
|
||||
return aStringGrip._fullText.promise;
|
||||
}
|
||||
|
||||
let deferred = aStringGrip._fullText = Promise.defer();
|
||||
let { actor, initial, length } = aStringGrip;
|
||||
let longStringClient = this.webConsoleClient.longString(aStringGrip);
|
||||
|
||||
longStringClient.substring(initial.length, length, (aResponse) => {
|
||||
if (aResponse.error) {
|
||||
Cu.reportError(aResponse.error + ": " + aResponse.message);
|
||||
deferred.reject(aResponse);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(initial + aResponse.substring);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
@ -497,7 +534,10 @@ NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
|
||||
*/
|
||||
Object.defineProperties(window, {
|
||||
"create": {
|
||||
get: function() ViewHelpers.create,
|
||||
get: function() ViewHelpers.create
|
||||
},
|
||||
"gNetwork": {
|
||||
get: function() NetMonitorController.NetworkEventsHandler
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -886,7 +886,7 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
|
||||
|
||||
for (let header of aResponse.headers) {
|
||||
let headerVar = headersScope.addVar(header.name, { null: true }, true);
|
||||
headerVar.setGrip(header.value);
|
||||
gNetwork.getString(header.value).then((aString) => headerVar.setGrip(aString));
|
||||
}
|
||||
},
|
||||
|
||||
@ -929,7 +929,7 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
|
||||
|
||||
for (let cookie of aResponse.cookies) {
|
||||
let cookieVar = cookiesScope.addVar(cookie.name, { null: true }, true);
|
||||
cookieVar.setGrip(cookie.value);
|
||||
gNetwork.getString(cookie.value).then((aString) => cookieVar.setGrip(aString));
|
||||
|
||||
// By default the cookie name and value are shown. If this is the only
|
||||
// information available, then nothing else is to be displayed.
|
||||
@ -961,12 +961,7 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
|
||||
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
|
||||
let query = uri.query;
|
||||
if (query) {
|
||||
this._addParams(this._paramsQueryString, query.split("&").map((e) =>
|
||||
let (param = e.split("=")) {
|
||||
name: param[0],
|
||||
value: NetworkHelper.convertToUnicode(unescape(param[1]))
|
||||
}
|
||||
));
|
||||
this._addParams(this._paramsQueryString, query);
|
||||
}
|
||||
},
|
||||
|
||||
@ -985,27 +980,28 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
|
||||
let contentType = aHeadersResponse.headers.filter(({ name }) => name == "Content-Type")[0];
|
||||
let text = aPostResponse.postData.text;
|
||||
|
||||
if (contentType.value.contains("x-www-form-urlencoded")) {
|
||||
this._addParams(this._paramsFormData, text.replace(/^[?&]/, "").split("&").map((e) =>
|
||||
let (param = e.split("=")) {
|
||||
name: param[0],
|
||||
value: NetworkHelper.convertToUnicode(unescape(param[1]))
|
||||
}
|
||||
));
|
||||
} else {
|
||||
// This is really awkward, but hey, it works. Let's show an empty
|
||||
// scope in the params view and place the source editor containing
|
||||
// the raw post data directly underneath.
|
||||
$("#request-params-box").removeAttribute("flex");
|
||||
let paramsScope = this._params.addScope(this._paramsPostPayload);
|
||||
paramsScope.expanded = true;
|
||||
paramsScope.locked = true;
|
||||
gNetwork.getString(text).then((aString) => {
|
||||
// Handle query strings (poor man's forms, e.g. "?foo=bar&baz=42").
|
||||
if (contentType.value.contains("x-www-form-urlencoded")) {
|
||||
this._addParams(this._paramsFormData, aString);
|
||||
}
|
||||
// Handle actual forms ("multipart/form-data" content type).
|
||||
else {
|
||||
// This is really awkward, but hey, it works. Let's show an empty
|
||||
// scope in the params view and place the source editor containing
|
||||
// the raw post data directly underneath.
|
||||
$("#request-params-box").removeAttribute("flex");
|
||||
let paramsScope = this._params.addScope(this._paramsPostPayload);
|
||||
paramsScope.expanded = true;
|
||||
paramsScope.locked = true;
|
||||
|
||||
$("#request-post-data-textarea-box").hidden = false;
|
||||
NetMonitorView.editor("#request-post-data-textarea").then((aEditor) => {
|
||||
aEditor.setText(text);
|
||||
});
|
||||
}
|
||||
$("#request-post-data-textarea-box").hidden = false;
|
||||
NetMonitorView.editor("#request-post-data-textarea").then((aEditor) => {
|
||||
aEditor.setText(aString);
|
||||
});
|
||||
}
|
||||
window.emit("NetMonitor:ResponsePostParamsAvailable");
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1013,14 +1009,21 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
|
||||
*
|
||||
* @param string aName
|
||||
* The type of params to populate (get or post).
|
||||
* @param object aParams
|
||||
* An array containing { name: value } param information tuples.
|
||||
* @param string aParams
|
||||
* A query string of params (e.g. "?foo=bar&baz=42").
|
||||
*/
|
||||
_addParams: function NVND__addParams(aName, aParams) {
|
||||
// Turn the params string into an array containing { name: value } tuples.
|
||||
let paramsArray = aParams.replace(/^[?&]/, "").split("&").map((e) =>
|
||||
let (param = e.split("=")) {
|
||||
name: NetworkHelper.convertToUnicode(unescape(param[0])),
|
||||
value: NetworkHelper.convertToUnicode(unescape(param[1]))
|
||||
});
|
||||
|
||||
let paramsScope = this._params.addScope(aName);
|
||||
paramsScope.expanded = true;
|
||||
|
||||
for (let param of aParams) {
|
||||
for (let param of paramsArray) {
|
||||
let headerVar = paramsScope.addVar(param.name, { null: true }, true);
|
||||
headerVar.setGrip(param.value);
|
||||
}
|
||||
@ -1039,52 +1042,59 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
|
||||
return;
|
||||
}
|
||||
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
|
||||
let { mimeType: mime, text, encoding } = aResponse.content;
|
||||
let { mimeType, text, encoding } = aResponse.content;
|
||||
|
||||
if (mime.contains("/json")) {
|
||||
$("#response-content-json-box").hidden = false;
|
||||
let jsonScope = this._json.addScope("JSON");
|
||||
let sanitizedText = text.replace(/^[a-zA-Z0-9_$]+\(|\)$/g, ""); // JSONP
|
||||
jsonScope.addVar().populate(JSON.parse(sanitizedText), { expanded: true });
|
||||
jsonScope.expanded = true;
|
||||
}
|
||||
else if (mime.contains("image/")) {
|
||||
$("#response-content-image-box").setAttribute("align", "center");
|
||||
$("#response-content-image-box").setAttribute("pack", "center");
|
||||
$("#response-content-image-box").hidden = false;
|
||||
$("#response-content-image").src = "data:" + mime + ";" + encoding + "," + text;
|
||||
gNetwork.getString(text).then((aString) => {
|
||||
// Handle json.
|
||||
if (mimeType.contains("/json")) {
|
||||
$("#response-content-json-box").hidden = false;
|
||||
let jsonScope = this._json.addScope("JSON");
|
||||
let sanitizedText = aString.replace(/^[a-zA-Z0-9_$]+\(|\)$/g, ""); // JSONP
|
||||
jsonScope.addVar().populate(JSON.parse(sanitizedText), { expanded: true });
|
||||
jsonScope.expanded = true;
|
||||
}
|
||||
// Handle images.
|
||||
else if (mimeType.contains("image/")) {
|
||||
$("#response-content-image-box").setAttribute("align", "center");
|
||||
$("#response-content-image-box").setAttribute("pack", "center");
|
||||
$("#response-content-image-box").hidden = false;
|
||||
$("#response-content-image").src =
|
||||
"data:" + mimeType + ";" + encoding + "," + aString;
|
||||
|
||||
// Immediately display additional information about the image:
|
||||
// file name, mime type and encoding.
|
||||
$("#response-content-image-name-value").setAttribute("value", uri.fileName);
|
||||
$("#response-content-image-mime-value").setAttribute("value", mime);
|
||||
$("#response-content-image-encoding-value").setAttribute("value", encoding);
|
||||
// Immediately display additional information about the image:
|
||||
// file name, mime type and encoding.
|
||||
$("#response-content-image-name-value").setAttribute("value", uri.fileName);
|
||||
$("#response-content-image-mime-value").setAttribute("value", mimeType);
|
||||
$("#response-content-image-encoding-value").setAttribute("value", encoding);
|
||||
|
||||
// Wait for the image to load in order to display the width and height.
|
||||
$("#response-content-image").onload = (e) => {
|
||||
// XUL images are majestic so they don't bother storing their dimensions
|
||||
// in width and height attributes like the rest of the folk. Hack around
|
||||
// this by getting the bounding client rect and subtracting the margins.
|
||||
let { width, height } = e.target.getBoundingClientRect();
|
||||
let dimensions = (width - 2) + " x " + (height - 2);
|
||||
$("#response-content-image-dimensions-value").setAttribute("value", dimensions);
|
||||
};
|
||||
}
|
||||
else {
|
||||
$("#response-content-textarea-box").hidden = false;
|
||||
NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
|
||||
aEditor.setMode(SourceEditor.MODES.TEXT);
|
||||
aEditor.setText(typeof text == "string" ? text : text.initial);
|
||||
// Wait for the image to load in order to display the width and height.
|
||||
$("#response-content-image").onload = (e) => {
|
||||
// XUL images are majestic so they don't bother storing their dimensions
|
||||
// in width and height attributes like the rest of the folk. Hack around
|
||||
// this by getting the bounding client rect and subtracting the margins.
|
||||
let { width, height } = e.target.getBoundingClientRect();
|
||||
let dimensions = (width - 2) + " x " + (height - 2);
|
||||
$("#response-content-image-dimensions-value").setAttribute("value", dimensions);
|
||||
};
|
||||
}
|
||||
// Handle anything else.
|
||||
else {
|
||||
$("#response-content-textarea-box").hidden = false;
|
||||
NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
|
||||
aEditor.setMode(SourceEditor.MODES.TEXT);
|
||||
aEditor.setText(aString);
|
||||
|
||||
// Maybe set a more appropriate mode in the Source Editor if possible.
|
||||
for (let key in CONTENT_MIME_TYPE_MAPPINGS) {
|
||||
if (mime.contains(key)) {
|
||||
aEditor.setMode(CONTENT_MIME_TYPE_MAPPINGS[key]);
|
||||
break;
|
||||
// Maybe set a more appropriate mode in the Source Editor if possible.
|
||||
for (let key in CONTENT_MIME_TYPE_MAPPINGS) {
|
||||
if (mimeType.contains(key)) {
|
||||
aEditor.setMode(CONTENT_MIME_TYPE_MAPPINGS[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
window.emit("NetMonitor:ResponseBodyAvailable");
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,7 @@ MOCHITEST_BROWSER_TESTS = \
|
||||
browser_net_status-codes.js \
|
||||
browser_net_post-data.js \
|
||||
browser_net_jsonp.js \
|
||||
browser_net_json-long.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
@ -35,6 +36,7 @@ MOCHITEST_BROWSER_PAGES = \
|
||||
html_status-codes-test-page.html \
|
||||
html_post-data-test-page.html \
|
||||
html_jsonp-test-page.html \
|
||||
html_json-long-test-page.html \
|
||||
sjs_simple-test-server.sjs \
|
||||
sjs_content-type-test-server.sjs \
|
||||
sjs_status-codes-test-server.sjs \
|
||||
|
94
browser/devtools/netmonitor/test/browser_net_json-long.js
Normal file
94
browser/devtools/netmonitor/test/browser_net_json-long.js
Normal file
@ -0,0 +1,94 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if JSONP responses are handled correctly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(JSON_LONG_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
// This is receiving over 80 KB of json and will populate over 6000 items
|
||||
// in a variables view instance. Debug builds are slow.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
let { document, L10N, SourceEditor, NetMonitorView } = aMonitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
waitForNetworkEvents(aMonitor, 1).then(() => {
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-long", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
type: "json",
|
||||
fullMimeType: "text/json; charset=utf-8",
|
||||
size: L10N.getFormatStr("networkMenu.sizeKB", 83.96),
|
||||
time: true
|
||||
});
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.getElementById("details-pane-toggle"));
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll("#details-pane tab")[3]);
|
||||
|
||||
aMonitor.panelWin.once("NetMonitor:ResponseBodyAvailable", () => {
|
||||
testResponseTab();
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
||||
|
||||
function testResponseTab() {
|
||||
let tab = document.querySelectorAll("#details-pane tab")[3];
|
||||
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
|
||||
|
||||
is(tab.getAttribute("selected"), "true",
|
||||
"The response tab in the network details pane should be selected.");
|
||||
|
||||
is(tabpanel.querySelector("#response-content-json-box")
|
||||
.hasAttribute("hidden"), false,
|
||||
"The response content json box doesn't have the intended visibility.");
|
||||
|
||||
is(tabpanel.querySelector("#response-content-textarea-box")
|
||||
.hasAttribute("hidden"), true,
|
||||
"The response content textarea box doesn't have the intended visibility.");
|
||||
|
||||
is(tabpanel.querySelector("#response-content-image-box")
|
||||
.hasAttribute("hidden"), true,
|
||||
"The response content image box doesn't have the intended visibility.");
|
||||
|
||||
is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
|
||||
"There should be 1 json scope displayed in this tabpanel.");
|
||||
is(tabpanel.querySelectorAll(".variables-view-property").length, 6057,
|
||||
"There should be 6057 json properties displayed in this tabpanel.");
|
||||
is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
|
||||
"The empty notice should not be displayed in this tabpanel.");
|
||||
|
||||
let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
|
||||
let names = ".variables-view-property .name";
|
||||
let values = ".variables-view-property .value";
|
||||
|
||||
is(jsonScope.querySelector(".name").getAttribute("value"),
|
||||
"JSON", "The json scope doesn't have the correct title.");
|
||||
|
||||
is(jsonScope.querySelectorAll(names)[0].getAttribute("value"),
|
||||
"0", "The first json property name was incorrect.");
|
||||
is(jsonScope.querySelectorAll(values)[0].getAttribute("value"),
|
||||
"[object Object]", "The first json property value was incorrect.");
|
||||
|
||||
is(jsonScope.querySelectorAll(names)[1].getAttribute("value"),
|
||||
"greeting", "The second json property name was incorrect.");
|
||||
is(jsonScope.querySelectorAll(values)[1].getAttribute("value"),
|
||||
"\"Hello long string JSON!\"", "The second json property value was incorrect.");
|
||||
|
||||
is(Array.slice(jsonScope.querySelectorAll(names), -1).shift().getAttribute("value"),
|
||||
"__proto__", "The last json property name was incorrect.");
|
||||
is(Array.slice(jsonScope.querySelectorAll(values), -1).shift().getAttribute("value"),
|
||||
"[object Object]", "The last json property value was incorrect.");
|
||||
}
|
||||
});
|
||||
|
||||
aDebuggee.performRequests();
|
||||
});
|
||||
}
|
@ -30,7 +30,7 @@ function test() {
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll("#details-pane tab")[3]);
|
||||
|
||||
testResponseTab("jsonp");
|
||||
testResponseTab();
|
||||
teardown(aMonitor).then(finish);
|
||||
|
||||
function testResponseTab() {
|
||||
|
@ -17,6 +17,7 @@ const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
|
||||
const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
|
||||
const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html";
|
||||
const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
|
||||
const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
|
||||
|
||||
const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";
|
||||
const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs";
|
||||
|
@ -0,0 +1,33 @@
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Network Monitor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>JSON long string test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
function get(aAddress, aCallback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", aAddress, true);
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState == this.DONE) {
|
||||
aCallback();
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
function performRequests() {
|
||||
get("sjs_content-type-test-server.sjs?fmt=json-long", function() {
|
||||
// Done.
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -11,43 +11,57 @@ function handleRequest(request, response) {
|
||||
|
||||
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => {
|
||||
switch (format) {
|
||||
case "xml":
|
||||
case "xml": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/xml; charset=utf-8", false);
|
||||
response.write("<label value='greeting'>Hello XML!</label>");
|
||||
response.finish();
|
||||
break;
|
||||
case "css":
|
||||
}
|
||||
case "css": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/css; charset=utf-8", false);
|
||||
response.write("body:pre { content: 'Hello CSS!' }");
|
||||
response.finish();
|
||||
break;
|
||||
case "js":
|
||||
}
|
||||
case "js": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "application/javascript; charset=utf-8", false);
|
||||
response.write("function() { return 'Hello JS!'; }");
|
||||
response.finish();
|
||||
break;
|
||||
case "json":
|
||||
}
|
||||
case "json": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "application/json; charset=utf-8", false);
|
||||
response.write("{ \"greeting\": \"Hello JSON!\" }");
|
||||
response.finish();
|
||||
break;
|
||||
case "jsonp":
|
||||
}
|
||||
case "jsonp": {
|
||||
let fun = params.filter((s) => s.contains("jsonp="))[0].split("=")[1];
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/json; charset=utf-8", false);
|
||||
response.write(fun + "({ \"greeting\": \"Hello JSONP!\" })");
|
||||
response.finish();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
case "json-long": {
|
||||
let str = "{ \"greeting\": \"Hello long string JSON!\" },";
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/json; charset=utf-8", false);
|
||||
response.write("[" + new Array(2048).join(str).slice(0, -1) + "]");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
response.setStatusLine(request.httpVersion, 404, "Not Found");
|
||||
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
|
||||
response.write("<blink>Not Found</blink>");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, 10, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms.
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user