Home | History | Annotate | Download | only in sdk
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * This may not be an interface due to "instanceof WebInspector.RemoteObject" checks in the code.
     33  *
     34  * @constructor
     35  */
     36 WebInspector.RemoteObject = function() { }
     37 
     38 WebInspector.RemoteObject.prototype = {
     39     /** @return {string} */
     40     get type()
     41     {
     42         throw "Not implemented";
     43     },
     44 
     45     /** @return {string|undefined} */
     46     get subtype()
     47     {
     48         throw "Not implemented";
     49     },
     50 
     51     /** @return {string|undefined} */
     52     get description()
     53     {
     54         throw "Not implemented";
     55     },
     56 
     57     /** @return {boolean} */
     58     get hasChildren()
     59     {
     60         throw "Not implemented";
     61     },
     62 
     63     /**
     64      * @return {number}
     65      */
     66     arrayLength: function()
     67     {
     68         throw "Not implemented";
     69     },
     70 
     71     /**
     72      * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
     73      */
     74     getOwnProperties: function(callback)
     75     {
     76         throw "Not implemented";
     77     },
     78 
     79     /**
     80      * @param {boolean} accessorPropertiesOnly
     81      * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback
     82      */
     83     getAllProperties: function(accessorPropertiesOnly, callback)
     84     {
     85         throw "Not implemented";
     86     },
     87 
     88     /**
     89      * @param {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  */
    151 WebInspector.RemoteObject.fromLocalObject = function(value)
    152 {
    153     return new WebInspector.LocalJSONObject(value);
    154 }
    155 
    156 /**
    157  * @param {!WebInspector.RemoteObject} remoteObject
    158  * @return {string}
    159  */
    160 WebInspector.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  */
    176 WebInspector.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  */
    211 WebInspector.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 
    240 WebInspector.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  */
    593 WebInspector.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  */
    605 WebInspector.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  */
    668 WebInspector.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 
    676 WebInspector.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  */
    752 WebInspector.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  */
    769 WebInspector.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 
    782 WebInspector.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  */
    803 WebInspector.LocalJSONObject = function(value)
    804 {
    805     WebInspector.RemoteObject.call(this);
    806     this._value = value;
    807 }
    808 
    809 WebInspector.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