1
0
mirror of https://github.com/roytam1/UXP.git synced 2026-05-27 13:28:54 +00:00
Files
UXP/devtools/client/webconsole/net/utils/json.js
T

235 lines
6.0 KiB
JavaScript

/* 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";
// List of JSON content types.
const contentTypes = {
"text/plain": 1,
"text/javascript": 1,
"text/x-javascript": 1,
"text/json": 1,
"text/x-json": 1,
"application/json": 1,
"application/x-json": 1,
"application/javascript": 1,
"application/x-javascript": 1,
"application/json-rpc": 1
};
// Implementation
var Json = {};
/**
* Parsing JSON
*/
Json.parseJSONString = function (jsonString) {
if (!jsonString.length) {
return null;
}
let regex, matches;
let first = firstNonWs(jsonString);
if (first !== "[" && first !== "{") {
// This (probably) isn't pure JSON. Let's try to strip various sorts
// of XSSI protection/wrapping and see if that works better.
// Prototype-style secure requests
regex = /^\s*\/\*-secure-([\s\S]*)\*\/\s*$/;
matches = regex.exec(jsonString);
if (matches) {
jsonString = matches[1];
if (jsonString[0] === "\\" && jsonString[1] === "n") {
jsonString = jsonString.substr(2);
}
if (jsonString[jsonString.length - 2] === "\\" &&
jsonString[jsonString.length - 1] === "n") {
jsonString = jsonString.substr(0, jsonString.length - 2);
}
}
// Google-style (?) delimiters
if (jsonString.indexOf("&&&START&&&") !== -1) {
regex = /&&&START&&&([\s\S]*)&&&END&&&/;
matches = regex.exec(jsonString);
if (matches) {
jsonString = matches[1];
}
}
// while(1);, for(;;);, and )]}'
regex = /^\s*(\)\]\}[^\n]*\n|while\s*\(1\);|for\s*\(;;\);)([\s\S]*)/;
matches = regex.exec(jsonString);
if (matches) {
jsonString = matches[2];
}
// JSONP
regex = /^\s*([A-Za-z0-9_$.]+\s*(?:\[.*\]|))\s*\(([\s\S]*)\)/;
matches = regex.exec(jsonString);
if (matches) {
jsonString = matches[2];
}
}
try {
return JSON.parse(jsonString);
} catch (err) {
// eslint-disable-line no-empty
}
// Give up if we don't have valid start, to avoid some unnecessary overhead.
first = firstNonWs(jsonString);
if (first !== "[" && first !== "{" && isNaN(first) && first !== '"') {
return null;
}
// Remove JavaScript comments, quote non-quoted identifiers, and merge
// multi-line structures like |{"a": 1} \n {"b": 2}| into a single JSON
// object [{"a": 1}, {"b": 2}].
jsonString = pseudoJsonToJson(jsonString);
try {
return JSON.parse(jsonString);
} catch (err) {
// eslint-disable-line no-empty
}
return null;
};
function firstNonWs(str) {
for (let i = 0, len = str.length; i < len; i++) {
let ch = str[i];
if (ch !== " " && ch !== "\n" && ch !== "\t" && ch !== "\r") {
return ch;
}
}
return "";
}
function pseudoJsonToJson(json) {
let ret = "";
let at = 0, lasti = 0, lastch = "", hasMultipleParts = false;
for (let i = 0, len = json.length; i < len; ++i) {
let ch = json[i];
if (/\s/.test(ch)) {
continue;
}
if (ch === '"') {
// Consume a string.
++i;
while (i < len) {
if (json[i] === "\\") {
++i;
} else if (json[i] === '"') {
break;
}
++i;
}
} else if (ch === "'") {
// Convert an invalid string into a valid one.
ret += json.slice(at, i) + "\"";
at = i + 1;
++i;
while (i < len) {
if (json[i] === "\\") {
++i;
} else if (json[i] === "'") {
break;
}
++i;
}
if (i < len) {
ret += json.slice(at, i) + "\"";
at = i + 1;
}
} else if ((ch === "[" || ch === "{") &&
(lastch === "]" || lastch === "}")) {
// Multiple JSON messages in one... Make it into a single array by
// inserting a comma and setting the "multiple parts" flag.
ret += json.slice(at, i) + ",";
hasMultipleParts = true;
at = i;
} else if (lastch === "," && (ch === "]" || ch === "}")) {
// Trailing commas in arrays/objects.
ret += json.slice(at, lasti);
at = i;
} else if (lastch === "/" && lasti === i - 1) {
// Some kind of comment; remove it.
if (ch === "/") {
ret += json.slice(at, i - 1);
at = i + json.slice(i).search(/\n|\r|$/);
i = at - 1;
} else if (ch === "*") {
ret += json.slice(at, i - 1);
at = json.indexOf("*/", i + 1) + 2;
if (at === 1) {
at = len;
}
i = at - 1;
}
ch = "\0";
} else if (/[a-zA-Z$_]/.test(ch) && lastch !== ":") {
// Non-quoted identifier. Quote it.
ret += json.slice(at, i) + "\"";
at = i;
i = i + json.slice(i).search(/[^a-zA-Z0-9$_]|$/);
ret += json.slice(at, i) + "\"";
at = i;
}
lastch = ch;
lasti = i;
}
ret += json.slice(at);
if (hasMultipleParts) {
ret = "[" + ret + "]";
}
return ret;
}
Json.isJSON = function (contentType, data) {
// Workaround for JSON responses without proper content type
// Let's consider all responses starting with "{" as JSON. In the worst
// case there will be an exception when parsing. This means that no-JSON
// responses (and post data) (with "{") can be parsed unnecessarily,
// which represents a little overhead, but this happens only if the request
// is actually expanded by the user in the UI (Net & Console panels).
// Do a manual string search instead of checking (data.strip()[0] === "{")
// to improve performance/memory usage.
let len = data ? data.length : 0;
for (let i = 0; i < len; i++) {
let ch = data.charAt(i);
if (ch === "{") {
return true;
}
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
continue;
}
break;
}
if (!contentType) {
return false;
}
contentType = contentType.split(";")[0];
contentType = contentType.trim();
return !!contentTypes[contentType];
};
// Exports from this module
module.exports = Json;