Commit 7eed7d05 authored by eustas@chromium.org's avatar eustas@chromium.org

DevTools: NetworkPanel: use optimistic JSON parser for response preview.

There are some legal tricks used to encode JSON. But JSON.parse rejects
to parse it, whereas XHR response is correctly transformed to JSON.

BUG=406900

Review URL: https://codereview.chromium.org/654083006

git-svn-id: svn://svn.chromium.org/blink/trunk@183821 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent daeab551
Tests RequestJSONView ability to parse JSON passed in XHR, JSONP
Bug 65559
Input: {"name": "value"}
Prefix:
Data: {"name":"value"}
Suffix:
Input: while(1); {"name": "value"}
Prefix: while(1);
Data: {"name":"value"}
Suffix:
Input: [,"foo", -4.2, true, false, null]
Prefix:
Data: [null,"foo",-4.2,true,false,null]
Suffix:
Input: [{"foo": {}, "bar": []},[[],{}]]
Prefix:
Data: [{"foo":{},"bar":[]},[[],{}]]
Suffix:
Input: /* vanilla */ run([1, 2, 3]);
Prefix: /* vanilla */ run(
Data: [1,2,3]
Suffix: );
Input: ["A\"B\u0020C\nD\\E\u04ABF"]
Prefix:
Data: ["A\"B C\nD\\EҫF"]
Suffix:
Input: <html>404 Page not found</html>
Can't parse
......@@ -6,43 +6,27 @@
function test()
{
var testData;
testData = "while(1);";
InspectorTest.assertTrue(!WebInspector.RequestJSONView.parseJSON(testData), "Should not be able to parse \"" + testData + "\".");
testData = "{\"name\": \"value\"";
InspectorTest.assertTrue(!WebInspector.RequestJSONView.parseJSON(testData), "Should not be able to parse \"" + testData + "\".");
testData = "{\"name\": \"value\"}";
var parsedJSON = WebInspector.RequestJSONView.parseJSON(testData);
InspectorTest.assertEquals(parsedJSON.prefix, "");
InspectorTest.assertEquals(parsedJSON.data.name, "value");
InspectorTest.assertEquals(parsedJSON.suffix, "");
testData = "while(1); {\"name\": \"value\"}";
parsedJSON = WebInspector.RequestJSONView.parseJSON(testData);
InspectorTest.assertEquals(parsedJSON.prefix, "while(1); ");
InspectorTest.assertEquals(parsedJSON.data.name, "value");
InspectorTest.assertEquals(parsedJSON.suffix, "");
testData = "func({)";
InspectorTest.assertTrue(!WebInspector.RequestJSONView.parseJSONP(testData), "Should not be able to parse \"" + testData + "\".");
testData = "func){(";
InspectorTest.assertTrue(!WebInspector.RequestJSONView.parseJSONP(testData), "Should not be able to parse \"" + testData + "\".");
testData = "func({\"name\": \"value\"}";
InspectorTest.assertTrue(!WebInspector.RequestJSONView.parseJSONP(testData), "Should not be able to parse \"" + testData + "\".");
testData = "func{\"name\": \"value\"})";
InspectorTest.assertTrue(!WebInspector.RequestJSONView.parseJSONP(testData), "Should not be able to parse \"" + testData + "\".");
testData = "func({\"name\": \"value\"})";
var parsedJSONP = WebInspector.RequestJSONView.parseJSONP(testData);
InspectorTest.assertEquals(parsedJSONP.prefix, "func(");
InspectorTest.assertEquals(parsedJSONP.data.name, "value");
InspectorTest.assertEquals(parsedJSONP.suffix, ")");
function check(text) {
InspectorTest.addResult("");
InspectorTest.addResult("Input: " + text);
var parsedJSON = WebInspector.RequestJSONView.parseJSON(text);
if (!parsedJSON) {
InspectorTest.addResult("Can't parse");
return;
}
InspectorTest.addResult("Prefix: " + parsedJSON.prefix);
InspectorTest.addResult("Data: " + JSON.stringify(parsedJSON.data));
InspectorTest.addResult("Suffix: " + parsedJSON.suffix);
}
check("{\"name\": \"value\"}");
check("while(1); {\"name\": \"value\"}");
check("[,\"foo\", -4.2, true, false, null]");
check("[{\"foo\": {}, \"bar\": []},[[],{}]]");
check("/* vanilla */ run([1, 2, 3]);");
check("[\"A\\\"B\\u0020C\\nD\\\\E\\u04ABF\"]");
check("<html>404 Page not found</html>");
InspectorTest.completeTest();
}
......
......@@ -41,51 +41,139 @@ WebInspector.RequestJSONView = function(request, parsedJSON)
this.element.classList.add("json");
}
// "false", "true", "null", ",", "{", "}", "[", "]", number, double-quoted string.
WebInspector.RequestJSONView._jsonToken = new RegExp('(?:false|true|null|[,\\{\\}\\[\\]]|(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)|(?:\"(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))*\"))', 'g');
// Escaped unicode char.
WebInspector.RequestJSONView._escapedUnicode = new RegExp('\\\\(?:([^u])|u(.{4}))', 'g');
// Map from escaped char to its literal value.
WebInspector.RequestJSONView._standardEscapes = {'"': '"', '/': '/', '\\': '\\', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t'};
/**
* @param {string} text
* @return {?WebInspector.ParsedJSON}
* @param {string} full
* @param {string} standard
* @param {string} unicode
* @return {string}
*/
WebInspector.RequestJSONView.parseJSON = function(text)
WebInspector.RequestJSONView._unescape = function(full, standard, unicode)
{
var prefix = "";
return standard ? WebInspector.RequestJSONView._standardEscapes[standard] : String.fromCharCode(parseInt(unicode, 16));
}
// Trim while(1), for(;;), weird numbers, etc. We need JSON start.
var start = /[{[]/.exec(text);
if (start && start.index) {
prefix = text.substring(0, start.index);
text = text.substring(start.index);
}
/**
* @param {string} text
* @return {string}
*/
WebInspector.RequestJSONView._unescapeString = function(text)
{
return text.indexOf("\\") === -1 ? text : text.replace(WebInspector.RequestJSONView._escapedUnicode, WebInspector.RequestJSONView._unescape);
}
try {
return new WebInspector.ParsedJSON(JSON.parse(text), prefix, "");
} catch (e) {
return null;
/**
* @return {*}
*/
WebInspector.RequestJSONView._buildObjectFromJSON = function(text)
{
var regExp = WebInspector.RequestJSONView._jsonToken;
regExp.lastIndex = 0;
var result = [];
var tip = result;
var stack = [];
var key = undefined;
var token = undefined;
var lastToken = undefined;
while (true) {
var match = regExp.exec(text);
if (match === null)
break;
lastToken = token;
token = match[0];
var code = token.charCodeAt(0);
if ((code === 0x5b) || (code === 0x7b)) { // [ or {
var newTip = (code === 0x5b) ? [] : {};
tip[key || tip.length] = newTip;
stack.push(tip);
tip = newTip;
} else if ((code === 0x5d) || (code === 0x7d)) { // ] or }
tip = stack.pop();
if (!tip)
break;
} else if (code === 0x2C) { // ,
if ((tip instanceof Array) && (lastToken === undefined || lastToken === "[" || lastToken === ","))
tip[tip.length] = undefined;
} else if (code === 0x22) { // "
token = WebInspector.RequestJSONView._unescapeString(token.substring(1, token.length - 1));
if (!key) {
if (tip instanceof Array) {
key = tip.length;
} else {
key = token || "";
continue;
}
}
tip[key] = token;
} else if (code === 0x66) { // f
tip[key || tip.length] = false;
} else if (code === 0x6e) { // n
tip[key || tip.length] = null;
} else if (code === 0x74) { // t
tip[key || tip.length] = true;
} else { // sign or digit
tip[key || tip.length] = +(token);
}
key = undefined;
}
return (result.length > 1) ? result : result[0];
}
/**
* @param {string} text
* @return {?WebInspector.ParsedJSON}
*/
WebInspector.RequestJSONView.parseJSONP = function(text)
WebInspector.RequestJSONView.parseJSON = function(text)
{
// Taking everything between first and last parentheses
var start = text.indexOf("(");
var end = text.lastIndexOf(")");
if (start == -1 || end == -1 || end < start)
// Trim stubs like "while(1)", "for(;;)", weird numbers, etc. We need JSON start.
var inner = WebInspector.RequestJSONView._findBrackets(text, "{", "}");
var inner2 = WebInspector.RequestJSONView._findBrackets(text, "[", "]");
inner = inner2.length > inner.length ? inner2 : inner;
var inner3 = WebInspector.RequestJSONView._findBrackets(text, "(", ")");
if (inner3.length - 2 > inner.length) {
inner = inner3;
++inner.start;
--inner.end;
}
if (inner.length === -1)
return null;
var prefix = text.substring(0, start + 1);
var suffix = text.substring(end);
text = text.substring(start + 1, end);
var prefix = text.substring(0, inner.start);
var suffix = text.substring(inner.end + 1);
text = text.substring(inner.start, inner.end + 1);
try {
return new WebInspector.ParsedJSON(JSON.parse(text), prefix, suffix);
return new WebInspector.ParsedJSON(WebInspector.RequestJSONView._buildObjectFromJSON(text), prefix, suffix);
} catch (e) {
return null;
}
}
/**
* @param {string} text
* @param {string} open
* @param {string} close
* @return {{start: number, end: number, length: number}}
*/
WebInspector.RequestJSONView._findBrackets = function(text, open, close)
{
var start = text.indexOf(open);
var end = text.lastIndexOf(close);
var length = end - start - 1;
if (start == -1 || end == -1 || end < start)
length = -1;
return {start: start, end: end, length: length};
}
WebInspector.RequestJSONView.prototype = {
wasShown: function()
{
......
......@@ -81,7 +81,10 @@ WebInspector.RequestPreviewView.prototype = {
*/
_jsonView: function()
{
var parsedJSON = WebInspector.RequestJSONView.parseJSON(this.request.content || "");
var request = this.request;
var content = request.content;
content = request.contentEncoded ? window.atob(content || "") : (content || "");
var parsedJSON = WebInspector.RequestJSONView.parseJSON(content);
return parsedJSON ? new WebInspector.RequestJSONView(this.request, parsedJSON) : null;
},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment