DevTools: Fix console not showing array items inherited from prototype.

BUG=320632
R=pfeldman

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

git-svn-id: svn://svn.chromium.org/blink/trunk@188228 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/LayoutTests/inspector/console/console-format-array-prototype-expected.txt b/LayoutTests/inspector/console/console-format-array-prototype-expected.txt
new file mode 100644
index 0000000..13f327b
--- /dev/null
+++ b/LayoutTests/inspector/console/console-format-array-prototype-expected.txt
@@ -0,0 +1,43 @@
+CONSOLE MESSAGE: line 9: 
+CONSOLE MESSAGE: line 9: arr0
+CONSOLE MESSAGE: line 9: arr0,,,,
+CONSOLE MESSAGE: line 9: arr0,2,3
+CONSOLE MESSAGE: line 9: arr0,,,,,obj5,,,,,arr10,,,,
+CONSOLE MESSAGE: line 9: arr0,,,,,obj5,,,8,,arr10,,,,
+CONSOLE MESSAGE: line 9: 0,,,,,obj5,,,,,10,,,,
+CONSOLE MESSAGE: line 9: arr0,,,4,,obj5,,,,,arr10,,,,
+CONSOLE MESSAGE: line 9: 0,1,2,3,4,5,6,7,8,9
+CONSOLE MESSAGE: line 9: arr0,1,2,3,4,obj5,6,7,8,9,arr10
+Tests that console logging dumps array values defined on Array.prototype[].
+
+a0
+[]
+console-format-array-prototype.html:9 [0: "arr0", 10: "arr10", 5: "obj5"]
+a1
+["arr0"]
+console-format-array-prototype.html:9 ["arr0", 10: "arr10", 5: "obj5"]
+a2
+["arr0", undefined × 4]
+console-format-array-prototype.html:9 ["arr0", 10: "arr10", 5: "obj5"]
+a3
+["arr0", 2, 3]
+console-format-array-prototype.html:9 ["arr0", 2, 3, 10: "arr10", 5: "obj5"]
+a4
+["arr0", undefined × 4, "obj5", undefined × 4, "arr10", undefined × 4]
+console-format-array-prototype.html:9 ["arr0", 5: "obj5", 10: "arr10"]
+a5
+["arr0", undefined × 4, "obj5", undefined × 2, 8, undefined × 1, "arr10", undefined × 4]
+console-format-array-prototype.html:9 ["arr0", 5: "obj5", 8: 8, 10: "arr10"]
+a6
+[0, undefined × 4, "obj5", undefined × 4, 10, undefined × 4]
+console-format-array-prototype.html:9 [0, 5: "obj5", 10: 10]
+a7
+["arr0", undefined × 2, 4, undefined × 1, "obj5", undefined × 4, "arr10", undefined × 4]
+console-format-array-prototype.html:9 [3: 4, index0: 0, index1: 1, index2: 2, index3: 3, index4: 4…]
+a8
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+console-format-array-prototype.html:9 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10: "arr10"]
+a9
+["arr0", 1, 2, 3, 4, "obj5", 6, 7, 8, 9, "arr10"]
+console-format-array-prototype.html:9 ["arr0", 1, 2, 3, 4, "obj5", 6, 7, 8, 9, "arr10", foo: "bar"]
+
diff --git a/LayoutTests/inspector/console/console-format-array-prototype.html b/LayoutTests/inspector/console/console-format-array-prototype.html
new file mode 100644
index 0000000..c997f25
--- /dev/null
+++ b/LayoutTests/inspector/console/console-format-array-prototype.html
@@ -0,0 +1,90 @@
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/console-test.js"></script>
+<script>
+
+function log(data)
+{
+    console.log(data);
+}
+
+Array.prototype[0] = "arr0";
+Array.prototype[10] = "arr10";
+Object.defineProperty(Object.prototype, "5", {
+    value: "obj5",
+    configurable: true,
+    writable: true,
+    enumerable: false
+});
+
+function tearDown()
+{
+    delete Array.prototype[0];
+    delete Array.prototype[10];
+    delete Object.prototype[5];
+}
+
+var a0 = [];
+var a1 = []; a1.length = 1;
+var a2 = []; a2.length = 5;
+var a3 = [,2,3];
+var a4 = []; a4.length = 15;
+var a5 = []; a5.length = 15; a5[8] = 8;
+var a6 = []; a6.length = 15; a6[0] = 0; a6[10] = 10;
+var a7 = [,,,4]; a7.length = 15;
+for (var i = 0; i < 6; ++i)
+    a7["index" + i] = i;
+var a8 = [];
+for (var i = 0; i < 10; ++i)
+    a8[i] = i;
+var a9 = [];
+for (var i = 1; i < 5; ++i) {
+    a9[i] = i;
+    a9[i + 5] = i + 5;
+}
+a9.length = 11;
+a9.foo = "bar";
+
+function test()
+{
+    loopOverGlobals(0, 10);
+
+    function loopOverGlobals(current, total)
+    {
+        function advance()
+        {
+            var next = current + 1;
+            if (next === total) {
+                InspectorTest.evaluateInPage("tearDown()");
+                InspectorTest.expandConsoleMessages();
+                InspectorTest.runAfterPendingDispatches(finish);
+            } else {
+                loopOverGlobals(next, total);
+            }
+        }
+
+        function finish()
+        {
+            InspectorTest.dumpConsoleMessages(false, false, InspectorTest.textContentWithLineBreaks);
+            InspectorTest.completeTest();
+        }
+
+        InspectorTest.evaluateInConsole("a" + current);
+        InspectorTest.runAfterPendingDispatches(invokeConsoleLog);
+        function invokeConsoleLog()
+        {
+            InspectorTest.evaluateInPage("log(a" + current + ")");
+            InspectorTest.runAfterPendingDispatches(advance);
+        }
+    }
+}
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests that console logging dumps array values defined on Array.prototype[].
+</p>
+</body>
+</html>
diff --git a/Source/core/inspector/InjectedScriptSource.js b/Source/core/inspector/InjectedScriptSource.js
index c03173c..00480ed 100644
--- a/Source/core/inspector/InjectedScriptSource.js
+++ b/Source/core/inspector/InjectedScriptSource.js
@@ -149,12 +149,14 @@
 }
 
 /**
- * @param {*} obj
+ * @param {number|string} obj
  * @return {boolean}
  */
 function isUInt32(obj)
 {
-    return typeof obj === "number" && obj >>> 0 === obj && (obj > 0 || 1 / obj > 0);
+    if (typeof obj === "number")
+        return obj >>> 0 === obj && (obj > 0 || 1 / obj > 0);
+    return "" + (obj >>> 0) === obj;
 }
 
 /**
@@ -167,8 +169,10 @@
     if (typeof obj !== "object")
         return false;
     try {
-        if (typeof obj.splice === "function")
-            return isUInt32(obj.length);
+        if (typeof obj.splice === "function") {
+            var len = obj.length;
+            return typeof len === "number" && isUInt32(len);
+        }
     } catch (e) {
     }
     return false;
@@ -1362,14 +1366,14 @@
                 preview.lossless = false;
                 continue;
             }
-            if (!descriptor.enumerable && !descriptor.isOwn)
-                continue;
 
             var name = descriptor.name;
             if (name === "__proto__")
                 continue;
             if (this.subtype === "array" && name === "length")
                 continue;
+            if (!descriptor.enumerable && !descriptor.isOwn && !(this.subtype === "array" && isUInt32(name)))
+                continue;
 
             if (!("value" in descriptor)) {
                 preview.lossless = false;
diff --git a/Source/devtools/front_end/console/ConsoleViewMessage.js b/Source/devtools/front_end/console/ConsoleViewMessage.js
index 97dd5b2..809a405 100644
--- a/Source/devtools/front_end/console/ConsoleViewMessage.js
+++ b/Source/devtools/front_end/console/ConsoleViewMessage.js
@@ -517,14 +517,44 @@
     _appendPropertiesPreview: function(parentElement, preview, object)
     {
         var isArray = preview.subtype === "array";
+        var arrayLength = WebInspector.RemoteObject.arrayLength(preview);
+        var properties = preview.properties;
+        if (isArray)
+            properties = properties.slice().stableSort(compareIndexesFirst);
+
+        /**
+         * @param {!RuntimeAgent.PropertyPreview} a
+         * @param {!RuntimeAgent.PropertyPreview} b
+         */
+        function compareIndexesFirst(a, b)
+        {
+            var index1 = toArrayIndex(a.name);
+            var index2 = toArrayIndex(b.name);
+            if (index1 < 0)
+                return index2 < 0 ? 0 : 1;
+            return index2 < 0 ? -1 : index1 - index2;
+        }
+
+        /**
+         * @param {string} name
+         * @return {number}
+         */
+        function toArrayIndex(name)
+        {
+            var index = name >>> 0;
+            if (String(index) === name && index < arrayLength)
+                return index;
+            return -1;
+        }
+
         parentElement.createTextChild(isArray ? "[" : "{");
