Bug 1156720 - Firefox freezes when parsing large data in Storage Inspector. r=mratcliffe
authorJarda Snajdr <jsnajdr@gmail.com>
Thu, 21 Apr 2016 08:52:00 +0200
changeset 332344 80598be5d80e695727ff74e712faa623b02f3c70
parent 332343 0361b7030d363459416d46e651a8c50c9536788f
child 332345 6f83467f1a10cdee1f9b596e7b368b71cc601f2c
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmratcliffe
bugs1156720
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1156720 - Firefox freezes when parsing large data in Storage Inspector. r=mratcliffe MozReview-Commit-ID: CopXwMnhzxY
devtools/client/storage/test/browser_storage_values.js
devtools/client/storage/test/storage-complex-values.html
devtools/client/storage/ui.js
--- a/devtools/client/storage/test/browser_storage_values.js
+++ b/devtools/client/storage/test/browser_storage_values.js
@@ -9,16 +9,18 @@
 //     null do click nothing,
 //   null to skip checking value in variables view or a key value pair object
 //     which will be asserted to exist in the storage sidebar,
 //   true if the check is to be made in the parsed value section
 // ]
 
 "use strict";
 
+const LONG_WORD = "a".repeat(1000);
+
 const testCases = [
   ["cs2", [
     {name: "cs2", value: "sessionCookie"},
     {name: "cs2.path", value: "/"},
     {name: "cs2.isDomain", value: "true"},
     {name: "cs2.isHttpOnly", value: "false"},
     {name: "cs2.host", value: ".example.org"},
     {name: "cs2.expires", value: "Session"},
@@ -88,16 +90,33 @@ const testCases = [
     {name: "ss2.3", value: "array"},
   ], true],
   ["ss3", [
     {name: "ss3", value: "Object"},
     {name: "ss3.this", value: "is"},
     {name: "ss3.an", value: "object"},
     {name: "ss3.foo", value: "bar"},
   ], true],
+  ["ss4", [
+    {name: "ss4", value: "Array"},
+    {name: "ss4.0", value: ""},
+    {name: "ss4.1", value: "array"},
+    {name: "ss4.2", value: ""},
+    {name: "ss4.3", value: "with"},
+    {name: "ss4.4", value: "empty"},
+    {name: "ss4.5", value: "items"},
+  ], true],
+  ["ss5", [
+    {name: "ss5", value: "Array"},
+    {name: "ss5.0", value: LONG_WORD},
+    {name: "ss5.1", value: LONG_WORD},
+    {name: "ss5.2", value: LONG_WORD},
+    {name: "ss5.3", value: `${LONG_WORD}&${LONG_WORD}`},
+    {name: "ss5.4", value: `${LONG_WORD}&${LONG_WORD}`},
+  ], true],
   [["indexedDB", "http://test1.example.org", "idb1", "obj1"]],
   [1, [
     {name: 1, value: JSON.stringify({id: 1, name: "foo", email: "foo@bar.com"})}
   ]],
   [null, [
     {name: "1.id", value: "1"},
     {name: "1.name", value: "foo"},
     {name: "1.email", value: "foo@bar.com"},
--- a/devtools/client/storage/test/storage-complex-values.html
+++ b/devtools/client/storage/test/storage-complex-values.html
@@ -27,16 +27,21 @@ localStorage.setItem("ls1", JSON.stringi
     nobody: "cares"
   }]}));
 localStorage.setItem("ls2", "foobar-2");
 localStorage.setItem("ls3", "http://foobar.com/baz.php");
 // ... and finally some session storage items too
 sessionStorage.setItem("ss1", "This#is#an#array");
 sessionStorage.setItem("ss2", "This~is~another~array");
 sessionStorage.setItem("ss3", "this#is~an#object~foo#bar");
+sessionStorage.setItem("ss4", "#array##with#empty#items");
+// long string that is almost an object and might trigger exponential
+// regexp backtracking
+let s = "a".repeat(1000);
+sessionStorage.setItem("ss5", `${s}=${s}=${s}=${s}&${s}=${s}&${s}`);
 console.log("added cookies and stuff from main page");
 
 let idbGenerator = function*() {
   let request = indexedDB.open("idb1", 1);
   request.onerror = function() {
     throw new Error("error opening db connection");
   };
   let db = yield new Promise(done => {
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -602,27 +602,31 @@ StorageUI.prototype = {
     // Testing for object
     for (let i = 0; i < separators.length; i++) {
       let kv = separators[i];
       for (let j = 0; j < separators.length; j++) {
         if (i == j) {
           continue;
         }
         let p = separators[j];
-        let regex = new RegExp("^([^" + kv + p + "]*" + kv + "+[^" + kv + p +
-                               "]*" + p + "*)+$", "g");
+        let word = `[^${kv}${p}]*`;
+        let keyValue = `${word}${kv}${word}`;
+        let keyValueList = `${keyValue}(${p}${keyValue})*`;
+        let regex = new RegExp(`^${keyValueList}$`);
         if (value.match && value.match(regex) && value.includes(kv) &&
             (value.includes(p) || value.split(kv).length == 2)) {
           return makeObject(kv, p);
         }
       }
     }
     // Testing for array
     for (let p of separators) {
-      let regex = new RegExp("^[^" + p + "]+(" + p + "+[^" + p + "]*)+$", "g");
+      let word = `[^${p}]*`;
+      let wordList = `(${word}${p})+${word}`;
+      let regex = new RegExp(`^${wordList}$`);
       if (value.match && value.match(regex)) {
         return value.split(p.replace(/\\*/g, ""));
       }
     }
     return null;
   },
 
   /**