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