Web Inspector: Better Console Previews for Arrays / Small Objects
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Mar 2015 04:41:31 +0000 (04:41 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Mar 2015 04:41:31 +0000 (04:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142322

Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* inspector/InjectedScriptSource.js:
Create deep valuePreviews for simple previewable objects,
such as arrays with 5 values, or basic objects with
3 properties.

Source/WebInspectorUI:

* UserInterface/Views/ObjectPreviewView.js:
If there is a sub-preview, show the sub-preview.

* UserInterface/Views/ObjectTreeView.js:
(WebInspector.ObjectTreeView):
For an ObjectTree that is not a root (e.g. one inside of
an array/set/map property tree element) allow it to be
expanded even if the preview is lossless.

LayoutTests:

* inspector/model/remote-object-expected.txt:
* inspector/model/remote-object.html:
Include a test with cyclic values, and update
results which now have sub-previews.

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

LayoutTests/ChangeLog
LayoutTests/inspector/model/remote-object-expected.txt
LayoutTests/inspector/model/remote-object.html
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/InjectedScriptSource.js
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Views/ObjectPreviewView.js
Source/WebInspectorUI/UserInterface/Views/ObjectTreeView.js

index 7fbddaf..f81648d 100644 (file)
@@ -1,3 +1,15 @@
+2015-03-16  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Better Console Previews for Arrays / Small Objects
+        https://bugs.webkit.org/show_bug.cgi?id=142322
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/model/remote-object-expected.txt:
+        * inspector/model/remote-object.html:
+        Include a test with cyclic values, and update
+        results which now have sub-previews.
+
 2015-03-16  Ryosuke Niwa  <rniwa@webkit.org>
 
         Implement default constructor
index b15f60b..d2f0549 100644 (file)
@@ -592,7 +592,7 @@ EXPRESSION: [[1],[2],[3]]
     "_type": "object",
     "_subtype": "array",
     "_description": "Array",
-    "_lossless": false,
+    "_lossless": true,
     "_overflow": false,
     "_size": 3,
     "_properties": [
@@ -600,19 +600,64 @@ EXPRESSION: [[1],[2],[3]]
         "_name": "0",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array"
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        }
       },
       {
         "_name": "1",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array"
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
       },
       {
         "_name": "2",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array"
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "3"
+            }
+          ],
+          "_entries": null
+        }
       }
     ],
     "_entries": null
@@ -678,24 +723,63 @@ EXPRESSION: [{a:1}, {b:2}, {c:2}]
     "_type": "object",
     "_subtype": "array",
     "_description": "Array",
-    "_lossless": false,
+    "_lossless": true,
     "_overflow": false,
     "_size": 3,
     "_properties": [
       {
         "_name": "0",
         "_type": "object",
-        "_value": "Object"
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "a",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        }
       },
       {
         "_name": "1",
         "_type": "object",
-        "_value": "Object"
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "b",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
       },
       {
         "_name": "2",
         "_type": "object",
-        "_value": "Object"
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "c",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
       }
     ],
     "_entries": null
@@ -714,7 +798,7 @@ EXPRESSION: [[{a:1}, {b:2}, {c:2}]]
     "_type": "object",
     "_subtype": "array",
     "_description": "Array",
-    "_lossless": false,
+    "_lossless": true,
     "_overflow": false,
     "_size": 1,
     "_properties": [
@@ -722,7 +806,71 @@ EXPRESSION: [[{a:1}, {b:2}, {c:2}]]
         "_name": "0",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array"
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 3,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "object",
+              "_valuePreview": {
+                "_type": "object",
+                "_description": "Object",
+                "_lossless": true,
+                "_overflow": false,
+                "_properties": [
+                  {
+                    "_name": "a",
+                    "_type": "number",
+                    "_value": "1"
+                  }
+                ],
+                "_entries": null
+              }
+            },
+            {
+              "_name": "1",
+              "_type": "object",
+              "_valuePreview": {
+                "_type": "object",
+                "_description": "Object",
+                "_lossless": true,
+                "_overflow": false,
+                "_properties": [
+                  {
+                    "_name": "b",
+                    "_type": "number",
+                    "_value": "2"
+                  }
+                ],
+                "_entries": null
+              }
+            },
+            {
+              "_name": "2",
+              "_type": "object",
+              "_valuePreview": {
+                "_type": "object",
+                "_description": "Object",
+                "_lossless": true,
+                "_overflow": false,
+                "_properties": [
+                  {
+                    "_name": "c",
+                    "_type": "number",
+                    "_value": "2"
+                  }
+                ],
+                "_entries": null
+              }
+            }
+          ],
+          "_entries": null
+        }
       }
     ],
     "_entries": null
