Bug 30974

Summary: Web Inspector: Pretty print array-like objects
Product: WebKit Reporter: Keishi Hattori <keishi>
Component: Web Inspector (Deprecated)Assignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Normal CC: abarth, bweinstein, joepeck, john.david.dalton, kangax, keishi, oliver, pfeldman, pmuellr, rik, timothy
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Attachments:
Description Flags
proposed patch
abarth: review-
[PATCH] Change suggested by John-David Dalton. joepeck: review+

Description Keishi Hattori 2009-10-31 06:18:09 PDT
I think jQuery users would like to have things like jQuery(document) logged as an array. I don't want to treat jQuery special so why not log all objects with "length" property as an array.
Comment 1 Keishi Hattori 2009-10-31 06:20:12 PDT
Created attachment 42255 [details]
proposed patch
Comment 2 Timothy Hatcher 2009-10-31 08:25:58 PDT
This seems fine to me. What does everyone else think? Joe?
Comment 3 Patrick Mueller 2009-10-31 10:06:34 PDT
I guess the question is, what kind of output do you get if you print an object which happens to have a length property but is not actually an array?  Won't it skip over all keys that aren't in the range 0..length?  Which is probably all the keys?  Here's an example session, which shows the (potential) problem:

   > x = [1,2,3]
   [1, 2, 3] 
   > x.a = 4
   4
   > x
   [1, 2, 3]

woops, where's x.a printed???

Personally, it seems like it'd be more consistent to alway print arrays and objects the same way; both are indexed collections, but one has a constrained set of index values.  

Maybe instead we need to have different ways to print different representations - different functions, or a "mode" switch, or something.