-        for (var i = 0; i < preview.properties.length; ++i) {
+        for (var i = 0; i < properties.length; ++i) {
             if (i > 0)
                 parentElement.createTextChild(", ");
 
-            var property = preview.properties[i];
+            var property = properties[i];
             var name = property.name;
-            if (!isArray || name != i) {
+            if (!isArray || name != i || i >= arrayLength) {
                 if (/^\s|\s$|^$|\n/.test(name))
                     parentElement.createChild("span", "name").createTextChildren("\"", name.replace(/\n/g, "\u21B5"), "\"");
                 else
@@ -654,11 +684,11 @@
             return;
         }
 
-        const maxFlatArrayLength = 100;
+        var maxFlatArrayLength = 100;
         if (this._message.isOutdated || array.arrayLength() > maxFlatArrayLength)
             this._formatParameterAsObject(array, elem, false);
         else
-            array.getOwnProperties(this._printArray.bind(this, array, elem));
+            array.getAllProperties(false, this._printArray.bind(this, array, elem));
     },
 
     /**
@@ -750,8 +780,10 @@
      */
     _printArray: function(array, elem, properties)
     {
-        if (!properties)
+        if (!properties) {
+            this._formatParameterAsObject(array, elem, false);
             return;
+        }
 
         var elements = [];
         for (var i = 0; i < properties.length; ++i) {
diff --git a/Source/devtools/front_end/sdk/RemoteObject.js b/Source/devtools/front_end/sdk/RemoteObject.js
index f6bd216..5d2844d 100644
--- a/Source/devtools/front_end/sdk/RemoteObject.js
+++ b/Source/devtools/front_end/sdk/RemoteObject.js
@@ -190,6 +190,20 @@
 }
 
 /**
+ * @param {!WebInspector.RemoteObject|!RuntimeAgent.RemoteObject|!RuntimeAgent.ObjectPreview} object
+ * @return {number}
+ */
+WebInspector.RemoteObject.arrayLength = function(object)
+{
+    if (object.subtype !== "array")
+        return 0;
+    var matches = object.description.match(/\[([0-9]+)\]/);
+    if (!matches)
+        return 0;
+    return parseInt(matches[1], 10);
+}
+
+/**
  * @param {!RuntimeAgent.RemoteObject|!WebInspector.RemoteObject|number|string|boolean|undefined|null} object
  * @return {!RuntimeAgent.CallArgument}
  */
@@ -624,13 +638,7 @@
      */
     arrayLength: function()
     {
-        if (this.subtype !== "array")
-            return 0;
-
-        var matches = this._description.match(/\[([0-9]+)\]/);
-        if (!matches)
-            return 0;
-        return parseInt(matches[1], 10);
+        return WebInspector.RemoteObject.arrayLength(this);
     },
 
     /**