@@ -1554,6 +1702,33 @@ EXPRESSION: ({a: 1, b: "string", c: /regex/, d: Symbol('sym')})
 }
 
 -----------------------------------------------------
+EXPRESSION: o = {a:1}; o.b = o; o
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "a",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "b",
+        "_type": "object",
+        "_value": "Object"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
 EXPRESSION: ({a:function a(){}, b:function b(){}, get getter(){}, set setter(v){}})
 {
   "_type": "object",
@@ -2948,14 +3123,23 @@ EXPRESSION: [][Symbol.iterator]()
     "_type": "object",
     "_subtype": "iterator",
     "_description": "ArrayIterator",
-    "_lossless": false,
+    "_lossless": true,
     "_overflow": false,
     "_properties": [
       {
         "_name": "array",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 0,
+          "_properties": [],
+          "_entries": null
+        },
         "_internal": true
       },
       {
@@ -2980,14 +3164,29 @@ EXPRESSION: [1][Symbol.iterator]()
     "_type": "object",
     "_subtype": "iterator",
     "_description": "ArrayIterator",
-    "_lossless": false,
+    "_lossless": true,
     "_overflow": false,
     "_properties": [
       {
         "_name": "array",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        },
         "_internal": true
       },
       {
@@ -3684,14 +3883,34 @@ EXPRESSION: x = undefined; (function() { x = arguments; })(1, 'two'); x[Symbol.i
     "_type": "object",
     "_subtype": "iterator",
     "_description": "ArgumentsIterator",
-    "_lossless": false,
+    "_lossless": true,
     "_overflow": false,
     "_properties": [
       {
         "_name": "arguments",
         "_type": "object",
         "_subtype": "array",
-        "_value": "Arguments",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Arguments",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "two"
+            }
+          ],
+          "_entries": null
+        },
         "_internal": true
       }
     ],
@@ -3839,7 +4058,7 @@ EXPRESSION: Promise.resolve({result:1})
   "_preview": {
     "_type": "object",
     "_description": "Promise",
-    "_lossless": false,
+    "_lossless": true,
     "_overflow": false,
     "_properties": [
       {
@@ -3851,7 +4070,20 @@ EXPRESSION: Promise.resolve({result:1})
       {
         "_name": "result",
         "_type": "object",
-        "_value": "Object",
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "result",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        },
         "_internal": true
       }
     ],
index 2134948..6ecac87 100644 (file)
@@ -91,6 +91,7 @@ function test()
         {expression: "({a: 1})"},
         {expression: "({a: 1, b: 0, c: -0})"},
         {expression: "({a: 1, b: \"string\", c: /regex/, d: Symbol('sym')})"},
+        {expression: "o = {a:1}; o.b = o; o"}, // Cyclic.
         {expression: "({a:function a(){}, b:function b(){}, get getter(){}, set setter(v){}})"},
         {expression: "function Foo() {}; new Foo"},
         {expression: "var Foo2 = function() {}; new Foo2"},
index f17088a..d86f6b9 100644 (file)
@@ -1,3 +1,15 @@
+2015-03-16  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Better Console Previews for Arrays / Small Objects
+        https://bugs.webkit.org/show_bug.cgi?id=142322
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/InjectedScriptSource.js:
+        Create deep valuePreviews for simple previewable objects,
+        such as arrays with 5 values, or basic objects with
+        3 properties.
+
 2015-03-16  Ryosuke Niwa  <rniwa@webkit.org>
 
         Add support for default constructor
index 9127eff..a1c252d 100644 (file)
@@ -990,13 +990,14 @@ InjectedScript.RemoteObject.prototype = {
                     return preview;
             }
 
+            if (preview.entries)
+                return preview;
+
             // Properties.
             var descriptors = injectedScript._propertyDescriptors(object, InjectedScript.CollectionMode.AllProperties);
             this._appendPropertyPreviews(preview, descriptors, false, propertiesThreshold, firstLevelKeys, secondLevelKeys);
             if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
                 return preview;
-
-            // FIXME: Iterator entries.
         } catch (e) {
             preview.lossless = false;
         }
@@ -1093,6 +1094,13 @@ InjectedScript.RemoteObject.prototype = {
                     preview.lossless = false;
                 if (subPreview.overflow)
                     preview.overflow = true;
+            } else if (this._isPreviewableObject(value)) {
+                var subPreview = this._createObjectPreviewForValue(value);
+                property.valuePreview = subPreview;
+                if (!subPreview.lossless)
+                    preview.lossless = false;
+                if (subPreview.overflow)
+                    preview.overflow = true;
             } else {
                 var description = "";
                 if (type !== "function")
@@ -1145,6 +1153,57 @@ InjectedScript.RemoteObject.prototype = {
         }, this);
     },
 