But I don't have much skin in the game here - I rarely print array-like things in the console.  Objects, all the time, but for some reason, almost never arrays.
Comment 4 Joseph Pecoraro 2009-10-31 10:16:25 PDT
(In reply to comment #2)
> This seems fine to me. What does everyone else think? Joe?

This seems like a good, future-proof way to handle things.

> Personally, it seems like it'd be more consistent to alway print arrays and
> objects the same way; both are indexed collections, but one has a constrained
> set of index values.  

I believe that is what "console.dir" is for (poorly named from the Firebug API):

  > var x = [1,2,3];
  > x.a = 4;
  > console.dir(x)
  Array
  0: 1
  1: 2
  2: 3
  a: 4


> But I don't have much skin in the game here - I rarely print array-like things
> in the console.  Objects, all the time, but for some reason, almost never
> arrays.

Sometimes printing objects requires a lot of scrolling for property/values pairs that are really short (multiple could fit on a line). I like the compactness of the Array printing.
Comment 5 Timothy Hatcher 2009-10-31 10:22:20 PDT
To force object output for DOM nodes, arrays and anything else, use the dir() function. A poorly named API that matches Firebug.

So this will do what you want:

  > dir(x)

  Array
       0: 1
       1: 2
       2: 3
       a: 4

But I tend to agree, we should only do array formatting when we are positive about it.

Maybe we should look for any non-numeric enumerable properties. If there are any, we just use object formatting. (This might be too slow for large arrays though…)
Comment 6 Pavel Feldman 2009-10-31 14:18:35 PDT
I would not be doing it actually. Even now, dumping document.body.childNodes on a large page might lead to unpleasant overhead. console.dir is what users do and the heavy result is predictable there.

Btw, I experience difficulties parsing document.body.childNodes output. Show we print cell-per-row with array index instead?
Comment 7 Keishi Hattori 2009-10-31 19:15:04 PDT
(In reply to comment #6)
> I would not be doing it actually. Even now, dumping document.body.childNodes on
> a large page might lead to unpleasant overhead. console.dir is what users do
> and the heavy result is predictable there.
> 
> Btw, I experience difficulties parsing document.body.childNodes output. Show we
> print cell-per-row with array index instead?

I think both are valid concerns but should be a separate bug. The problem this addresses is when using jQuery, like 90% of the objects you deal with is this Array-like object. Having to click to check what's inside every time will big stress to the user.

My worry is that the isArrayLike Object() logic too forgiving. This patch uses 
  typeof o.length === "number"
but I think there is a good chance that the user will have an object like this.
  var video = {title:"foo", length: 20, url: "http://foo/ba.mp4"}
Comment 8 Pavel Feldman 2009-11-18 09:08:41 PST
What do we do to this one? Do we have an agreement here? (Are you expecting feedback?)
Comment 9 Timothy Hatcher 2009-11-18 09:48:19 PST
I still think we should try this:

> Maybe we should look for any non-numeric enumerable properties. If there are
> any, we just use object formatting. (This might be too slow for large arrays
> though…)

If there are tons of properties, just use the object display?
Comment 10 Oliver Hunt 2009-11-19 22:01:43 PST
Just for reference ES5 defines (and we support) Array.isArray(obj) which returns true for real arrays.
Comment 11 Adam Barth 2009-11-30 12:22:10 PST
Attachment 42255 [details] passed the style-queue
Comment 12 Adam Barth 2010-01-01 17:57:52 PST
Comment on attachment 42255 [details]
proposed patch

This patch has been sitting around for a long time and is clearly wrong as explained in Comment #7.
Comment 13 Patrick Mueller 2010-07-21 14:12:42 PDT
This feature has been addressed for jQuery via Bug 42758.

Here's another thought.  Have library implementors tell us when they create objects which should be displayed in the debugger as arrays.  For instance, such objects could have a property like __debugDisplayAsArray.  In the jQuery case, this could just be added to the jQuery prototype.  Or it could be a property of the constructor function, in which case you could test with obj.constructor.__whatever ...
Comment 14 Joseph Pecoraro 2010-08-12 15:21:26 PDT
(In reply to comment #20)
> A more robust (though certainly not foolproof) check might be `(obj.length ===
> obj.length >>> 0) && "0" in obj`, or even iterating over the object's properties
> until a numeric one is encountered.

I think this check is pretty sufficient.


> I also like kangax's proposal for a separate representation for array-like
> objects, maybe something akin to `<List: [...]>`. I would, however, also like
> to propose using that representation for NodeLists and HTMLCollections as well,
> reserving `[...]` for arrays only.

I also like this idea. Currently we treat NodeLists and Collections as arrays,
and I think they deserve some differentiation.

http://trac.webkit.org/browser/trunk/WebCore/inspector/front-end/InjectedScript.js

>    if (obj instanceof inspectedWindow.Array)
>        return "array";
>    if (obj instanceof inspectedWindow.NodeList)
>        return "array";
>    if (obj instanceof inspectedWindow.HTMLCollection)
>        return "array";

------

It seems the major issue is:

  JavaScript libraries have "array like" collections which would be useful to
  display like arrays in the console! But, sometimes this could go wrong, or
  sometimes this could hide information.

For those "sometimes" cases, like was mentioned above, I think it would be
good enough to indicate that this is in fact not an array, but an "array like"
object. As a user, if I saw this indicator it be helpful, and I might want
to display this as an object instead. So, if that indicator was clickable,
and could toggle between the array-like quick view and the complete object
view that sounds best to me.

Does that sound good? Is there a use case I'm missing?
Comment 15 Joseph Pecoraro 2010-08-12 15:34:45 PDT
(In reply to comment #25)
> You just made "window" render as  [undefined] in the console :P

Yah, I guess more have a "length" than I thought. Functions do too, but they
have good instanceof and typeof semantics.


> But since webkit already sets arguments's [[Class]] to "Arguments", the other
> — more complex — versions could probably be avoided.

"Arguments" is one of my major use-cases. I would like to see that arraylike.
And you're right, this can be special cased differently.
Comment 16 John-David Dalton 2010-08-12 15:35:21 PDT
For reference this is how Firebug detects array-like:
http://code.google.com/p/fbug/source/browse/branches/firebug1.6/content/firebug/reps.js#560

This allows jQuery results, MooTools 1.3 results and anything with a 
length property and splice method to be displayed as an array. It covers the arguments object too.
Comment 17 Pavel Feldman 2010-08-12 15:53:22 PDT
Created attachment 64278 [details]
[PATCH] Change suggested by John-David Dalton.
Comment 18 Joseph Pecoraro 2010-08-12 15:59:26 PDT
Comment on attachment 64278 [details]
[PATCH] Change suggested by John-David Dalton.

> +++ b/WebCore/inspector/front-end/InjectedScript.js
> +    if (isFinite(obj.length) && typeof obj.callee === "function")  // arguments.

One space before the comment. ;)

r=me
Comment 19 Pavel Feldman 2010-08-12 16:08:21 PDT
Fix suggested in Comment #16 is landed.

Committing to http://svn.webkit.org/repository/webkit/trunk ...
	M	WebCore/ChangeLog
	M	WebCore/inspector/front-end/InjectedScript.js
Committed r65278