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 {!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  */
    154 WebInspector.RemoteObject.fromLocalObject = function(value)
    155 {
    156     return new WebInspector.LocalJSONObject(value);
    157 }
    158 
    159 /**
    160  * @param {!WebInspector.RemoteObject} remoteObject
    161  * @return {string}
    162  */
    163 WebInspector.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  */
    179 WebInspector.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  */
    235 WebInspector.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 
    264 WebInspector.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  */
    644 WebInspector.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  */
    656 WebInspector.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  */
    719 WebInspector.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 
    727 WebInspector.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  */
    803 WebInspector.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  */
    820 WebInspector.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 
    833 WebInspector.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  */
    854 WebInspector.LocalJSONObject = function(value)
    855 {
    856     WebInspector.RemoteObject.call(this);
    857     this._value = value;
    858 }
    859 
    860 WebInspector.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  */
   1100 WebInspector.MapEntryLocalJSONObject = function(value)
   1101 {
   1102     WebInspector.LocalJSONObject.call(this, value);
   1103 }
   1104 
   1105 WebInspector.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