+    _isPreviewableObject: function(object)
+    {
+        return this._isPreviewableObjectInternal(object, new Set, 1);
+    },
+
+    _isPreviewableObjectInternal: function(object, knownObjects, depth)
+    {
+        // Deep object.
+        if (depth > 3)
+            return false;
+
+        // Primitive.
+        if (injectedScript.isPrimitiveValue(object) || isSymbol(object))
+            return true;
+
+        // Cyclic objects.
+        if (knownObjects.has(object))
+            return false;
+
+        ++depth;
+        knownObjects.add(object);
+
+        // Arrays are simple if they have 5 or less simple objects.
+        var subtype = injectedScript._subtype(object);
+        if (subtype === "array") {
+            var length = object.length;
+            if (length > 5)
+                return false;
+            for (var i = 0; i < length; ++i) {
+                if (!this._isPreviewableObjectInternal(object[i], knownObjects, depth))
+                    return false;
+            }
+            return true;
+        }
+
+        // Not a basic object.
+        if (object.__proto__ && object.__proto__.__proto__)
+            return false;
+
+        // Objects are simple if they have 3 or less simple properties.
+        var ownPropertyNames = Object.getOwnPropertyNames(object);
+        if (ownPropertyNames.length > 3)
+            return false;
+        for (var propertyName of ownPropertyNames) {
+            if (!this._isPreviewableObjectInternal(object[propertyName], knownObjects, depth))
+                return false;
+        }
+
+        return true;
+    },
+
     _abbreviateString: function(string, maxLength, middle)
     {
         if (string.length <= maxLength)
index adf2cb3..0aef173 100644 (file)
@@ -1,3 +1,19 @@
+2015-03-16  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Better Console Previews for Arrays / Small Objects
+        https://bugs.webkit.org/show_bug.cgi?id=142322
+
+        Reviewed by Timothy Hatcher.
+
+        * UserInterface/Views/ObjectPreviewView.js:
+        If there is a sub-preview, show the sub-preview.
+
+        * UserInterface/Views/ObjectTreeView.js:
+        (WebInspector.ObjectTreeView):
+        For an ObjectTree that is not a root (e.g. one inside of
+        an array/set/map property tree element) allow it to be
+        expanded even if the preview is lossless.
+
 2015-03-16  Nikita Vasilyev  <nvasilyev@apple.com>
 
         Web Inspector: Rename ConsoleMessage and ConsoleMessageImpl to LegacyConsoleMessage and LegacyConsoleMessageImpl respectively
index 9d87a45..c7ed96e 100644 (file)
@@ -211,7 +211,10 @@ WebInspector.ObjectPreviewView.prototype = {
                 element.appendChild(document.createTextNode(": "));
             }
 
-            element.appendChild(WebInspector.FormattedValue.createElementForPropertyPreview(property));
+            if (property.valuePreview)
+                this._appendPreview(element, property.valuePreview);
+            else
+                element.appendChild(WebInspector.FormattedValue.createElementForPropertyPreview(property));
         }
 
         if (preview.overflow)
index 6818bef..8d70501 100644 (file)
@@ -49,7 +49,7 @@ WebInspector.ObjectTreeView = function(object, mode, propertyPath, forceExpandin
         this._previewView.element.addEventListener("click", this._handlePreviewOrTitleElementClick.bind(this));
         this._element.appendChild(this._previewView.element);
 
-        if (this._previewView.lossless && !forceExpanding) {
+        if (this._previewView.lossless && !this._propertyPath.parent && !forceExpanding) {
             this._hasLosslessPreview = true;
             this.element.classList.add("lossless-preview");
         }