Web Inspector: Array/Collection Sizes should be visible and distinct
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Mar 2015 02:27:23 +0000 (02:27 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Mar 2015 02:27:23 +0000 (02:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142254

Patch by Joseph Pecoraro <pecoraro@apple.com> on 2015-03-04
Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* runtime/WeakMapData.h:
(JSC::WeakMapData::size):
* inspector/JSInjectedScriptHost.cpp:
(Inspector::JSInjectedScriptHost::weakMapSize):
* inspector/JSInjectedScriptHost.h:
* inspector/JSInjectedScriptHostPrototype.cpp:
(Inspector::JSInjectedScriptHostPrototype::finishCreation):
(Inspector::jsInjectedScriptHostPrototypeFunctionWeakMapSize):
Add a way to get a WeakMap's size.

* inspector/protocol/Runtime.json:
Include size in RemoteObject and ObjectPreview.

* inspector/InjectedScriptSource.js:
Set the size of RemoteObjects and previews if they
are array/collection types.

Source/WebInspectorUI:

* UserInterface/Models/ObjectPreview.js:
(WebInspector.ObjectPreview):
(WebInspector.ObjectPreview.fromPayload):
(WebInspector.ObjectPreview.prototype.get size):
(WebInspector.ObjectPreview.prototype.hasSize):
* UserInterface/Protocol/RemoteObject.js:
(WebInspector.RemoteObject):
(WebInspector.RemoteObject.fromPrimitiveValue):
(WebInspector.RemoteObject.fromPayload):
(WebInspector.RemoteObject.prototype.get size):
(WebInspector.RemoteObject.prototype.hasSize):
Check if this type has a size and get the size.
Gracefully handle construction for legacy protocols.

* UserInterface/Views/ObjectPreviewView.css:
(.object-preview > .size):
* UserInterface/Views/FormattedValue.css:
(:matches(.formatted-array, .formatted-map, .formatted-set, .formatted-weakmap) > .size):
Style the array/collection size.

* UserInterface/Views/ObjectPreviewView.js:
(WebInspector.ObjectPreviewView):
* UserInterface/Views/FormattedValue.js:
(WebInspector.FormattedValue.createElementForTypesAndValue):
(WebInspector.FormattedValue.createElementForRemoteObject):
(WebInspector.FormattedValue.createElementForObjectPreview):
(WebInspector.FormattedValue.createElementForPropertyPreview):
Add an element showing the array/collection size.

* UserInterface/Views/ObjectTreePropertyTreeElement.js:
(WebInspector.ObjectTreePropertyTreeElement.prototype):
Remove special handling for Array sizes now that this is handled earlier.

* UserInterface/Controllers/StorageManager.js:
(WebInspector.StorageManager.prototype.processData):
(WebInspector.StorageManager.prototype.requestIndexedDatabaseData):
Fix what looks like broken RemoteObject construction.

LayoutTests:

* inspector-protocol/runtime/getProperties-expected.txt:
* inspector/model/remote-object-expected.txt:
* inspector/model/remote-object.html:
Update tests now that RemoteObjects and Previews may have an explicit size.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@181061 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector-protocol/runtime/getProperties-expected.txt
LayoutTests/inspector/model/remote-object-expected.txt
LayoutTests/inspector/model/remote-object.html
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/InjectedScriptSource.js
Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
Source/JavaScriptCore/inspector/JSInjectedScriptHost.h
Source/JavaScriptCore/inspector/JSInjectedScriptHostPrototype.cpp
Source/JavaScriptCore/inspector/protocol/Runtime.json
Source/JavaScriptCore/runtime/WeakMapData.h
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Controllers/StorageManager.js
Source/WebInspectorUI/UserInterface/Models/ObjectPreview.js
Source/WebInspectorUI/UserInterface/Protocol/RemoteObject.js
Source/WebInspectorUI/UserInterface/Views/FormattedValue.css
Source/WebInspectorUI/UserInterface/Views/FormattedValue.js
Source/WebInspectorUI/UserInterface/Views/ObjectPreviewView.css
Source/WebInspectorUI/UserInterface/Views/ObjectPreviewView.js
Source/WebInspectorUI/UserInterface/Views/ObjectTreePropertyTreeElement.js

index 28c5dbd..f340f75 100644 (file)
@@ -1,3 +1,15 @@
+2015-03-04  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Array/Collection Sizes should be visible and distinct
+        https://bugs.webkit.org/show_bug.cgi?id=142254
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector-protocol/runtime/getProperties-expected.txt:
+        * inspector/model/remote-object-expected.txt:
+        * inspector/model/remote-object.html:
+        Update tests now that RemoteObjects and Previews may have an explicit size.
+
 2015-03-04  Timothy Horton  <timothy_horton@apple.com>
 
         <attachment> should show the file size as detail text below the icon
index 16d63f9..078d0da 100644 (file)
@@ -2,7 +2,7 @@ Properties of Object(5)
   __proto__ object Number
   foo string cat
 Properties of array
-  __proto__ object Array[0]
+  __proto__ object Array
   0 string red
   1 string green
   2 string blue
@@ -16,7 +16,7 @@ Properties of Bound function
   length number 0
   name string Number
 Internal properties
-  boundArgs object Array[1]
+  boundArgs object Array
   boundThis object Object
   targetFunction function function Number() {
     [native code]
index 1e98d4f..66589bd 100644 (file)
@@ -517,13 +517,15 @@ EXPRESSION: []
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[0]",
+  "_description": "Array",
+  "_size": 0,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[0]",
+    "_description": "Array",
     "_lossless": true,
     "_overflow": false,
+    "_size": 0,
     "_properties": [],
     "_entries": null
   }
@@ -535,13 +537,15 @@ EXPRESSION: [1, 2]
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[2]",
+  "_description": "Array",
+  "_size": 2,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[2]",
+    "_description": "Array",
     "_lossless": true,
     "_overflow": false,
+    "_size": 2,
     "_properties": [
       {
         "_name": "0",
@@ -564,31 +568,33 @@ EXPRESSION: [[1],[2],[3]]
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[3]",
+  "_description": "Array",
+  "_size": 3,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[3]",
+    "_description": "Array",
     "_lossless": false,
     "_overflow": false,
+    "_size": 3,
     "_properties": [
       {
         "_name": "0",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array[1]"
+        "_value": "Array"
       },
       {
         "_name": "1",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array[1]"
+        "_value": "Array"
       },
       {
         "_name": "2",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array[1]"
+        "_value": "Array"
       }
     ],
     "_entries": null
@@ -601,13 +607,15 @@ EXPRESSION: [true, 1, 1.234, 'string', /regex/]
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[5]",
+  "_description": "Array",
+  "_size": 5,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[5]",
+    "_description": "Array",
     "_lossless": false,
     "_overflow": false,
+    "_size": 5,
     "_properties": [
       {
         "_name": "0",
@@ -646,13 +654,15 @@ EXPRESSION: [{a:1}, {b:2}, {c:2}]
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[3]",
+  "_description": "Array",
+  "_size": 3,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[3]",
+    "_description": "Array",
     "_lossless": false,
     "_overflow": false,
+    "_size": 3,
     "_properties": [
       {
         "_name": "0",
@@ -680,19 +690,21 @@ EXPRESSION: [[{a:1}, {b:2}, {c:2}]]
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[1]",
+  "_description": "Array",
+  "_size": 1,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[1]",
+    "_description": "Array",
     "_lossless": false,
     "_overflow": false,
+    "_size": 1,
     "_properties": [
       {
         "_name": "0",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array[3]"
+        "_value": "Array"
       }
     ],
     "_entries": null
@@ -705,13 +717,15 @@ EXPRESSION: arr = []; arr.length = 100; arr
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[100]",
+  "_description": "Array",
+  "_size": 100,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[100]",
+    "_description": "Array",
     "_lossless": true,
     "_overflow": false,
+    "_size": 100,
     "_properties": [],
     "_entries": null
   }
@@ -723,13 +737,15 @@ EXPRESSION: arr = []; arr.length = 100; arr.fill(1)
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[100]",
+  "_description": "Array",
+  "_size": 100,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[100]",
+    "_description": "Array",
     "_lossless": true,
     "_overflow": false,
+    "_size": 100,
     "_properties": [
       {
         "_name": "0",
@@ -1242,13 +1258,15 @@ EXPRESSION: arr = []; arr.length = 100; arr[10] = 1; arr
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Array[100]",
+  "_description": "Array",
+  "_size": 100,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Array[100]",
+    "_description": "Array",
     "_lossless": true,
     "_overflow": false,
+    "_size": 100,
     "_properties": [
       {
         "_name": "10",
@@ -1266,13 +1284,15 @@ EXPRESSION: a = null; (function() { a = arguments; })(1, '2', /3/); a
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Arguments[3]",
+  "_description": "Arguments",
+  "_size": 3,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Arguments[3]",
+    "_description": "Arguments",
     "_lossless": false,
     "_overflow": false,
+    "_size": 3,
     "_properties": [
       {
         "_name": "0",
@@ -1301,13 +1321,15 @@ EXPRESSION: new Int32Array(new ArrayBuffer(16))
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Int32Array[4]",
+  "_description": "Int32Array",
+  "_size": 4,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Int32Array[4]",
+    "_description": "Int32Array",
     "_lossless": false,
     "_overflow": false,
+    "_size": 4,
     "_properties": [
       {
         "_name": "0",
@@ -1355,13 +1377,15 @@ EXPRESSION: var intArray = new Int32Array(new ArrayBuffer(16)); for (var i = 0;
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "Int32Array[4]",
+  "_description": "Int32Array",
+  "_size": 4,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "Int32Array[4]",
+    "_description": "Int32Array",
     "_lossless": false,
     "_overflow": false,
+    "_size": 4,
     "_properties": [
       {
         "_name": "0",
@@ -1894,13 +1918,15 @@ EXPRESSION: document.head.children
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "HTMLCollection[3]",
+  "_description": "HTMLCollection",
+  "_size": 3,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "HTMLCollection[3]",
+    "_description": "HTMLCollection",
     "_lossless": false,
     "_overflow": false,
+    "_size": 3,
     "_properties": [
       {
         "_name": "0",
@@ -1941,13 +1967,15 @@ EXPRESSION: document.getElementsByClassName('my-test')
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "NodeList[3]",
+  "_description": "NodeList",
+  "_size": 3,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "NodeList[3]",
+    "_description": "NodeList",
     "_lossless": false,
     "_overflow": false,
+    "_size": 3,
     "_properties": [
       {
         "_name": "0",
@@ -1988,13 +2016,15 @@ EXPRESSION: document.querySelectorAll('.my-test')
   "_type": "object",
   "_subtype": "array",
   "_objectId": "<filtered>",
-  "_description": "NodeList[3]",
+  "_description": "NodeList",
+  "_size": 3,
   "_preview": {
     "_type": "object",
     "_subtype": "array",
-    "_description": "NodeList[3]",
+    "_description": "NodeList",
     "_lossless": false,
     "_overflow": false,
+    "_size": 3,
     "_properties": [
       {
         "_name": "0",
@@ -2152,50 +2182,20 @@ EXPRESSION: error = null; try { document.createTextNode('').splitText(100); } ca
 }
 
 -----------------------------------------------------
-EXPRESSION: Object.seal({})
-{
-  "_type": "object",
-  "_objectId": "<filtered>",
-  "_description": "Object",
-  "_preview": {
-    "_type": "object",
-    "_description": "Object",
-    "_lossless": true,
-    "_overflow": false,
-    "_properties": [],
-    "_entries": null
-  }
-}
-
------------------------------------------------------
-EXPRESSION: Object.freeze({})
-{
-  "_type": "object",
-  "_objectId": "<filtered>",
-  "_description": "Object",
-  "_preview": {
-    "_type": "object",
-    "_description": "Object",
-    "_lossless": true,
-    "_overflow": false,
-    "_properties": [],
-    "_entries": null
-  }
-}
-
------------------------------------------------------
 EXPRESSION: new Map
 {
   "_type": "object",
   "_subtype": "map",
   "_objectId": "<filtered>",
   "_description": "Map",
+  "_size": 0,
   "_preview": {
     "_type": "object",
     "_subtype": "map",
     "_description": "Map",
     "_lossless": true,
     "_overflow": false,
+    "_size": 0,
     "_properties": [],
     "_entries": []
   }
@@ -2208,12 +2208,14 @@ EXPRESSION: map = new Map; map.set(1, 2); map.set('key', 'value'); map
   "_subtype": "map",
   "_objectId": "<filtered>",
   "_description": "Map",
+  "_size": 2,
   "_preview": {
     "_type": "object",
     "_subtype": "map",
     "_description": "Map",
     "_lossless": true,
     "_overflow": false,
+    "_size": 2,
     "_properties": [],
     "_entries": [
       {
@@ -2263,12 +2265,14 @@ EXPRESSION: map = new Map; map.set({a:1}, {b:2}); map.set(document.body, [1,2]);
   "_subtype": "map",
   "_objectId": "<filtered>",
   "_description": "Map",
+  "_size": 2,
   "_preview": {
     "_type": "object",
     "_subtype": "map",
     "_description": "Map",
     "_lossless": true,
     "_overflow": false,
+    "_size": 2,
     "_properties": [],
     "_entries": [
       {
@@ -2340,9 +2344,10 @@ EXPRESSION: map = new Map; map.set({a:1}, {b:2}); map.set(document.body, [1,2]);
         "_value": {
           "_type": "object",
           "_subtype": "array",
-          "_description": "Array[2]",
+          "_description": "Array",
           "_lossless": true,
           "_overflow": false,
+          "_size": 2,
           "_properties": [
             {
               "_name": "0",
@@ -2369,12 +2374,14 @@ EXPRESSION: map = new Map; for (var i = 0; i <= 100; i++) map.set(i, i); map
   "_subtype": "map",
   "_objectId": "<filtered>",
   "_description": "Map",
+  "_size": 101,
   "_preview": {
     "_type": "object",
     "_subtype": "map",
     "_description": "Map",
     "_lossless": false,
     "_overflow": true,
+    "_size": 101,
     "_properties": [],
     "_entries": [
       {
@@ -2478,12 +2485,14 @@ EXPRESSION: map = new WeakMap; strongKey = {id:1}; map.set(strongKey, [1,2]); ma
   "_subtype": "weakmap",
   "_objectId": "<filtered>",
   "_description": "WeakMap",
+  "_size": 1,
   "_preview": {
     "_type": "object",
     "_subtype": "weakmap",
     "_description": "WeakMap",
     "_lossless": true,
     "_overflow": false,
+    "_size": 1,
     "_properties": [],
     "_entries": [
       {
@@ -2504,9 +2513,10 @@ EXPRESSION: map = new WeakMap; strongKey = {id:1}; map.set(strongKey, [1,2]); ma
         "_value": {
           "_type": "object",
           "_subtype": "array",
-          "_description": "Array[2]",
+          "_description": "Array",
           "_lossless": true,
           "_overflow": false,
+          "_size": 2,
           "_properties": [
             {
               "_name": "0",
@@ -2533,12 +2543,14 @@ EXPRESSION: new Set
   "_subtype": "set",
   "_objectId": "<filtered>",
   "_description": "Set",
+  "_size": 0,
   "_preview": {
     "_type": "object",
     "_subtype": "set",
     "_description": "Set",
     "_lossless": true,
     "_overflow": false,
+    "_size": 0,
     "_properties": [],
     "_entries": []
   }
@@ -2551,12 +2563,14 @@ EXPRESSION: set = new Set; set.add(1); set.add(2); set.add('key'); set
   "_subtype": "set",
   "_objectId": "<filtered>",
   "_description": "Set",
+  "_size": 3,
   "_preview": {
     "_type": "object",
     "_subtype": "set",
     "_description": "Set",
     "_lossless": true,
     "_overflow": false,
+    "_size": 3,
     "_properties": [],
     "_entries": [
       {
@@ -2600,12 +2614,14 @@ EXPRESSION: set = new Set; set.add({a:1}); set.add(document.body); set.add([1,2]
   "_subtype": "set",
   "_objectId": "<filtered>",
   "_description": "Set",
+  "_size": 3,
   "_preview": {
     "_type": "object",
     "_subtype": "set",
     "_description": "Set",
     "_lossless": true,
     "_overflow": false,
+    "_size": 3,
     "_properties": [],
     "_entries": [
       {
@@ -2665,9 +2681,10 @@ EXPRESSION: set = new Set; set.add({a:1}); set.add(document.body); set.add([1,2]
         "_value": {
           "_type": "object",
           "_subtype": "array",
-          "_description": "Array[2]",
+          "_description": "Array",
           "_lossless": true,
           "_overflow": false,
+          "_size": 2,
           "_properties": [
             {
               "_name": "0",
@@ -2694,12 +2711,14 @@ EXPRESSION: set = new Set; for (var i = 0; i <= 100; i++) set.add(i); set
   "_subtype": "set",
   "_objectId": "<filtered>",
   "_description": "Set",
+  "_size": 101,
   "_preview": {
     "_type": "object",
     "_subtype": "set",
     "_description": "Set",
     "_lossless": false,
     "_overflow": true,
+    "_size": 101,
     "_properties": [],
     "_entries": [
       {
@@ -2895,3 +2914,35 @@ EXPRESSION: Promise.resolve({result:1})
   }
 }
 
+-----------------------------------------------------
+EXPRESSION: Object.seal({})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Object.freeze({})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
index bb6204d..ae0f52d 100644 (file)
@@ -116,12 +116,6 @@ function test()
         {expression: "error = null; try { eval('if()'); } catch (e) { error = e; }; error"},
         {expression: "error = null; try { document.createTextNode('').splitText(100); } catch (e) { error = e; }; error"},
 
-    // Improveable:
-
-        // Sealed / Frozen objects.
-        {expression: "Object.seal({})"},
-        {expression: "Object.freeze({})"},
-
         // Map / WeakMap
         {expression: "new Map"},
         {expression: "map = new Map; map.set(1, 2); map.set('key', 'value'); map"},
@@ -142,6 +136,11 @@ function test()
         {expression: "Promise.resolve()"},
         {expression: "Promise.resolve({result:1})"},
 
+    // Improveable:
+
+        // Sealed / Frozen objects.
+        {expression: "Object.seal({})"},
+        {expression: "Object.freeze({})"},
     ];
 
     if (!window.WebInspector) {
index 064df8d..6d3e9af 100644 (file)
@@ -1,3 +1,27 @@
+2015-03-04  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Array/Collection Sizes should be visible and distinct
+        https://bugs.webkit.org/show_bug.cgi?id=142254
+
+        Reviewed by Timothy Hatcher.
+
+        * runtime/WeakMapData.h:
+        (JSC::WeakMapData::size):
+        * inspector/JSInjectedScriptHost.cpp:
+        (Inspector::JSInjectedScriptHost::weakMapSize):
+        * inspector/JSInjectedScriptHost.h:
+        * inspector/JSInjectedScriptHostPrototype.cpp:
+        (Inspector::JSInjectedScriptHostPrototype::finishCreation):
+        (Inspector::jsInjectedScriptHostPrototypeFunctionWeakMapSize):
+        Add a way to get a WeakMap's size.
+
+        * inspector/protocol/Runtime.json:
+        Include size in RemoteObject and ObjectPreview.
+
+        * inspector/InjectedScriptSource.js:
+        Set the size of RemoteObjects and previews if they
+        are array/collection types.
+
 2015-03-04  Andreas Kling  <akling@apple.com>
 
         GC should compute stack bounds and dump registers at the earliest opportunity.
index 029e56d..8b362fe 100644 (file)
@@ -768,11 +768,8 @@ InjectedScript.prototype = {
         }
 
         var className = InjectedScriptHost.internalConstructorName(obj);
-        if (subtype === "array") {
-            if (typeof obj.length === "number")
-                className += "[" + obj.length + "]";
+        if (subtype === "array")
             return className;
-        }
 
         // NodeList in JSC is a function, check for array prior to this.
         if (typeof obj === "function")
@@ -904,6 +901,13 @@ InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType,
     this.className = InjectedScriptHost.internalConstructorName(object);
     this.description = injectedScript._describe(object);
 
+    if (subtype === "array")
+        this.size = typeof object.length === "number" ? object.length : 0;
+    else if (subtype === "set" || subtype === "map")
+        this.size = object.size;
+    else if (subtype === "weakmap")
+        this.size = InjectedScriptHost.weakMapSize(object);
+
     if (generatePreview && this.type === "object")
         this.preview = this._generatePreview(object, undefined, columnNames);
 }
@@ -925,6 +929,9 @@ InjectedScript.RemoteObject.prototype = {
             }
         }
 
+        if ("size" in this)
+            preview.size = this.size;
+
         return preview;
     },
 
index d29d36f..83b204f 100644 (file)
@@ -253,6 +253,19 @@ JSValue JSInjectedScriptHost::getInternalProperties(ExecState* exec)
     return jsUndefined();
 }
 
+JSValue JSInjectedScriptHost::weakMapSize(ExecState* exec)
+{
+    if (exec->argumentCount() < 1)
+        return jsUndefined();
+
+    JSValue value = exec->uncheckedArgument(0);
+    JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(value);
+    if (!weakMap)
+        return jsUndefined();
+
+    return jsNumber(weakMap->weakMapData()->size());
+}
+
 JSValue JSInjectedScriptHost::weakMapEntries(ExecState* exec)
 {
     if (exec->argumentCount() < 1)
index ff11e52..8c8d8b0 100644 (file)
@@ -65,6 +65,7 @@ public:
     JSC::JSValue subtype(JSC::ExecState*);
     JSC::JSValue functionDetails(JSC::ExecState*);
     JSC::JSValue getInternalProperties(JSC::ExecState*);
+    JSC::JSValue weakMapSize(JSC::ExecState*);
     JSC::JSValue weakMapEntries(JSC::ExecState*);
 
 protected:
index b069073..2f857ff 100644 (file)
@@ -43,6 +43,7 @@ static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionFunctio
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionGetInternalProperties(ExecState*);
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionInternalConstructorName(ExecState*);
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionIsHTMLAllCollection(ExecState*);
+static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionWeakMapSize(ExecState*);
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionWeakMapEntries(ExecState*);
 
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeAttributeEvaluate(ExecState*);
@@ -60,6 +61,7 @@ void JSInjectedScriptHostPrototype::finishCreation(VM& vm, JSGlobalObject* globa
     JSC_NATIVE_FUNCTION("getInternalProperties", jsInjectedScriptHostPrototypeFunctionGetInternalProperties, DontEnum, 1);
     JSC_NATIVE_FUNCTION("internalConstructorName", jsInjectedScriptHostPrototypeFunctionInternalConstructorName, DontEnum, 1);
     JSC_NATIVE_FUNCTION("isHTMLAllCollection", jsInjectedScriptHostPrototypeFunctionIsHTMLAllCollection, DontEnum, 1);
+    JSC_NATIVE_FUNCTION("weakMapSize", jsInjectedScriptHostPrototypeFunctionWeakMapSize, DontEnum, 1);
     JSC_NATIVE_FUNCTION("weakMapEntries", jsInjectedScriptHostPrototypeFunctionWeakMapEntries, DontEnum, 1);
 
     Identifier evaluateIdentifier(&vm, "evaluate");
@@ -102,6 +104,17 @@ EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionIsHTMLAllColle
     return JSValue::encode(castedThis->isHTMLAllCollection(exec));
 }
 
+EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionWeakMapSize(ExecState* exec)
+{
+    JSValue thisValue = exec->thisValue();
+    JSInjectedScriptHost* castedThis = jsDynamicCast<JSInjectedScriptHost*>(thisValue);
+    if (!castedThis)
+        return throwVMTypeError(exec);
+
+    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
+    return JSValue::encode(castedThis->weakMapSize(exec));
+}
+
 EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionWeakMapEntries(ExecState* exec)
 {
     JSValue thisValue = exec->thisValue();
index 16ca013..69fe926 100644 (file)
@@ -18,6 +18,7 @@
                 { "name": "value", "type": "any", "optional": true, "description": "Remote object value (in case of primitive values or JSON values if it was requested)." },
                 { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
                 { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." },
+                { "name": "size", "type": "integer", "optional": true, "description": "Size of the array/collection. Specified for array/map/set/weakmap object type values only." },
                 { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only." }
             ]
         },
@@ -32,7 +33,8 @@
                 { "name": "lossless", "type": "boolean", "description": "Determines whether preview is lossless (contains all information of the original object)." },
                 { "name": "overflow", "type": "boolean", "optional": true, "description": "True iff some of the properties of the original did not fit." },
                 { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "optional": true, "description": "List of the properties." },
-                { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for <code>map</code> and <code>set</code> subtype values only." }
+                { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for <code>map</code> and <code>set</code> subtype values only." },
+                { "name": "size", "type": "integer", "optional": true, "description": "Size of the array/collection. Specified for array/map/set/weakmap object type values only." }
             ]
         },
         {
index 68ea46f..45336c2 100644 (file)
@@ -67,6 +67,8 @@ public:
     MapType::const_iterator begin() const { return m_map.begin(); }
     MapType::const_iterator end() const { return m_map.end(); }
 
+    int size() const { return m_map.size(); }
+
 private:
     WeakMapData(VM&);
     static void destroy(JSCell*);
index 0d95bd5..0b9d4a0 100644 (file)
@@ -1,3 +1,48 @@
+2015-03-04  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Array/Collection Sizes should be visible and distinct
+        https://bugs.webkit.org/show_bug.cgi?id=142254
+
+        Reviewed by Timothy Hatcher.
+
+        * UserInterface/Models/ObjectPreview.js:
+        (WebInspector.ObjectPreview):
+        (WebInspector.ObjectPreview.fromPayload):
+        (WebInspector.ObjectPreview.prototype.get size):
+        (WebInspector.ObjectPreview.prototype.hasSize):
+        * UserInterface/Protocol/RemoteObject.js:
+        (WebInspector.RemoteObject):
+        (WebInspector.RemoteObject.fromPrimitiveValue):
+        (WebInspector.RemoteObject.fromPayload):
+        (WebInspector.RemoteObject.prototype.get size):
+        (WebInspector.RemoteObject.prototype.hasSize):
+        Check if this type has a size and get the size.
+        Gracefully handle construction for legacy protocols.
+
+        * UserInterface/Views/ObjectPreviewView.css:
+        (.object-preview > .size):
+        * UserInterface/Views/FormattedValue.css:
+        (:matches(.formatted-array, .formatted-map, .formatted-set, .formatted-weakmap) > .size):
+        Style the array/collection size.
+
+        * UserInterface/Views/ObjectPreviewView.js:
+        (WebInspector.ObjectPreviewView):
+        * UserInterface/Views/FormattedValue.js:
+        (WebInspector.FormattedValue.createElementForTypesAndValue):
+        (WebInspector.FormattedValue.createElementForRemoteObject):
+        (WebInspector.FormattedValue.createElementForObjectPreview):
+        (WebInspector.FormattedValue.createElementForPropertyPreview):
+        Add an element showing the array/collection size.
+
+        * UserInterface/Views/ObjectTreePropertyTreeElement.js:
+        (WebInspector.ObjectTreePropertyTreeElement.prototype):
+        Remove special handling for Array sizes now that this is handled earlier.
+
+        * UserInterface/Controllers/StorageManager.js:
+        (WebInspector.StorageManager.prototype.processData):
+        (WebInspector.StorageManager.prototype.requestIndexedDatabaseData):
+        Fix what looks like broken RemoteObject construction.
+
 2015-03-04  Brian J. Burg  <burg@cs.washington.edu>
 
         Web Inspector: TimelineViews should be displayed in a ContentViewContainer
index a23942e..29f35c7 100644 (file)
@@ -150,9 +150,9 @@ WebInspector.StorageManager.prototype = {
 
             for (var entryPayload of entryPayloads) {
                 var entry = {};
-                entry.primaryKey = new WebInspector.RemoteObject.fromPayload(entryPayload.primaryKey);
-                entry.key = new WebInspector.RemoteObject.fromPayload(entryPayload.key);
-                entry.value = new WebInspector.RemoteObject.fromPayload(entryPayload.value);
+                entry.primaryKey = WebInspector.RemoteObject.fromPayload(entryPayload.primaryKey);
+                entry.key = WebInspector.RemoteObject.fromPayload(entryPayload.key);
+                entry.value = WebInspector.RemoteObject.fromPayload(entryPayload.value);
                 entries.push(entry);
             }
 
index f96b83b..8a029b8 100644 (file)
@@ -23,7 +23,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.ObjectPreview = function(type, subtype, description, lossless, overflow, properties, entries)
+WebInspector.ObjectPreview = function(type, subtype, description, lossless, overflow, properties, entries, size)
 {
     WebInspector.Object.call(this);
 
@@ -37,6 +37,7 @@ WebInspector.ObjectPreview = function(type, subtype, description, lossless, over
     this._description = description || "";
     this._lossless = lossless;
     this._overflow = overflow || false;
+    this._size = size;
 
     this._properties = properties || null;
     this._entries = entries || null;
@@ -50,7 +51,17 @@ WebInspector.ObjectPreview.fromPayload = function(payload)
     if (payload.entries)
         payload.entries = payload.entries.map(function(entry) { return WebInspector.CollectionEntryPreview.fromPayload(entry); });
 
-    return new WebInspector.ObjectPreview(payload.type, payload.subtype, payload.description, payload.lossless, payload.overflow, payload.properties, payload.entries);
+    if (payload.subtype === "array") {
+        // COMPATIBILITY (iOS 8): Runtime.ObjectPreview did not have size property,
+        // instead it was tacked onto the end of the description, like "Array[#]".
+        var match = payload.description.match(/\[(\d+)\]$/);
+        if (match) {
+            payload.size = parseInt(match[1]);
+            payload.description = payload.description.replace(/\[\d+\]$/, "");
+        }
+    }
+
+    return new WebInspector.ObjectPreview(payload.type, payload.subtype, payload.description, payload.lossless, payload.overflow, payload.properties, payload.entries, payload.size);
 };
 
 WebInspector.ObjectPreview.prototype = {
@@ -92,5 +103,15 @@ WebInspector.ObjectPreview.prototype = {
     get collectionEntryPreviews()
     {
         return this._entries;
+    },
+
+    get size()
+    {
+        return this._size;
+    },
+
+    hasSize: function()
+    {
+        return this._size !== undefined && (this._subtype === "array" || this._subtype === "set" || this._subtype === "map" || this._subtype === "weakmap");
     }
 };
index 3d3f941..b90a4b2 100644 (file)
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.RemoteObject = function(objectId, type, subtype, value, description, preview)
+WebInspector.RemoteObject = function(objectId, type, subtype, value, description, size, preview)
 {
     // No superclass.
 
@@ -47,6 +47,7 @@ WebInspector.RemoteObject = function(objectId, type, subtype, value, description
         this._objectId = objectId;
         this._description = description;
         this._hasChildren = type !== "symbol";
+        this._size = size;
         this._preview = preview;
     } else {
         // Primitive or null.
@@ -61,13 +62,23 @@ WebInspector.RemoteObject = function(objectId, type, subtype, value, description
 
 WebInspector.RemoteObject.fromPrimitiveValue = function(value)
 {
-    return new WebInspector.RemoteObject(undefined, typeof value, undefined, value);
+    return new WebInspector.RemoteObject(undefined, typeof value, undefined, undefined, value);
 };
 
 WebInspector.RemoteObject.fromPayload = function(payload)
 {
     console.assert(typeof payload === "object", "Remote object payload should only be an object");
 
+    if (payload.subtype === "array") {
+        // COMPATIBILITY (iOS 8): Runtime.RemoteObject did not have size property,
+        // instead it was tacked onto the end of the description, like "Array[#]".
+        var match = payload.description.match(/\[(\d+)\]$/);
+        if (match) {
+            payload.size = parseInt(match[1]);
+            payload.description = payload.description.replace(/\[\d+\]$/, "");
+        }
+    }
+
     if (payload.preview) {
         // COMPATIBILITY (iOS 8): iOS 7 and 8 did not have type/subtype/description on
         // Runtime.ObjectPreview. Copy them over from the RemoteObject.
@@ -75,11 +86,12 @@ WebInspector.RemoteObject.fromPayload = function(payload)
             payload.preview.type = payload.type;
             payload.preview.subtype = payload.subtype;
             payload.preview.description = payload.description;
+            payload.preview.size = payload.size;
         }
         payload.preview = WebInspector.ObjectPreview.fromPayload(payload.preview);
     }
 
-    return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
+    return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.size, payload.preview);
 };
 
 WebInspector.RemoteObject.createCallArgument = function(valueOrObject)
@@ -151,11 +163,21 @@ WebInspector.RemoteObject.prototype = {
         return this._value;
     },
 
+    get size()
+    {
+        return this._size || 0;
+    },
+
     get preview()
     {
         return this._preview;
     },
 
+    hasSize: function()
+    {
+        return this.isArray() || this.isCollectionType();
+    },
+
     hasValue: function()
     {
         return "_value" in this;
index be33119..d29451a 100644 (file)
     color: black;
 }
 
+:matches(.formatted-array, .formatted-map, .formatted-set, .formatted-weakmap) > .size {
+    font-style: normal;
+    color: hsl(0, 0%, 67%);
+}
+
 .formatted-node > ol {
     display: block !important;
 }
index ee2a130..094b1f8 100644 (file)
@@ -69,7 +69,7 @@ WebInspector.FormattedValue.createElementForNode = function(object)
     return span;
 };
 
-WebInspector.FormattedValue.createElementForTypesAndValue = function(type, subtype, displayString, isPreview, hadException)
+WebInspector.FormattedValue.createElementForTypesAndValue = function(type, subtype, displayString, size, isPreview, hadException)
 {
     var span = document.createElement("span");
     span.classList.add(WebInspector.FormattedValue.classNameForTypes(type, subtype));
@@ -94,22 +94,30 @@ WebInspector.FormattedValue.createElementForTypesAndValue = function(type, subty
 
     // Everything else, the description/value string.
     span.textContent = displayString;
+
+    // If there is a size, include it.
+    if (size !== undefined && (subtype === "array" || subtype === "set" || subtype === "map" || subtype === "weakmap")) {
+        var sizeElement = span.appendChild(document.createElement("span"));
+        sizeElement.className = "size";
+        sizeElement.textContent = " (" + size + ")";
+    }
+
     return span;
 };
 
 WebInspector.FormattedValue.createElementForRemoteObject = function(object, hadException)
 {
-    return WebInspector.FormattedValue.createElementForTypesAndValue(object.type, object.subtype, object.description, false, hadException);
+    return WebInspector.FormattedValue.createElementForTypesAndValue(object.type, object.subtype, object.description, object.size, false, hadException);
 };
 
 WebInspector.FormattedValue.createElementForObjectPreview = function(objectPreview)
 {
-    return WebInspector.FormattedValue.createElementForTypesAndValue(objectPreview.type, objectPreview.subtype, objectPreview.description, true, false);
+    return WebInspector.FormattedValue.createElementForTypesAndValue(objectPreview.type, objectPreview.subtype, objectPreview.description, objectPreview.size, true, false);
 };
 
 WebInspector.FormattedValue.createElementForPropertyPreview = function(propertyPreview)
 {
-    return WebInspector.FormattedValue.createElementForTypesAndValue(propertyPreview.type, propertyPreview.subtype, propertyPreview.value, true, false);
+    return WebInspector.FormattedValue.createElementForTypesAndValue(propertyPreview.type, propertyPreview.subtype, propertyPreview.value, undefined, true, false);
 };
 
 WebInspector.FormattedValue.createObjectTreeOrFormattedValueForRemoteObject = function(object, propertyPath)
index 2da3e6e..7fddde8 100644 (file)
@@ -38,3 +38,8 @@
 .object-preview .name {
     color: rgb(136, 19, 145);
 }
+
+.object-preview > .size {
+    font-style: normal;
+    color: hsl(0, 0%, 67%);
+}
index 0af32f6..4f9909f 100644 (file)
@@ -44,6 +44,12 @@ WebInspector.ObjectPreviewView = function(preview, mode)
     this._titleElement.hidden = true;
     this._initTitleElement();
 
+    if (this._preview.hasSize()) {
+        var sizeElement = this._element.appendChild(document.createElement("span"));
+        sizeElement.className = "size";
+        sizeElement.textContent = " (" + this._preview.size + ")";
+    }
+
     if (this._lossless)
         this._element.classList.add("lossless");
 };
index 2e61d15..98fffee 100644 (file)
@@ -375,7 +375,7 @@ WebInspector.ObjectTreePropertyTreeElement.prototype = {
         if (value.subtype === "regexp")
             return "RegExp";
 
-        return value.description.replace(/\[\d+\]$/, "").replace(/Prototype$/, "");
+        return value.description.replace(/Prototype$/, "");
     },
 
     _propertyPathString: function(propertyPath)