Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sdk / RemoteObject.js
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * This may not be an interface due to "instanceof WebInspector.RemoteObject" checks in the code.
33  *
34  * @constructor
35  */
36 WebInspector.RemoteObject = function() { }
37
38 WebInspector.RemoteObject.prototype = {
39     /** @return {string} */
40     get type()
41     {
42         throw "Not implemented";
43     },
44
45     /** @return {string|undefined} */
46     get subtype()
47     {
48         throw "Not implemented";
49     },
50
51     /** @return {string|undefined} */
52     get description()
53     {
54         throw "Not implemented";
55     },
56
57     /** @return {boolean} */
58     get hasChildren()
59     {
60         throw "Not implemented";
61     },
62
63     /**
64      * @return {number}
65      */
66     arrayLength: function()
67     {
68         throw "Not implemented";
69     },
70
71     /**
72      * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
73      */
74     getOwnProperties: function(callback)
75     {
76         throw "Not implemented";
77     },
78
79     /**
80      * @param {boolean} accessorPropertiesOnly
81      * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
82      */
83     getAllProperties: function(accessorPropertiesOnly, callback)
84     {
85         throw "Not implemented";
86     },
87
88     /**
89      * @param {function(this:Object, ...)} functionDeclaration
90      * @param {!Array.<!RuntimeAgent.CallArgument>=} args
91      * @param {function(?WebInspector.RemoteObject, boolean=)=} callback
92      */
93     callFunction: function(functionDeclaration, args, callback)
94     {
95         throw "Not implemented";
96     },
97
98     /**
99      * @param {function(this:Object)} functionDeclaration
100      * @param {!Array.<!RuntimeAgent.CallArgument>|undefined} args
101      * @param {function(*)} callback
102      */
103     callFunctionJSON: function(functionDeclaration, args, callback)
104     {
105         throw "Not implemented";
106     },
107
108     /**
109      * @return {!WebInspector.Target}
110      */
111     target: function()
112     {
113         throw new Error("Target-less object");
114     },
115
116     /**
117      * @return {boolean}
118      */
119     isNode: function()
120     {
121         return false;
122     },
123
124     reveal: function()
125     {
126         WebInspector.Revealer.reveal(this);
127     },
128
129     /**
130      * @param {function(?DebuggerAgent.FunctionDetails)} callback
131      */
132     functionDetails: function(callback)
133     {
134         callback(null);
135     }
136 }
137
138 /**
139  * @param {*} value
140  * @return {!WebInspector.RemoteObject}
141  */
142 WebInspector.RemoteObject.fromLocalObject = function(value)
143 {
144     return new WebInspector.LocalJSONObject(value);
145 }
146
147 /**
148  * @param {!WebInspector.RemoteObject} remoteObject
149  * @return {string}
150  */
151 WebInspector.RemoteObject.type = function(remoteObject)
152 {
153     if (remoteObject === null)
154         return "null";
155
156     var type = typeof remoteObject;
157     if (type !== "object" && type !== "function")
158         return type;
159
160     return remoteObject.type;
161 }
162
163 /**
164  * @param {!RuntimeAgent.RemoteObject|!WebInspector.RemoteObject} remoteObject
165  * @return {!RuntimeAgent.CallArgument}
166  */
167 WebInspector.RemoteObject.toCallArgument = function(remoteObject)
168 {
169     var type = /** @type {!RuntimeAgent.CallArgumentType.<string>} */ (remoteObject.type);
170     var value = remoteObject.value;
171
172     // Handle special numbers: NaN, Infinity, -Infinity, -0.
173     if (type === "number") {
174         switch (remoteObject.description) {
175         case "NaN":
176         case "Infinity":
177         case "-Infinity":
178         case "-0":
179             value = remoteObject.description;
180             break;
181         }
182     }
183
184     return {
185         value: value,
186         objectId: remoteObject.objectId,
187         type: type
188     };
189 }
190
191 /**
192  * @constructor
193  * @extends {WebInspector.RemoteObject}
194  * @param {!WebInspector.Target} target
195  * @param {string|undefined} objectId
196  * @param {string} type
197  * @param {string|undefined} subtype
198  * @param {*} value
199  * @param {string=} description
200  * @param {!RuntimeAgent.ObjectPreview=} preview
201  */
202 WebInspector.RemoteObjectImpl = function(target, objectId, type, subtype, value, description, preview)
203 {
204     WebInspector.RemoteObject.call(this);
205
206     this._target = target;
207     this._runtimeAgent = target.runtimeAgent();
208     this._domModel = target.domModel;
209
210     this._type = type;
211     this._subtype = subtype;
212     if (objectId) {
213         // handle
214         this._objectId = objectId;
215         this._description = description;
216         this._hasChildren = true;
217         this._preview = preview;
218     } else {
219         // Primitive or null object.
220         console.assert(type !== "object" || value === null);
221         this._description = description || (value + "");
222         this._hasChildren = false;
223         // Handle special numbers: NaN, Infinity, -Infinity, -0.
224         if (type === "number" && typeof value !== "number")
225             this.value = Number(value);
226         else
227             this.value = value;
228     }
229 }
230
231 WebInspector.RemoteObjectImpl.prototype = {
232     /** @return {!RuntimeAgent.RemoteObjectId} */
233     get objectId()
234     {
235         return this._objectId;
236     },
237
238     /** @return {string} */
239     get type()
240     {
241         return this._type;
242     },
243
244     /** @return {string|undefined} */
245     get subtype()
246     {
247         return this._subtype;
248     },
249
250     /** @return {string|undefined} */
251     get description()
252     {
253         return this._description;
254     },
255
256     /** @return {boolean} */
257     get hasChildren()
258     {
259         return this._hasChildren;
260     },
261
262     /** @return {!RuntimeAgent.ObjectPreview|undefined} */
263     get preview()
264     {
265         return this._preview;
266     },
267
268     /**
269      * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
270      */
271     getOwnProperties: function(callback)
272     {
273         this.doGetProperties(true, false, callback);
274     },
275
276     /**
277      * @param {boolean} accessorPropertiesOnly
278      * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
279      */
280     getAllProperties: function(accessorPropertiesOnly, callback)
281     {
282         this.doGetProperties(false, accessorPropertiesOnly, callback);
283     },
284
285     /**
286      * @param {!Array.<string>} propertyPath
287      * @param {function(?WebInspector.RemoteObject, boolean=)} callback
288      */
289     getProperty: function(propertyPath, callback)
290     {
291         /**
292          * @param {string} arrayStr
293          * @suppressReceiverCheck
294          * @this {Object}
295          */
296         function remoteFunction(arrayStr)
297         {
298             var result = this;
299             var properties = JSON.parse(arrayStr);
300             for (var i = 0, n = properties.length; i < n; ++i)
301                 result = result[properties[i]];
302             return result;
303         }
304
305         var args = [{ value: JSON.stringify(propertyPath) }];
306         this.callFunction(remoteFunction, args, callback);
307     },
308
309     /**
310      * @param {boolean} ownProperties
311      * @param {boolean} accessorPropertiesOnly
312      * @param {?function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
313      */
314     doGetProperties: function(ownProperties, accessorPropertiesOnly, callback)
315     {
316         if (!this._objectId) {
317             callback(null, null);
318             return;
319         }
320
321         /**
322          * @param {?Protocol.Error} error
323          * @param {!Array.<!RuntimeAgent.PropertyDescriptor>} properties
324          * @param {!Array.<!RuntimeAgent.InternalPropertyDescriptor>=} internalProperties
325          * @this {WebInspector.RemoteObjectImpl}
326          */
327         function remoteObjectBinder(error, properties, internalProperties)
328         {
329             if (error) {
330                 callback(null, null);
331                 return;
332             }
333             var result = [];
334             for (var i = 0; properties && i < properties.length; ++i) {
335                 var property = properties[i];
336                 var propertyValue = property.value ? this._target.runtimeModel.createRemoteObject(property.value) : null;
337                 var remoteProperty = new WebInspector.RemoteObjectProperty(property.name, propertyValue,
338                         !!property.enumerable, !!property.writable, !!property.isOwn, !!property.wasThrown);
339
340                 if (typeof property.value === "undefined") {
341                     if (property.get && property.get.type !== "undefined")
342                         remoteProperty.getter = this._target.runtimeModel.createRemoteObject(property.get);
343                     if (property.set && property.set.type !== "undefined")
344                         remoteProperty.setter = this._target.runtimeModel.createRemoteObject(property.set);
345                 }
346
347                 result.push(remoteProperty);
348             }
349             var internalPropertiesResult = null;
350             if (internalProperties) {
351                 internalPropertiesResult = [];
352                 for (var i = 0; i < internalProperties.length; i++) {
353                     var property = internalProperties[i];
354                     if (!property.value)
355                         continue;
356                     internalPropertiesResult.push(new WebInspector.RemoteObjectProperty(property.name, this._target.runtimeModel.createRemoteObject(property.value)));
357                 }
358             }
359             callback(result, internalPropertiesResult);
360         }
361         this._runtimeAgent.getProperties(this._objectId, ownProperties, accessorPropertiesOnly, remoteObjectBinder.bind(this));
362     },
363
364     /**
365      * @param {string} name
366      * @param {string} value
367      * @param {function(string=)} callback
368      */
369     setPropertyValue: function(name, value, callback)
370     {
371         if (!this._objectId) {
372             callback("Can't set a property of non-object.");
373             return;
374         }
375
376         this._runtimeAgent.invoke_evaluate({expression:value, doNotPauseOnExceptionsAndMuteConsole:true}, evaluatedCallback.bind(this));
377
378         /**
379          * @param {?Protocol.Error} error
380          * @param {!RuntimeAgent.RemoteObject} result
381          * @param {boolean=} wasThrown
382          * @this {WebInspector.RemoteObject}
383          */
384         function evaluatedCallback(error, result, wasThrown)
385         {
386             if (error || wasThrown) {
387                 callback(error || result.description);
388                 return;
389             }
390
391             this.doSetObjectPropertyValue(result, name, callback);
392
393             if (result.objectId)
394                 this._runtimeAgent.releaseObject(result.objectId);
395         }
396     },
397
398     /**
399      * @param {!RuntimeAgent.RemoteObject} result
400      * @param {string} name
401      * @param {function(string=)} callback
402      */
403     doSetObjectPropertyValue: function(result, name, callback)
404     {
405         // This assignment may be for a regular (data) property, and for an acccessor property (with getter/setter).
406         // Note the sensitive matter about accessor property: the property may be physically defined in some proto object,
407         // but logically it is bound to the object in question. JavaScript passes this object to getters/setters, not the object
408         // where property was defined; so do we.
409         var setPropertyValueFunction = "function(a, b) { this[a] = b; }";
410
411         var argv = [{ value: name }, WebInspector.RemoteObject.toCallArgument(result)]
412         this._runtimeAgent.callFunctionOn(this._objectId, setPropertyValueFunction, argv, true, undefined, undefined, propertySetCallback);
413
414         /**
415          * @param {?Protocol.Error} error
416          * @param {!RuntimeAgent.RemoteObject} result
417          * @param {boolean=} wasThrown
418          */
419         function propertySetCallback(error, result, wasThrown)
420         {
421             if (error || wasThrown) {
422                 callback(error || result.description);
423                 return;
424             }
425             callback();
426         }
427     },
428
429     /**
430      * @param {function(?WebInspector.DOMNode)} callback
431      */
432     pushNodeToFrontend: function(callback)
433     {
434         if (this.isNode())
435             this._domModel.pushNodeToFrontend(this._objectId, callback);
436         else
437             callback(null);
438     },
439
440     highlightAsDOMNode: function()
441     {
442         this._domModel.highlightDOMNode(undefined, undefined, this._objectId);
443     },
444
445     hideDOMNodeHighlight: function()
446     {
447         this._domModel.hideDOMNodeHighlight();
448     },
449
450     /**
451      * @param {function(this:Object, ...)} functionDeclaration
452      * @param {!Array.<!RuntimeAgent.CallArgument>=} args
453      * @param {function(?WebInspector.RemoteObject, boolean=)=} callback
454      */
455     callFunction: function(functionDeclaration, args, callback)
456     {
457         /**
458          * @param {?Protocol.Error} error
459          * @param {!RuntimeAgent.RemoteObject} result
460          * @param {boolean=} wasThrown
461          * @this {WebInspector.RemoteObjectImpl}
462          */
463         function mycallback(error, result, wasThrown)
464         {
465             if (!callback)
466                 return;
467             if (error)
468                 callback(null, false);
469             else
470                 callback(this.target().runtimeModel.createRemoteObject(result), wasThrown);
471         }
472
473         this._runtimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, undefined, mycallback.bind(this));
474     },
475
476     /**
477      * @param {function(this:Object)} functionDeclaration
478      * @param {!Array.<!RuntimeAgent.CallArgument>|undefined} args
479      * @param {function(*)} callback
480      */
481     callFunctionJSON: function(functionDeclaration, args, callback)
482     {
483         /**
484          * @param {?Protocol.Error} error
485          * @param {!RuntimeAgent.RemoteObject} result
486          * @param {boolean=} wasThrown
487          */
488         function mycallback(error, result, wasThrown)
489         {
490             callback((error || wasThrown) ? null : result.value);
491         }
492
493         this._runtimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, false, mycallback);
494     },
495
496     release: function()
497     {
498         if (!this._objectId)
499             return;
500         this._runtimeAgent.releaseObject(this._objectId);
501     },
502
503     /**
504      * @return {number}
505      */
506     arrayLength: function()
507     {
508         if (this.subtype !== "array")
509             return 0;
510
511         var matches = this._description.match(/\[([0-9]+)\]/);
512         if (!matches)
513             return 0;
514         return parseInt(matches[1], 10);
515     },
516
517     /**
518      * @return {!WebInspector.Target}
519      */
520     target: function()
521     {
522         return this._target;
523     },
524
525     /**
526      * @return {boolean}
527      */
528     isNode: function()
529     {
530         return !!this._objectId && this.type === "object" && this.subtype === "node";
531     },
532
533     /**
534      * @param {function(?DebuggerAgent.FunctionDetails)} callback
535      */
536     functionDetails: function(callback)
537     {
538         this._target.debuggerModel.functionDetails(this, callback)
539     },
540
541     __proto__: WebInspector.RemoteObject.prototype
542 };
543
544
545 /**
546  * @param {!WebInspector.RemoteObject} object
547  * @param {boolean} flattenProtoChain
548  * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
549  */
550 WebInspector.RemoteObject.loadFromObject = function(object, flattenProtoChain, callback)
551 {
552     if (flattenProtoChain)
553         object.getAllProperties(false, callback);
554     else
555         WebInspector.RemoteObject.loadFromObjectPerProto(object, callback);
556 };
557
558 /**
559  * @param {!WebInspector.RemoteObject} object
560  * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
561  */
562 WebInspector.RemoteObject.loadFromObjectPerProto = function(object, callback)
563 {
564     // Combines 2 asynch calls. Doesn't rely on call-back orders (some calls may be loop-back).
565     var savedOwnProperties;
566     var savedAccessorProperties;
567     var savedInternalProperties;
568     var resultCounter = 2;
569
570     function processCallback()
571     {
572         if (--resultCounter)
573             return;
574         if (savedOwnProperties && savedAccessorProperties) {
575             var combinedList = savedAccessorProperties.slice(0);
576             for (var i = 0; i < savedOwnProperties.length; i++) {
577                 var property = savedOwnProperties[i];
578                 if (!property.isAccessorProperty())
579                     combinedList.push(property);
580             }
581             return callback(combinedList, savedInternalProperties ? savedInternalProperties : null);
582         } else {
583             callback(null, null);
584         }
585     }
586
587     /**
588      * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
589      * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
590      */
591     function allAccessorPropertiesCallback(properties, internalProperties)
592     {
593         savedAccessorProperties = properties;
594         processCallback();
595     }
596
597     /**
598      * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
599      * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
600      */
601     function ownPropertiesCallback(properties, internalProperties)
602     {
603         savedOwnProperties = properties;
604         savedInternalProperties = internalProperties;
605         processCallback();
606     }
607
608     object.getAllProperties(true, allAccessorPropertiesCallback);
609     object.getOwnProperties(ownPropertiesCallback);
610 };
611
612
613 /**
614  * @constructor
615  * @extends {WebInspector.RemoteObjectImpl}
616  * @param {!WebInspector.Target} target
617  * @param {string|undefined} objectId
618  * @param {!WebInspector.ScopeRef} scopeRef
619  * @param {string} type
620  * @param {string|undefined} subtype
621  * @param {*} value
622  * @param {string=} description
623  * @param {!RuntimeAgent.ObjectPreview=} preview
624  */
625 WebInspector.ScopeRemoteObject = function(target, objectId, scopeRef, type, subtype, value, description, preview)
626 {
627     WebInspector.RemoteObjectImpl.call(this, target, objectId, type, subtype, value, description, preview);
628     this._scopeRef = scopeRef;
629     this._savedScopeProperties = undefined;
630     this._debuggerAgent = target.debuggerAgent();
631 };
632
633 WebInspector.ScopeRemoteObject.prototype = {
634     /**
635      * @param {boolean} ownProperties
636      * @param {boolean} accessorPropertiesOnly
637      * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
638      * @override
639      */
640     doGetProperties: function(ownProperties, accessorPropertiesOnly, callback)
641     {
642         if (accessorPropertiesOnly) {
643             callback([], []);
644             return;
645         }
646         if (this._savedScopeProperties) {
647             // No need to reload scope variables, as the remote object never
648             // changes its properties. If variable is updated, the properties
649             // array is patched locally.
650             callback(this._savedScopeProperties.slice(), []);
651             return;
652         }
653
654         /**
655          * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
656          * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
657          * @this {WebInspector.ScopeRemoteObject}
658          */
659         function wrappedCallback(properties, internalProperties)
660         {
661             if (this._scopeRef && properties instanceof Array)
662                 this._savedScopeProperties = properties.slice();
663             callback(properties, internalProperties);
664         }
665
666         WebInspector.RemoteObjectImpl.prototype.doGetProperties.call(this, ownProperties, accessorPropertiesOnly, wrappedCallback.bind(this));
667     },
668
669     /**
670      * @override
671      * @param {!RuntimeAgent.RemoteObject} result
672      * @param {string} name
673      * @param {function(string=)} callback
674      */
675     doSetObjectPropertyValue: function(result, name, callback)
676     {
677         this._debuggerAgent.setVariableValue(this._scopeRef.number, name, WebInspector.RemoteObject.toCallArgument(result), this._scopeRef.callFrameId, this._scopeRef.functionId, setVariableValueCallback.bind(this));
678
679         /**
680          * @param {?Protocol.Error} error
681          * @this {WebInspector.ScopeRemoteObject}
682          */
683         function setVariableValueCallback(error)
684         {
685             if (error) {
686                 callback(error);
687                 return;
688             }
689             if (this._savedScopeProperties) {
690                 for (var i = 0; i < this._savedScopeProperties.length; i++) {
691                     if (this._savedScopeProperties[i].name === name)
692                         this._savedScopeProperties[i].value = this._target.runtimeModel.createRemoteObject(result);
693                 }
694             }
695             callback();
696         }
697     },
698
699     __proto__: WebInspector.RemoteObjectImpl.prototype
700 };
701
702 /**
703  * Either callFrameId or functionId (exactly one) must be defined.
704  * @constructor
705  * @param {number} number
706  * @param {string=} callFrameId
707  * @param {string=} functionId
708  */
709 WebInspector.ScopeRef = function(number, callFrameId, functionId)
710 {
711     this.number = number;
712     this.callFrameId = callFrameId;
713     this.functionId = functionId;
714 }
715
716 /**
717  * @constructor
718  * @param {string} name
719  * @param {?WebInspector.RemoteObject} value
720  * @param {boolean=} enumerable
721  * @param {boolean=} writable
722  * @param {boolean=} isOwn
723  * @param {boolean=} wasThrown
724  */
725 WebInspector.RemoteObjectProperty = function(name, value, enumerable, writable, isOwn, wasThrown)
726 {
727     this.name = name;
728     if (value !== null)
729         this.value = value;
730     this.enumerable = typeof enumerable !== "undefined" ? enumerable : true;
731     this.writable = typeof writable !== "undefined" ? writable : true;
732     this.isOwn = !!isOwn;
733     this.wasThrown = !!wasThrown;
734 }
735
736 WebInspector.RemoteObjectProperty.prototype = {
737     /**
738      * @return {boolean}
739      */
740     isAccessorProperty: function()
741     {
742         return !!(this.getter || this.setter);
743     }
744 };
745
746 // Below is a wrapper around a local object that implements the RemoteObject interface,
747 // which can be used by the UI code (primarily ObjectPropertiesSection).
748 // Note that only JSON-compliant objects are currently supported, as there's no provision
749 // for traversing prototypes, extracting class names via constructor, handling properties
750 // or functions.
751
752 /**
753  * @constructor
754  * @extends {WebInspector.RemoteObject}
755  * @param {*} value
756  */
757 WebInspector.LocalJSONObject = function(value)
758 {
759     WebInspector.RemoteObject.call(this);
760     this._value = value;
761 }
762
763 WebInspector.LocalJSONObject.prototype = {
764     /**
765      * @return {string}
766      */
767     get description()
768     {
769         if (this._cachedDescription)
770             return this._cachedDescription;
771
772         /**
773          * @param {!WebInspector.RemoteObjectProperty} property
774          */
775         function formatArrayItem(property)
776         {
777             return property.value.description;
778         }
779
780         /**
781          * @param {!WebInspector.RemoteObjectProperty} property
782          */
783         function formatObjectItem(property)
784         {
785             return property.name + ":" + property.value.description;
786         }
787
788         if (this.type === "object") {
789             switch (this.subtype) {
790             case "array":
791                 this._cachedDescription = this._concatenate("[", "]", formatArrayItem);
792                 break;
793             case "date":
794                 this._cachedDescription = "" + this._value;
795                 break;
796             case "null":
797                 this._cachedDescription = "null";
798                 break;
799             default:
800                 this._cachedDescription = this._concatenate("{", "}", formatObjectItem);
801             }
802         } else
803             this._cachedDescription = String(this._value);
804
805         return this._cachedDescription;
806     },
807
808     /**
809      * @param {string} prefix
810      * @param {string} suffix
811      * @param {function (!WebInspector.RemoteObjectProperty)} formatProperty
812      * @return {string}
813      */
814     _concatenate: function(prefix, suffix, formatProperty)
815     {
816         const previewChars = 100;
817
818         var buffer = prefix;
819         var children = this._children();
820         for (var i = 0; i < children.length; ++i) {
821             var itemDescription = formatProperty(children[i]);
822             if (buffer.length + itemDescription.length > previewChars) {
823                 buffer += ",\u2026";
824                 break;
825             }
826             if (i)
827                 buffer += ", ";
828             buffer += itemDescription;
829         }
830         buffer += suffix;
831         return buffer;
832     },
833
834     /**
835      * @return {string}
836      */
837     get type()
838     {
839         return typeof this._value;
840     },
841
842     /**
843      * @return {string|undefined}
844      */
845     get subtype()
846     {
847         if (this._value === null)
848             return "null";
849
850         if (this._value instanceof Array)
851             return "array";
852
853         if (this._value instanceof Date)
854             return "date";
855
856         return undefined;
857     },
858
859     /**
860      * @return {boolean}
861      */
862     get hasChildren()
863     {
864         if ((typeof this._value !== "object") || (this._value === null))
865             return false;
866         return !!Object.keys(/** @type {!Object} */ (this._value)).length;
867     },
868
869     /**
870      * @param {function(!Array.<!WebInspector.RemoteObjectProperty>)} callback
871      */
872     getOwnProperties: function(callback)
873     {
874         callback(this._children());
875     },
876
877     /**
878      * @param {boolean} accessorPropertiesOnly
879      * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
880      */
881     getAllProperties: function(accessorPropertiesOnly, callback)
882     {
883         if (accessorPropertiesOnly)
884             callback([], null);
885         else
886             callback(this._children(), null);
887     },
888
889     /**
890      * @return {!Array.<!WebInspector.RemoteObjectProperty>}
891      */
892     _children: function()
893     {
894         if (!this.hasChildren)
895             return [];
896         var value = /** @type {!Object} */ (this._value);
897
898         /**
899          * @param {string} propName
900          * @this {WebInspector.LocalJSONObject}
901          */
902         function buildProperty(propName)
903         {
904             return new WebInspector.RemoteObjectProperty(propName, new WebInspector.LocalJSONObject(this._value[propName]));
905         }
906         if (!this._cachedChildren)
907             this._cachedChildren = Object.keys(value).map(buildProperty.bind(this));
908         return this._cachedChildren;
909     },
910
911     /**
912      * @return {boolean}
913      */
914     isError: function()
915     {
916         return false;
917     },
918
919     /**
920      * @return {number}
921      */
922     arrayLength: function()
923     {
924         return this._value instanceof Array ? this._value.length : 0;
925     },
926
927     /**
928      * @param {function(this:Object, ...)} functionDeclaration
929      * @param {!Array.<!RuntimeAgent.CallArgument>=} args
930      * @param {function(?WebInspector.RemoteObject, boolean=)=} callback
931      */
932     callFunction: function(functionDeclaration, args, callback)
933     {
934         var target = /** @type {?Object} */ (this._value);
935         var rawArgs = args ? args.map(function(arg) {return arg.value;}) : [];
936
937         var result;
938         var wasThrown = false;
939         try {
940             result = functionDeclaration.apply(target, rawArgs);
941         } catch (e) {
942             wasThrown = true;
943         }
944
945         if (!callback)
946             return;
947         callback(WebInspector.RemoteObject.fromLocalObject(result), wasThrown);
948     },
949
950     /**
951      * @param {function(this:Object)} functionDeclaration
952      * @param {!Array.<!RuntimeAgent.CallArgument>|undefined} args
953      * @param {function(*)} callback
954      */
955     callFunctionJSON: function(functionDeclaration, args, callback)
956     {
957         var target = /** @type {?Object} */ (this._value);
958         var rawArgs = args ? args.map(function(arg) {return arg.value;}) : [];
959
960         var result;
961         try {
962             result = functionDeclaration.apply(target, rawArgs);
963         } catch (e) {
964             result = null;
965         }
966
967         callback(result);
968     },
969
970     __proto__: WebInspector.RemoteObject.prototype
971 }