Bug 964977 - Infer JSON from response, r=rcampbell

This commit is contained in:
Victor Porof 2014-02-03 22:32:59 +02:00
parent d706d2b8ec
commit 74859948e9
6 changed files with 150 additions and 9 deletions

View File

@ -2126,19 +2126,33 @@ NetworkDetailsView.prototype = {
// Handle json, which we tentatively identify by checking the MIME type
// for "json" after any word boundary. This works for the standard
// "application/json", and also for custom types like "x-bigcorp-json".
// This should be marginally more reliable than just looking for "json".
if (/\bjson/.test(mimeType)) {
let jsonpRegex = /^[a-zA-Z0-9_$]+\(|\)$/g; // JSONP with callback.
// Additionally, we also directly parse the response text content to
// verify whether it's json or not, to handle responses incorrectly
// labeled as text/plain instead.
let jsonMimeType, jsonObject, jsonObjectParseError;
try {
// Test the mime type *and* parse the string, because "JSONP" responses
// (json with callback) aren't actually valid json.
jsonMimeType = /\bjson/.test(mimeType);
jsonObject = JSON.parse(aString);
} catch (e) {
jsonObjectParseError = e;
}
if (jsonMimeType || jsonObject) {
// Extract the actual json substring in case this might be a "JSONP".
let jsonpRegex = /^[a-zA-Z0-9_$]+\(|\)$/g;
let sanitizedJSON = aString.replace(jsonpRegex, "");
let callbackPadding = aString.match(jsonpRegex);
// Make sure this is a valid JSON object first. If so, nicely display
// the parsing results in a variables view. Otherwise, simply show
// the contents as plain text.
try {
var jsonObject = JSON.parse(sanitizedJSON);
} catch (e) {
var parsingError = e;
if (sanitizedJSON != aString) {
try {
jsonObject = JSON.parse(sanitizedJSON);
} catch (e) {
jsonObjectParseError = e;
}
}
// Valid JSON.
@ -2157,8 +2171,8 @@ NetworkDetailsView.prototype = {
else {
$("#response-content-textarea-box").hidden = false;
let infoHeader = $("#response-content-info-header");
infoHeader.setAttribute("value", parsingError);
infoHeader.setAttribute("tooltiptext", parsingError);
infoHeader.setAttribute("value", jsonObjectParseError);
infoHeader.setAttribute("tooltiptext", jsonObjectParseError);
infoHeader.hidden = false;
return NetMonitorView.editor("#response-content-textarea").then(aEditor => {
aEditor.setMode(Editor.modes.js);

View File

@ -10,6 +10,7 @@ support-files =
html_json-custom-mime-test-page.html
html_json-long-test-page.html
html_json-malformed-test-page.html
html_json-text-mime-test-page.html
html_jsonp-test-page.html
html_navigate-test-page.html
html_post-data-test-page.html
@ -46,6 +47,7 @@ support-files =
[browser_net_json-long.js]
[browser_net_json-malformed.js]
[browser_net_json_custom_mime.js]
[browser_net_json_text_mime.js]
[browser_net_jsonp.js]
[browser_net_large-response.js]
[browser_net_open_request_in_tab.js]

View File

@ -0,0 +1,81 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if JSON responses with unusal/custom MIME types are handled correctly.
*/
function test() {
initNetMonitor(JSON_TEXT_MIME_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { document, L10N, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
waitForNetworkEvents(aMonitor, 1).then(() => {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
"GET", CONTENT_TYPE_SJS + "?fmt=json-text-mime", {
status: 200,
statusText: "OK",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
time: true
});
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[3]);
let RESPONSE_BODY_DISPLAYED = aMonitor.panelWin.EVENTS.RESPONSE_BODY_DISPLAYED;
waitFor(aMonitor.panelWin, RESPONSE_BODY_DISPLAYED)
.then(testResponseTab)
.then(() => 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-info-header")
.hasAttribute("hidden"), true,
"The response info header doesn't have the intended visibility.");
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, 2,
"There should be 2 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];
is(jsonScope.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
"greeting", "The first json property name was incorrect.");
is(jsonScope.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
"\"Hello third-party JSON!\"", "The first json property value was incorrect.");
is(jsonScope.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
"__proto__", "The second json property name was incorrect.");
is(jsonScope.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
"Object", "The second json property value was incorrect.");
}
});
aDebuggee.performRequests();
});
}

View File

@ -26,6 +26,7 @@ const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html";
const JSON_CUSTOM_MIME_URL = EXAMPLE_URL + "html_json-custom-mime-test-page.html";
const JSON_TEXT_MIME_URL = EXAMPLE_URL + "html_json-text-mime-test-page.html";
const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html";
const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html";
const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html";

View File

@ -0,0 +1,35 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Network Monitor test page</title>
</head>
<body>
<p>JSON text 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-text-mime", function() {
// Done.
});
}
</script>
</body>
</html>

View File

@ -112,6 +112,14 @@ function handleRequest(request, response) {
response.finish();
break;
}
case "json-text-mime": {
response.setStatusLine(request.httpVersion, status, "OK");
response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
maybeMakeCached();
response.write("{ \"greeting\": \"Hello third-party JSON!\" }");
response.finish();
break;
}
case "json-custom-mime": {
response.setStatusLine(request.httpVersion, status, "OK");
response.setHeader("Content-Type", "text/x-bigcorp-json; charset=utf-8", false);