From 71b2ac524e49c0d1cc4e5d7bd8fd9d5a1317a20b Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 4 Nov 2013 23:36:15 -0500 Subject: [PATCH] Fix #14492: More correct jQuery.parseJSON. Close gh-1419. (cherry picked from commit 60a6178131afec97b68c9a45bc24459f7b8bd905) Conflicts: src/ajax/parseJSON.js --- src/ajax/parseJSON.js | 6 +++- test/jquery.js | 3 +- test/unit/core.js | 75 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/ajax/parseJSON.js b/src/ajax/parseJSON.js index e8c449bca..3a96d15b9 100644 --- a/src/ajax/parseJSON.js +++ b/src/ajax/parseJSON.js @@ -2,7 +2,11 @@ define([ "../core" ], function( jQuery ) { -jQuery.parseJSON = JSON.parse; +// Support: Android 2.3 +// Workaround failure to string-cast null input +jQuery.parseJSON = function( data ) { + return JSON.parse( data + "" ); +}; return jQuery.parseJSON; diff --git a/test/jquery.js b/test/jquery.js index bdee83c28..e94f55fe8 100644 --- a/test/jquery.js +++ b/test/jquery.js @@ -11,12 +11,13 @@ QUnit.config.urlConfig.push({ id: "basic", label: "Bypass optimizations", - tooltip: "Force use of the most basic code by disabling native querySelectorAll; contains; compareDocumentPosition" + tooltip: "Force use of the most basic code by disabling native querySelectorAll; contains; compareDocumentPosition; JSON.parse" }); if ( QUnit.urlParams.basic ) { document.querySelectorAll = null; document.documentElement.contains = null; document.documentElement.compareDocumentPosition = null; + window.JSON = null; } // iFrames won't load AMD (the iframe tests synchronously expect jQuery to be there) diff --git a/test/unit/core.js b/test/unit/core.js index 448f39b8b..3f048e627 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -1360,13 +1360,35 @@ test("jQuery.parseHTML", function() { ok( jQuery.parseHTML("<#if>

This is a test.

<#/if>") || true, "Garbage input should not cause error" ); }); -test("jQuery.parseJSON", function(){ - expect( 9 ); +test("jQuery.parseJSON", function() { + expect( 20 ); + + strictEqual( jQuery.parseJSON( null ), null, "primitive null" ); + strictEqual( jQuery.parseJSON("0.88"), 0.88, "Number" ); + strictEqual( + jQuery.parseJSON("\" \\\" \\\\ \\/ \\b \\f \\n \\r \\t \\u007E \\u263a \""), + " \" \\ / \b \f \n \r \t ~ \u263A ", + "String escapes" + ); + deepEqual( jQuery.parseJSON("{}"), {}, "Empty object" ); + deepEqual( jQuery.parseJSON("{\"test\":1}"), { "test": 1 }, "Plain object" ); + deepEqual( jQuery.parseJSON("[0]"), [ 0 ], "Simple array" ); + + deepEqual( + jQuery.parseJSON("[ \"string\", -4.2, 2.7180e0, 3.14E-1, {}, [], true, false, null ]"), + [ "string", -4.2, 2.718, 0.314, {}, [], true, false, null ], + "Array of all data types" + ); + deepEqual( + jQuery.parseJSON( "{ \"string\": \"\", \"number\": 4.2e+1, \"object\": {}," + + "\"array\": [[]], \"boolean\": [ true, false ], \"null\": null }"), + { string: "", number: 42, object: {}, array: [[]], boolean: [ true, false ], "null": null }, + "Dictionary of all data types" + ); + + deepEqual( jQuery.parseJSON("\n{\"test\":1}\t"), { "test": 1 }, + "Leading and trailing whitespace are ignored" ); - equal( jQuery.parseJSON( null ), null, "Actual null returns null" ); - equal( jQuery.isEmptyObject( jQuery.parseJSON("{}") ), true, "Empty object returns empty object" ); - deepEqual( jQuery.parseJSON("{\"test\":1}"), { "test": 1 }, "Plain object parses" ); - deepEqual( jQuery.parseJSON("\n{\"test\":1}"), { "test": 1 }, "Leading whitespaces are ignored." ); raises(function() { jQuery.parseJSON(); }, null, "Undefined raises an error" ); @@ -1376,12 +1398,53 @@ test("jQuery.parseJSON", function(){ raises(function() { jQuery.parseJSON("''"); }, null, "Single-quoted string raises an error" ); + /* + + // Broken on IE8 + raises(function() { + jQuery.parseJSON("\" \\a \""); + }, null, "Invalid string escape raises an error" ); + + // Broken on IE8, Safari 5.1 Windows + raises(function() { + jQuery.parseJSON("\"\t\""); + }, null, "Unescaped control character raises an error" ); + + // Broken on IE8 + raises(function() { + jQuery.parseJSON(".123"); + }, null, "Number with no integer component raises an error" ); + + */ + raises(function() { + var result = jQuery.parseJSON("0101"); + + // Support: IE9+ + // Ensure base-10 interpretation on browsers that erroneously accept leading-zero numbers + if ( result === 101 ) { + throw new Error("close enough"); + } + }, null, "Leading-zero number raises an error or is parsed as decimal" ); raises(function() { jQuery.parseJSON("{a:1}"); }, null, "Unquoted property raises an error" ); raises(function() { jQuery.parseJSON("{'a':1}"); }, null, "Single-quoted property raises an error" ); + raises(function() { + jQuery.parseJSON("[,]"); + }, null, "Array element elision raises an error" ); + raises(function() { + jQuery.parseJSON("{},[]"); + }, null, "Comma expression raises an error" ); + raises(function() { + jQuery.parseJSON("[]\n,{}"); + }, null, "Newline-containing comma expression raises an error" ); + raises(function() { + jQuery.parseJSON("\"\"\n\"\""); + }, null, "Automatic semicolon insertion raises an error" ); + + strictEqual( jQuery.parseJSON([ 0 ]), 0, "Input cast to string" ); }); test("jQuery.parseXML", 8, function(){ -- 2.39.5