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 Tests RequestJSONView ability to parse JSON passed in XHR, JSONP
Bug 65559 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 @@ ...@@ -6,43 +6,27 @@
function test() function test()
{ {
var testData; function check(text) {
InspectorTest.addResult("");
testData = "while(1);"; InspectorTest.addResult("Input: " + text);
InspectorTest.assertTrue(!WebInspector.RequestJSONView.parseJSON(testData), "Should not be able to parse \"" + testData + "\"."); var parsedJSON = WebInspector.RequestJSONView.parseJSON(text);
if (!parsedJSON) {
testData = "{\"name\": \"value\""; InspectorTest.addResult("Can't parse");
InspectorTest.assertTrue(!WebInspector.RequestJSONView.parseJSON(testData), "Should not be able to parse \"" + testData + "\"."); return;
}
testData = "{\"name\": \"value\"}"; InspectorTest.addResult("Prefix: " + parsedJSON.prefix);
var parsedJSON = WebInspector.RequestJSONView.parseJSON(testData); InspectorTest.addResult("Data: " + JSON.stringify(parsedJSON.data));
InspectorTest.assertEquals(parsedJSON.prefix, ""); InspectorTest.addResult("Suffix: " + parsedJSON.suffix);
InspectorTest.assertEquals(parsedJSON.data.name, "value"); }
InspectorTest.assertEquals(parsedJSON.suffix, "");
check("{\"name\": \"value\"}");
testData = "while(1); {\"name\": \"value\"}"; check("while(1); {\"name\": \"value\"}");
parsedJSON = WebInspector.RequestJSONView.parseJSON(testData); check("[,\"foo\", -4.2, true, false, null]");
InspectorTest.assertEquals(parsedJSON.prefix, "while(1); "); check("[{\"foo\": {}, \"bar\": []},[[],{}]]");
InspectorTest.assertEquals(parsedJSON.data.name, "value"); check("/* vanilla */ run([1, 2, 3]);");
InspectorTest.assertEquals(parsedJSON.suffix, ""); check("[\"A\\\"B\\u0020C\\nD\\\\E\\u04ABF\"]");
testData = "func({)"; check("<html>404 Page not found</html>");
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, ")");
InspectorTest.completeTest(); InspectorTest.completeTest();
} }
......
...@@ -41,51 +41,139 @@ WebInspector.RequestJSONView = function(request, parsedJSON) ...@@ -41,51 +41,139 @@ WebInspector.RequestJSONView = function(request, parsedJSON)
this.element.classList.add("json"); 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 * @param {string} full
* @return {?WebInspector.ParsedJSON} * @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); * @param {string} text
if (start && start.index) { * @return {string}
prefix = text.substring(0, start.index); */
text = text.substring(start.index); 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, ""); * @return {*}
} catch (e) { */
return null; 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 * @param {string} text
* @return {?WebInspector.ParsedJSON} * @return {?WebInspector.ParsedJSON}
*/ */
WebInspector.RequestJSONView.parseJSONP = function(text) WebInspector.RequestJSONView.parseJSON = function(text)
{ {
// Taking everything between first and last parentheses // Trim stubs like "while(1)", "for(;;)", weird numbers, etc. We need JSON start.
var start = text.indexOf("("); var inner = WebInspector.RequestJSONView._findBrackets(text, "{", "}");
var end = text.lastIndexOf(")"); var inner2 = WebInspector.RequestJSONView._findBrackets(text, "[", "]");
if (start == -1 || end == -1 || end < start) 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; return null;
var prefix = text.substring(0, start + 1);
var suffix = text.substring(end); var prefix = text.substring(0, inner.start);
text = text.substring(start + 1, end); var suffix = text.substring(inner.end + 1);
text = text.substring(inner.start, inner.end + 1);
try { try {
return new WebInspector.ParsedJSON(JSON.parse(text), prefix, suffix); return new WebInspector.ParsedJSON(WebInspector.RequestJSONView._buildObjectFromJSON(text), prefix, suffix);
} catch (e) { } catch (e) {
return null; 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 = { WebInspector.RequestJSONView.prototype = {
wasShown: function() wasShown: function()
{ {
......
...@@ -81,7 +81,10 @@ WebInspector.RequestPreviewView.prototype = { ...@@ -81,7 +81,10 @@ WebInspector.RequestPreviewView.prototype = {
*/ */
_jsonView: function() _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; 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