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