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