• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 * @constructor
33 * @param {string|undefined} objectId
34 * @param {string} type
35 * @param {string|undefined} subtype
36 * @param {*} value
37 * @param {string=} description
38 * @param {!RuntimeAgent.ObjectPreview=} preview
39 */
40WebInspector.RemoteObject = function(objectId, type, subtype, value, description, preview)
41{
42    this._type = type;
43    this._subtype = subtype;
44    if (objectId) {
45        // handle
46        this._objectId = objectId;
47        this._description = description;
48        this._hasChildren = true;
49        this._preview = preview;
50    } else {
51        // Primitive or null object.
52        console.assert(type !== "object" || value === null);
53        this._description = description || (value + "");
54        this._hasChildren = false;
55        this.value = value;
56    }
57}
58
59/**
60 * @param {number|string|boolean} value
61 * @return {!WebInspector.RemoteObject}
62 */
63WebInspector.RemoteObject.fromPrimitiveValue = function(value)
64{
65    return new WebInspector.RemoteObject(undefined, typeof value, undefined, value);
66}
67
68/**
69 * @param {*} value
70 * @return {!WebInspector.RemoteObject}
71 */
72WebInspector.RemoteObject.fromLocalObject = function(value)
73{
74    return new WebInspector.LocalJSONObject(value);
75}
76
77/**
78 * @param {!WebInspector.DOMNode} node
79 * @param {string} objectGroup
80 * @param {function(?WebInspector.RemoteObject)} callback
81 */
82WebInspector.RemoteObject.resolveNode = function(node, objectGroup, callback)
83{
84    /**
85     * @param {?Protocol.Error} error
86     * @param {!RuntimeAgent.RemoteObject} object
87     */
88    function mycallback(error, object)
89    {
90        if (!callback)
91            return;
92
93        if (error || !object)
94            callback(null);
95        else
96            callback(WebInspector.RemoteObject.fromPayload(object));
97    }
98    DOMAgent.resolveNode(node.id, objectGroup, mycallback);
99}
100
101/**
102 * @param {!RuntimeAgent.RemoteObject} payload
103 * @return {!WebInspector.RemoteObject}
104 */
105WebInspector.RemoteObject.fromPayload = function(payload)
106{
107    console.assert(typeof payload === "object", "Remote object payload should only be an object");
108
109    return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
110}
111
112/**
113 * @param {!WebInspector.RemoteObject} remoteObject
114 * @return {string}
115 */
116WebInspector.RemoteObject.type = function(remoteObject)
117{
118    if (remoteObject === null)
119        return "null";
120
121    var type = typeof remoteObject;
122    if (type !== "object" && type !== "function")
123        return type;
124
125    return remoteObject.type;
126}
127
128WebInspector.RemoteObject.prototype = {
129    /** @return {!RuntimeAgent.RemoteObjectId} */
130    get objectId()
131    {
132        return this._objectId;
133    },
134
135    /** @return {string} */
136    get type()
137    {
138        return this._type;
139    },
140
141    /** @return {string|undefined} */
142    get subtype()
143    {
144        return this._subtype;
145    },
146
147    /** @return {string|undefined} */
148    get description()
149    {
150        return this._description;
151    },
152
153    /** @return {boolean} */
154    get hasChildren()
155    {
156        return this._hasChildren;
157    },
158
159    /** @return {!RuntimeAgent.ObjectPreview|undefined} */
160    get preview()
161    {
162        return this._preview;
163    },
164
165    /**
166     * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
167     */
168    getOwnProperties: function(callback)
169    {
170        this.doGetProperties(true, false, callback);
171    },
172
173    /**
174     * @param {boolean} accessorPropertiesOnly
175     * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
176     */
177    getAllProperties: function(accessorPropertiesOnly, callback)
178    {
179        this.doGetProperties(false, accessorPropertiesOnly, callback);
180    },
181
182    /**
183     * @param {!Array.<string>} propertyPath
184     * @param {function(?WebInspector.RemoteObject, boolean=)} callback
185     */
186    getProperty: function(propertyPath, callback)
187    {
188        /**
189         * @param {string} arrayStr
190         * @this {Object}
191         */
192        function remoteFunction(arrayStr)
193        {
194            var result = this;
195            var properties = JSON.parse(arrayStr);
196            for (var i = 0, n = properties.length; i < n; ++i)
197                result = result[properties[i]];
198            return result;
199        }
200
201        var args = [{ value: JSON.stringify(propertyPath) }];
202        this.callFunction(remoteFunction, args, callback);
203    },
204
205    /**
206     * @param {boolean} ownProperties
207     * @param {boolean} accessorPropertiesOnly
208     * @param {?function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
209     */
210    doGetProperties: function(ownProperties, accessorPropertiesOnly, callback)
211    {
212        if (!this._objectId) {
213            callback(null, null);
214            return;
215        }
216
217        /**
218         * @param {?Protocol.Error} error
219         * @param {!Array.<!RuntimeAgent.PropertyDescriptor>} properties
220         * @param {!Array.<!RuntimeAgent.InternalPropertyDescriptor>=} internalProperties
221         */
222        function remoteObjectBinder(error, properties, internalProperties)
223        {
224            if (error) {
225                callback(null, null);
226                return;
227            }
228            var result = [];
229            for (var i = 0; properties && i < properties.length; ++i) {
230                var property = properties[i];
231                result.push(new WebInspector.RemoteObjectProperty(property.name, null, property));
232            }
233            var internalPropertiesResult = null;
234            if (internalProperties) {
235                internalPropertiesResult = [];
236                for (var i = 0; i < internalProperties.length; i++) {
237                    var property = internalProperties[i];
238                    if (!property.value)
239                        continue;
240                    internalPropertiesResult.push(new WebInspector.RemoteObjectProperty(property.name, WebInspector.RemoteObject.fromPayload(property.value)));
241                }
242            }
243            callback(result, internalPropertiesResult);
244        }
245        RuntimeAgent.getProperties(this._objectId, ownProperties, accessorPropertiesOnly, remoteObjectBinder);
246    },
247
248    /**
249     * @param {string} name
250     * @param {string} value
251     * @param {function(string=)} callback
252     */
253    setPropertyValue: function(name, value, callback)
254    {
255        if (!this._objectId) {
256            callback("Can't set a property of non-object.");
257            return;
258        }
259
260        RuntimeAgent.evaluate.invoke({expression:value, doNotPauseOnExceptionsAndMuteConsole:true}, evaluatedCallback.bind(this));
261
262        /**
263         * @param {?Protocol.Error} error
264         * @param {!RuntimeAgent.RemoteObject} result
265         * @param {boolean=} wasThrown
266         * @this {WebInspector.RemoteObject}
267         */
268        function evaluatedCallback(error, result, wasThrown)
269        {
270            if (error || wasThrown) {
271                callback(error || result.description);
272                return;
273            }
274
275            this.doSetObjectPropertyValue(result, name, callback);
276
277            if (result.objectId)
278                RuntimeAgent.releaseObject(result.objectId);
279        }
280    },
281
282    /**
283     * @param {!RuntimeAgent.RemoteObject} result
284     * @param {string} name
285     * @param {function(string=)} callback
286     */
287    doSetObjectPropertyValue: function(result, name, callback)
288    {
289        // This assignment may be for a regular (data) property, and for an acccessor property (with getter/setter).
290        // Note the sensitive matter about accessor property: the property may be physically defined in some proto object,
291        // but logically it is bound to the object in question. JavaScript passes this object to getters/setters, not the object
292        // where property was defined; so do we.
293        var setPropertyValueFunction = "function(a, b) { this[a] = b; }";
294
295        // Special case for NaN, Infinity, -Infinity, -0.
296        if (result.type === "number" && String(result.value) !== result.description)
297            setPropertyValueFunction = "function(a) { this[a] = " + result.description + "; }";
298
299        delete result.description; // Optimize on traffic.
300        RuntimeAgent.callFunctionOn(this._objectId, setPropertyValueFunction, [{ value:name }, result], true, undefined, undefined, propertySetCallback.bind(this));
301
302        /**
303         * @param {?Protocol.Error} error
304         * @param {!RuntimeAgent.RemoteObject} result
305         * @param {boolean=} wasThrown
306         */
307        function propertySetCallback(error, result, wasThrown)
308        {
309            if (error || wasThrown) {
310                callback(error || result.description);
311                return;
312            }
313            callback();
314        }
315    },
316
317    /**
318     * @param {function(?DOMAgent.NodeId)} callback
319     */
320    pushNodeToFrontend: function(callback)
321    {
322        if (this._objectId)
323            WebInspector.domAgent.pushNodeToFrontend(this._objectId, callback);
324        else
325            callback(0);
326    },
327
328    highlightAsDOMNode: function()
329    {
330        WebInspector.domAgent.highlightDOMNode(undefined, undefined, this._objectId);
331    },
332
333    hideDOMNodeHighlight: function()
334    {
335        WebInspector.domAgent.hideDOMNodeHighlight();
336    },
337
338    /**
339     * @param {function(this:Object, ...)} functionDeclaration
340     * @param {!Array.<!RuntimeAgent.CallArgument>=} args
341     * @param {function(?WebInspector.RemoteObject, boolean=)=} callback
342     */
343    callFunction: function(functionDeclaration, args, callback)
344    {
345        /**
346         * @param {?Protocol.Error} error
347         * @param {!RuntimeAgent.RemoteObject} result
348         * @param {boolean=} wasThrown
349         */
350        function mycallback(error, result, wasThrown)
351        {
352            if (!callback)
353                return;
354            if (error)
355                callback(null, false);
356            else
357                callback(WebInspector.RemoteObject.fromPayload(result), wasThrown);
358        }
359
360        RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, undefined, mycallback);
361    },
362
363    /**
364     * @param {function(this:Object)} functionDeclaration
365     * @param {!Array.<!RuntimeAgent.CallArgument>|undefined} args
366     * @param {function(*)} callback
367     */
368    callFunctionJSON: function(functionDeclaration, args, callback)
369    {
370        /**
371         * @param {?Protocol.Error} error
372         * @param {!RuntimeAgent.RemoteObject} result
373         * @param {boolean=} wasThrown
374         */
375        function mycallback(error, result, wasThrown)
376        {
377            callback((error || wasThrown) ? null : result.value);
378        }
379
380        RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, false, mycallback);
381    },
382
383    release: function()
384    {
385        if (!this._objectId)
386            return;
387        RuntimeAgent.releaseObject(this._objectId);
388    },
389
390    /**
391     * @return {number}
392     */
393    arrayLength: function()
394    {
395        if (this.subtype !== "array")
396            return 0;
397
398        var matches = this._description.match(/\[([0-9]+)\]/);
399        if (!matches)
400            return 0;
401        return parseInt(matches[1], 10);
402    }
403};
404
405
406/**
407 * @param {!WebInspector.RemoteObject} object
408 * @param {boolean} flattenProtoChain
409 * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
410 */
411WebInspector.RemoteObject.loadFromObject = function(object, flattenProtoChain, callback)
412{
413    if (flattenProtoChain)
414       object.getAllProperties(false, callback);
415    else
416        WebInspector.RemoteObject.loadFromObjectPerProto(object, callback);
417};
418
419/**
420 * @param {!WebInspector.RemoteObject} object
421 * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
422 */
423WebInspector.RemoteObject.loadFromObjectPerProto = function(object, callback)
424{
425    // Combines 2 asynch calls. Doesn't rely on call-back orders (some calls may be loop-back).
426    var savedOwnProperties;
427    var savedAccessorProperties;
428    var savedInternalProperties;
429    var resultCounter = 2;
430
431    function processCallback()
432    {
433        if (--resultCounter)
434            return;
435        if (savedOwnProperties && savedAccessorProperties) {
436            var combinedList = savedAccessorProperties.slice(0);
437            for (var i = 0; i < savedOwnProperties.length; i++) {
438                var property = savedOwnProperties[i];
439                if (!property.isAccessorProperty())
440                    combinedList.push(property);
441            }
442            return callback(combinedList, savedInternalProperties ? savedInternalProperties : null);
443        } else {
444            callback(null, null);
445        }
446    }
447
448    /**
449     * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
450     * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
451     */
452    function allAccessorPropertiesCallback(properties, internalProperties)
453    {
454        savedAccessorProperties = properties;
455        processCallback();
456    }
457
458    /**
459     * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
460     * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
461     */
462    function ownPropertiesCallback(properties, internalProperties)
463    {
464        savedOwnProperties = properties;
465        savedInternalProperties = internalProperties;
466        processCallback();
467    }
468
469    object.getAllProperties(true, allAccessorPropertiesCallback);
470    object.getOwnProperties(ownPropertiesCallback);
471};
472
473
474/**
475 * @constructor
476 * @extends {WebInspector.RemoteObject}
477 * @param {string|undefined} objectId
478 * @param {!WebInspector.ScopeRef} scopeRef
479 * @param {string} type
480 * @param {string|undefined} subtype
481 * @param {*} value
482 * @param {string=} description
483 * @param {!RuntimeAgent.ObjectPreview=} preview
484 */
485WebInspector.ScopeRemoteObject = function(objectId, scopeRef, type, subtype, value, description, preview)
486{
487    WebInspector.RemoteObject.call(this, objectId, type, subtype, value, description, preview);
488    this._scopeRef = scopeRef;
489    this._savedScopeProperties = undefined;
490};
491
492/**
493 * @param {!RuntimeAgent.RemoteObject} payload
494 * @param {!WebInspector.ScopeRef=} scopeRef
495 * @return {!WebInspector.RemoteObject}
496 */
497WebInspector.ScopeRemoteObject.fromPayload = function(payload, scopeRef)
498{
499    if (scopeRef)
500        return new WebInspector.ScopeRemoteObject(payload.objectId, scopeRef, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
501    else
502        return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
503}
504
505WebInspector.ScopeRemoteObject.prototype = {
506    /**
507     * @param {boolean} ownProperties
508     * @param {boolean} accessorPropertiesOnly
509     * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
510     * @override
511     */
512    doGetProperties: function(ownProperties, accessorPropertiesOnly, callback)
513    {
514        if (accessorPropertiesOnly) {
515            callback([], []);
516            return;
517        }
518        if (this._savedScopeProperties) {
519            // No need to reload scope variables, as the remote object never
520            // changes its properties. If variable is updated, the properties
521            // array is patched locally.
522            callback(this._savedScopeProperties.slice(), []);
523            return;
524        }
525
526        /**
527         * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
528         * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
529         * @this {WebInspector.ScopeRemoteObject}
530         */
531        function wrappedCallback(properties, internalProperties)
532        {
533            if (this._scopeRef && properties instanceof Array)
534                this._savedScopeProperties = properties.slice();
535            callback(properties, internalProperties);
536        }
537
538        WebInspector.RemoteObject.prototype.doGetProperties.call(this, ownProperties, accessorPropertiesOnly, wrappedCallback.bind(this));
539    },
540
541    /**
542     * @override
543     * @param {!RuntimeAgent.RemoteObject} result
544     * @param {string} name
545     * @param {function(string=)} callback
546     */
547    doSetObjectPropertyValue: function(result, name, callback)
548    {
549        var newValue;
550
551        switch (result.type) {
552            case "undefined":
553                newValue = {};
554                break;
555            case "object":
556            case "function":
557                newValue = { objectId: result.objectId };
558                break;
559            default:
560                newValue = { value: result.value };
561        }
562
563        DebuggerAgent.setVariableValue(this._scopeRef.number, name, newValue, this._scopeRef.callFrameId, this._scopeRef.functionId, setVariableValueCallback.bind(this));
564
565        /**
566         * @param {?Protocol.Error} error
567         * @this {WebInspector.ScopeRemoteObject}
568         */
569        function setVariableValueCallback(error)
570        {
571            if (error) {
572                callback(error);
573                return;
574            }
575            if (this._savedScopeProperties) {
576                for (var i = 0; i < this._savedScopeProperties.length; i++) {
577                    if (this._savedScopeProperties[i].name === name)
578                        this._savedScopeProperties[i].value = WebInspector.RemoteObject.fromPayload(result);
579                }
580            }
581            callback();
582        }
583    },
584
585    __proto__: WebInspector.RemoteObject.prototype
586};
587
588/**
589 * Either callFrameId or functionId (exactly one) must be defined.
590 * @constructor
591 * @param {number} number
592 * @param {string=} callFrameId
593 * @param {string=} functionId
594 */
595WebInspector.ScopeRef = function(number, callFrameId, functionId)
596{
597    this.number = number;
598    this.callFrameId = callFrameId;
599    this.functionId = functionId;
600}
601
602/**
603 * @constructor
604 * @param {string} name
605 * @param {?WebInspector.RemoteObject} value
606 * @param {!RuntimeAgent.PropertyDescriptor=} descriptor
607 */
608WebInspector.RemoteObjectProperty = function(name, value, descriptor)
609{
610    this.name = name;
611    this.enumerable = descriptor ? !!descriptor.enumerable : true;
612    this.writable = descriptor ? !!descriptor.writable : true;
613
614    if (value === null && descriptor) {
615        if (descriptor.value)
616            this.value = WebInspector.RemoteObject.fromPayload(descriptor.value)
617        if (descriptor.get && descriptor.get.type !== "undefined")
618            this.getter = WebInspector.RemoteObject.fromPayload(descriptor.get);
619        if (descriptor.set && descriptor.set.type !== "undefined")
620            this.setter = WebInspector.RemoteObject.fromPayload(descriptor.set);
621    } else {
622         this.value = value;
623    }
624
625    if (descriptor) {
626        this.isOwn = descriptor.isOwn;
627        this.wasThrown = !!descriptor.wasThrown;
628    }
629}
630
631WebInspector.RemoteObjectProperty.prototype = {
632    isAccessorProperty: function()
633    {
634        return this.getter || this.setter;
635    }
636};
637
638/**
639 * @param {string} name
640 * @param {string} value
641 * @return {!WebInspector.RemoteObjectProperty}
642 */
643WebInspector.RemoteObjectProperty.fromPrimitiveValue = function(name, value)
644{
645    return new WebInspector.RemoteObjectProperty(name, WebInspector.RemoteObject.fromPrimitiveValue(value));
646}
647
648/**
649 * @param {string} name
650 * @param {!WebInspector.RemoteObject} value
651 * @return {!WebInspector.RemoteObjectProperty}
652 */
653WebInspector.RemoteObjectProperty.fromScopeValue = function(name, value)
654{
655    var result = new WebInspector.RemoteObjectProperty(name, value);
656    result.writable = false;
657    return result;
658}
659
660// The below is a wrapper around a local object that provides an interface comaptible
661// with RemoteObject, to be used by the UI code (primarily ObjectPropertiesSection).
662// Note that only JSON-compliant objects are currently supported, as there's no provision
663// for traversing prototypes, extracting class names via constuctor, handling properties
664// or functions.
665
666/**
667 * @constructor
668 * @extends {WebInspector.RemoteObject}
669 * @param {*} value
670 */
671WebInspector.LocalJSONObject = function(value)
672{
673    this._value = value;
674}
675
676WebInspector.LocalJSONObject.prototype = {
677    /**
678     * @return {string}
679     */
680    get description()
681    {
682        if (this._cachedDescription)
683            return this._cachedDescription;
684
685        /**
686         * @param {!WebInspector.RemoteObjectProperty} property
687         */
688        function formatArrayItem(property)
689        {
690            return property.value.description;
691        }
692
693        /**
694         * @param {!WebInspector.RemoteObjectProperty} property
695         */
696        function formatObjectItem(property)
697        {
698            return property.name + ":" + property.value.description;
699        }
700
701        if (this.type === "object") {
702            switch (this.subtype) {
703            case "array":
704                this._cachedDescription = this._concatenate("[", "]", formatArrayItem);
705                break;
706            case "date":
707                this._cachedDescription = "" + this._value;
708                break;
709            case "null":
710                this._cachedDescription = "null";
711                break;
712            default:
713                this._cachedDescription = this._concatenate("{", "}", formatObjectItem);
714            }
715        } else
716            this._cachedDescription = String(this._value);
717
718        return this._cachedDescription;
719    },
720
721    /**
722     * @param {string} prefix
723     * @param {string} suffix
724     * @param {function (!WebInspector.RemoteObjectProperty)} formatProperty
725     * @return {string}
726     */
727    _concatenate: function(prefix, suffix, formatProperty)
728    {
729        const previewChars = 100;
730
731        var buffer = prefix;
732        var children = this._children();
733        for (var i = 0; i < children.length; ++i) {
734            var itemDescription = formatProperty(children[i]);
735            if (buffer.length + itemDescription.length > previewChars) {
736                buffer += ",\u2026";
737                break;
738            }
739            if (i)
740                buffer += ", ";
741            buffer += itemDescription;
742        }
743        buffer += suffix;
744        return buffer;
745    },
746
747    /**
748     * @return {string}
749     */
750    get type()
751    {
752        return typeof this._value;
753    },
754
755    /**
756     * @return {string|undefined}
757     */
758    get subtype()
759    {
760        if (this._value === null)
761            return "null";
762
763        if (this._value instanceof Array)
764            return "array";
765
766        if (this._value instanceof Date)
767            return "date";
768
769        return undefined;
770    },
771
772    /**
773     * @return {boolean}
774     */
775    get hasChildren()
776    {
777        if ((typeof this._value !== "object") || (this._value === null))
778            return false;
779        return !!Object.keys(/** @type {!Object} */ (this._value)).length;
780    },
781
782    /**
783     * @param {function(!Array.<!WebInspector.RemoteObjectProperty>)} callback
784     */
785    getOwnProperties: function(callback)
786    {
787        callback(this._children());
788    },
789
790    /**
791     * @param {boolean} accessorPropertiesOnly
792     * @param {function(!Array.<!WebInspector.RemoteObjectProperty>)} callback
793     */
794    getAllProperties: function(accessorPropertiesOnly, callback)
795    {
796        if (accessorPropertiesOnly)
797            callback([]);
798        else
799            callback(this._children());
800    },
801
802    /**
803     * @return {!Array.<!WebInspector.RemoteObjectProperty>}
804     */
805    _children: function()
806    {
807        if (!this.hasChildren)
808            return [];
809        var value = /** @type {!Object} */ (this._value);
810
811        /**
812         * @param {string} propName
813         * @this {WebInspector.LocalJSONObject}
814         */
815        function buildProperty(propName)
816        {
817            return new WebInspector.RemoteObjectProperty(propName, new WebInspector.LocalJSONObject(this._value[propName]));
818        }
819        if (!this._cachedChildren)
820            this._cachedChildren = Object.keys(value).map(buildProperty.bind(this));
821        return this._cachedChildren;
822    },
823
824    /**
825     * @return {boolean}
826     */
827    isError: function()
828    {
829        return false;
830    },
831
832    /**
833     * @return {number}
834     */
835    arrayLength: function()
836    {
837        return this._value instanceof Array ? this._value.length : 0;
838    },
839
840    /**
841     * @param {function(this:Object, ...)} functionDeclaration
842     * @param {!Array.<!RuntimeAgent.CallArgument>=} args
843     * @param {function(?WebInspector.RemoteObject, boolean=)=} callback
844     */
845    callFunction: function(functionDeclaration, args, callback)
846    {
847        var target = /** @type {?Object} */ (this._value);
848        var rawArgs = args ? args.map(function(arg) {return arg.value;}) : [];
849
850        var result;
851        var wasThrown = false;
852        try {
853            result = functionDeclaration.apply(target, rawArgs);
854        } catch (e) {
855            wasThrown = true;
856        }
857
858        if (!callback)
859            return;
860        callback(WebInspector.RemoteObject.fromLocalObject(result), wasThrown);
861    },
862
863    /**
864     * @param {function(this:Object)} functionDeclaration
865     * @param {!Array.<!RuntimeAgent.CallArgument>|undefined} args
866     * @param {function(*)} callback
867     */
868    callFunctionJSON: function(functionDeclaration, args, callback)
869    {
870        var target = /** @type {?Object} */ (this._value);
871        var rawArgs = args ? args.map(function(arg) {return arg.value;}) : [];
872
873        var result;
874        try {
875            result = functionDeclaration.apply(target, rawArgs);
876        } catch (e) {
877            result = null;
878        }
879
880        callback(result);
881    }
882}
883