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 */ 40WebInspector.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 */ 63WebInspector.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 */ 72WebInspector.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 */ 82WebInspector.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 */ 105WebInspector.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 */ 116WebInspector.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 128WebInspector.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 {!Array.<string>} propertyPath 184 * @param {function(?WebInspector.RemoteObject, boolean=)} callback 185 */ 186 getProperty: function(propertyPath, callback) 187 { 188 /** 189 * @param {string} arrayStr 190 * @this {Object} 191 */ 192 function remoteFunction(arrayStr) 193 { 194 var result = this; 195 var properties = JSON.parse(arrayStr); 196 for (var i = 0, n = properties.length; i < n; ++i) 197 result = result[properties[i]]; 198 return result; 199 } 200 201 var args = [{ value: JSON.stringify(propertyPath) }]; 202 this.callFunction(remoteFunction, args, callback); 203 }, 204 205 /** 206 * @param {boolean} ownProperties 207 * @param {boolean} accessorPropertiesOnly 208 * @param {?function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback 209 */ 210 doGetProperties: function(ownProperties, accessorPropertiesOnly, callback) 211 { 212 if (!this._objectId) { 213 callback(null, null); 214 return; 215 } 216 217 /** 218 * @param {?Protocol.Error} error 219 * @param {!Array.<!RuntimeAgent.PropertyDescriptor>} properties 220 * @param {!Array.<!RuntimeAgent.InternalPropertyDescriptor>=} internalProperties 221 */ 222 function remoteObjectBinder(error, properties, internalProperties) 223 { 224 if (error) { 225 callback(null, null); 226 return; 227 } 228 var result = []; 229 for (var i = 0; properties && i < properties.length; ++i) { 230 var property = properties[i]; 231 result.push(new WebInspector.RemoteObjectProperty(property.name, null, property)); 232 } 233 var internalPropertiesResult = null; 234 if (internalProperties) { 235 internalPropertiesResult = []; 236 for (var i = 0; i < internalProperties.length; i++) { 237 var property = internalProperties[i]; 238 if (!property.value) 239 continue; 240 internalPropertiesResult.push(new WebInspector.RemoteObjectProperty(property.name, WebInspector.RemoteObject.fromPayload(property.value))); 241 } 242 } 243 callback(result, internalPropertiesResult); 244 } 245 RuntimeAgent.getProperties(this._objectId, ownProperties, accessorPropertiesOnly, remoteObjectBinder); 246 }, 247 248 /** 249 * @param {string} name 250 * @param {string} value 251 * @param {function(string=)} callback 252 */ 253 setPropertyValue: function(name, value, callback) 254 { 255 if (!this._objectId) { 256 callback("Can't set a property of non-object."); 257 return; 258 } 259 260 RuntimeAgent.evaluate.invoke({expression:value, doNotPauseOnExceptionsAndMuteConsole:true}, evaluatedCallback.bind(this)); 261 262 /** 263 * @param {?Protocol.Error} error 264 * @param {!RuntimeAgent.RemoteObject} result 265 * @param {boolean=} wasThrown 266 * @this {WebInspector.RemoteObject} 267 */ 268 function evaluatedCallback(error, result, wasThrown) 269 { 270 if (error || wasThrown) { 271 callback(error || result.description); 272 return; 273 } 274 275 this.doSetObjectPropertyValue(result, name, callback); 276 277 if (result.objectId) 278 RuntimeAgent.releaseObject(result.objectId); 279 } 280 }, 281 282 /** 283 * @param {!RuntimeAgent.RemoteObject} result 284 * @param {string} name 285 * @param {function(string=)} callback 286 */ 287 doSetObjectPropertyValue: function(result, name, callback) 288 { 289 // This assignment may be for a regular (data) property, and for an acccessor property (with getter/setter). 290 // Note the sensitive matter about accessor property: the property may be physically defined in some proto object, 291 // but logically it is bound to the object in question. JavaScript passes this object to getters/setters, not the object 292 // where property was defined; so do we. 293 var setPropertyValueFunction = "function(a, b) { this[a] = b; }"; 294 295 // Special case for NaN, Infinity, -Infinity, -0. 296 if (result.type === "number" && String(result.value) !== result.description) 297 setPropertyValueFunction = "function(a) { this[a] = " + result.description + "; }"; 298 299 delete result.description; // Optimize on traffic. 300 RuntimeAgent.callFunctionOn(this._objectId, setPropertyValueFunction, [{ value:name }, result], true, undefined, undefined, propertySetCallback.bind(this)); 301 302 /** 303 * @param {?Protocol.Error} error 304 * @param {!RuntimeAgent.RemoteObject} result 305 * @param {boolean=} wasThrown 306 */ 307 function propertySetCallback(error, result, wasThrown) 308 { 309 if (error || wasThrown) { 310 callback(error || result.description); 311 return; 312 } 313 callback(); 314 } 315 }, 316 317 /** 318 * @param {function(?DOMAgent.NodeId)} callback 319 */ 320 pushNodeToFrontend: function(callback) 321 { 322 if (this._objectId) 323 WebInspector.domAgent.pushNodeToFrontend(this._objectId, callback); 324 else 325 callback(0); 326 }, 327 328 highlightAsDOMNode: function() 329 { 330 WebInspector.domAgent.highlightDOMNode(undefined, undefined, this._objectId); 331 }, 332 333 hideDOMNodeHighlight: function() 334 { 335 WebInspector.domAgent.hideDOMNodeHighlight(); 336 }, 337 338 /** 339 * @param {function(this:Object, ...)} functionDeclaration 340 * @param {!Array.<!RuntimeAgent.CallArgument>=} args 341 * @param {function(?WebInspector.RemoteObject, boolean=)=} callback 342 */ 343 callFunction: function(functionDeclaration, args, callback) 344 { 345 /** 346 * @param {?Protocol.Error} error 347 * @param {!RuntimeAgent.RemoteObject} result 348 * @param {boolean=} wasThrown 349 */ 350 function mycallback(error, result, wasThrown) 351 { 352 if (!callback) 353 return; 354 if (error) 355 callback(null, false); 356 else 357 callback(WebInspector.RemoteObject.fromPayload(result), wasThrown); 358 } 359 360 RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, undefined, mycallback); 361 }, 362 363 /** 364 * @param {function(this:Object)} functionDeclaration 365 * @param {!Array.<!RuntimeAgent.CallArgument>|undefined} args 366 * @param {function(*)} callback 367 */ 368 callFunctionJSON: function(functionDeclaration, args, callback) 369 { 370 /** 371 * @param {?Protocol.Error} error 372 * @param {!RuntimeAgent.RemoteObject} result 373 * @param {boolean=} wasThrown 374 */ 375 function mycallback(error, result, wasThrown) 376 { 377 callback((error || wasThrown) ? null : result.value); 378 } 379 380 RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, false, mycallback); 381 }, 382 383 release: function() 384 { 385 if (!this._objectId) 386 return; 387 RuntimeAgent.releaseObject(this._objectId); 388 }, 389 390 /** 391 * @return {number} 392 */ 393 arrayLength: function() 394 { 395 if (this.subtype !== "array") 396 return 0; 397 398 var matches = this._description.match(/\[([0-9]+)\]/); 399 if (!matches) 400 return 0; 401 return parseInt(matches[1], 10); 402 } 403}; 404 405 406/** 407 * @param {!WebInspector.RemoteObject} object 408 * @param {boolean} flattenProtoChain 409 * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback 410 */ 411WebInspector.RemoteObject.loadFromObject = function(object, flattenProtoChain, callback) 412{ 413 if (flattenProtoChain) 414 object.getAllProperties(false, callback); 415 else 416 WebInspector.RemoteObject.loadFromObjectPerProto(object, callback); 417}; 418 419/** 420 * @param {!WebInspector.RemoteObject} object 421 * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback 422 */ 423WebInspector.RemoteObject.loadFromObjectPerProto = function(object, callback) 424{ 425 // Combines 2 asynch calls. Doesn't rely on call-back orders (some calls may be loop-back). 426 var savedOwnProperties; 427 var savedAccessorProperties; 428 var savedInternalProperties; 429 var resultCounter = 2; 430 431 function processCallback() 432 { 433 if (--resultCounter) 434 return; 435 if (savedOwnProperties && savedAccessorProperties) { 436 var combinedList = savedAccessorProperties.slice(0); 437 for (var i = 0; i < savedOwnProperties.length; i++) { 438 var property = savedOwnProperties[i]; 439 if (!property.isAccessorProperty()) 440 combinedList.push(property); 441 } 442 return callback(combinedList, savedInternalProperties ? savedInternalProperties : null); 443 } else { 444 callback(null, null); 445 } 446 } 447 448 /** 449 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties 450 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties 451 */ 452 function allAccessorPropertiesCallback(properties, internalProperties) 453 { 454 savedAccessorProperties = properties; 455 processCallback(); 456 } 457 458 /** 459 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties 460 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties 461 */ 462 function ownPropertiesCallback(properties, internalProperties) 463 { 464 savedOwnProperties = properties; 465 savedInternalProperties = internalProperties; 466 processCallback(); 467 } 468 469 object.getAllProperties(true, allAccessorPropertiesCallback); 470 object.getOwnProperties(ownPropertiesCallback); 471}; 472 473 474/** 475 * @constructor 476 * @extends {WebInspector.RemoteObject} 477 * @param {string|undefined} objectId 478 * @param {!WebInspector.ScopeRef} scopeRef 479 * @param {string} type 480 * @param {string|undefined} subtype 481 * @param {*} value 482 * @param {string=} description 483 * @param {!RuntimeAgent.ObjectPreview=} preview 484 */ 485WebInspector.ScopeRemoteObject = function(objectId, scopeRef, type, subtype, value, description, preview) 486{ 487 WebInspector.RemoteObject.call(this, objectId, type, subtype, value, description, preview); 488 this._scopeRef = scopeRef; 489 this._savedScopeProperties = undefined; 490}; 491 492/** 493 * @param {!RuntimeAgent.RemoteObject} payload 494 * @param {!WebInspector.ScopeRef=} scopeRef 495 * @return {!WebInspector.RemoteObject} 496 */ 497WebInspector.ScopeRemoteObject.fromPayload = function(payload, scopeRef) 498{ 499 if (scopeRef) 500 return new WebInspector.ScopeRemoteObject(payload.objectId, scopeRef, payload.type, payload.subtype, payload.value, payload.description, payload.preview); 501 else 502 return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview); 503} 504 505WebInspector.ScopeRemoteObject.prototype = { 506 /** 507 * @param {boolean} ownProperties 508 * @param {boolean} accessorPropertiesOnly 509 * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!WebInspector.RemoteObjectProperty>)} callback 510 * @override 511 */ 512 doGetProperties: function(ownProperties, accessorPropertiesOnly, callback) 513 { 514 if (accessorPropertiesOnly) { 515 callback([], []); 516 return; 517 } 518 if (this._savedScopeProperties) { 519 // No need to reload scope variables, as the remote object never 520 // changes its properties. If variable is updated, the properties 521 // array is patched locally. 522 callback(this._savedScopeProperties.slice(), []); 523 return; 524 } 525 526 /** 527 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties 528 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties 529 * @this {WebInspector.ScopeRemoteObject} 530 */ 531 function wrappedCallback(properties, internalProperties) 532 { 533 if (this._scopeRef && properties instanceof Array) 534 this._savedScopeProperties = properties.slice(); 535 callback(properties, internalProperties); 536 } 537 538 WebInspector.RemoteObject.prototype.doGetProperties.call(this, ownProperties, accessorPropertiesOnly, wrappedCallback.bind(this)); 539 }, 540 541 /** 542 * @override 543 * @param {!RuntimeAgent.RemoteObject} result 544 * @param {string} name 545 * @param {function(string=)} callback 546 */ 547 doSetObjectPropertyValue: function(result, name, callback) 548 { 549 var newValue; 550 551 switch (result.type) { 552 case "undefined": 553 newValue = {}; 554 break; 555 case "object": 556 case "function": 557 newValue = { objectId: result.objectId }; 558 break; 559 default: 560 newValue = { value: result.value }; 561 } 562 563 DebuggerAgent.setVariableValue(this._scopeRef.number, name, newValue, this._scopeRef.callFrameId, this._scopeRef.functionId, setVariableValueCallback.bind(this)); 564 565 /** 566 * @param {?Protocol.Error} error 567 * @this {WebInspector.ScopeRemoteObject} 568 */ 569 function setVariableValueCallback(error) 570 { 571 if (error) { 572 callback(error); 573 return; 574 } 575 if (this._savedScopeProperties) { 576 for (var i = 0; i < this._savedScopeProperties.length; i++) { 577 if (this._savedScopeProperties[i].name === name) 578 this._savedScopeProperties[i].value = WebInspector.RemoteObject.fromPayload(result); 579 } 580 } 581 callback(); 582 } 583 }, 584 585 __proto__: WebInspector.RemoteObject.prototype 586}; 587 588/** 589 * Either callFrameId or functionId (exactly one) must be defined. 590 * @constructor 591 * @param {number} number 592 * @param {string=} callFrameId 593 * @param {string=} functionId 594 */ 595WebInspector.ScopeRef = function(number, callFrameId, functionId) 596{ 597 this.number = number; 598 this.callFrameId = callFrameId; 599 this.functionId = functionId; 600} 601 602/** 603 * @constructor 604 * @param {string} name 605 * @param {?WebInspector.RemoteObject} value 606 * @param {!RuntimeAgent.PropertyDescriptor=} descriptor 607 */ 608WebInspector.RemoteObjectProperty = function(name, value, descriptor) 609{ 610 this.name = name; 611 this.enumerable = descriptor ? !!descriptor.enumerable : true; 612 this.writable = descriptor ? !!descriptor.writable : true; 613 614 if (value === null && descriptor) { 615 if (descriptor.value) 616 this.value = WebInspector.RemoteObject.fromPayload(descriptor.value) 617 if (descriptor.get && descriptor.get.type !== "undefined") 618 this.getter = WebInspector.RemoteObject.fromPayload(descriptor.get); 619 if (descriptor.set && descriptor.set.type !== "undefined") 620 this.setter = WebInspector.RemoteObject.fromPayload(descriptor.set); 621 } else { 622 this.value = value; 623 } 624 625 if (descriptor) { 626 this.isOwn = descriptor.isOwn; 627 this.wasThrown = !!descriptor.wasThrown; 628 } 629} 630 631WebInspector.RemoteObjectProperty.prototype = { 632 isAccessorProperty: function() 633 { 634 return this.getter || this.setter; 635 } 636}; 637 638/** 639 * @param {string} name 640 * @param {string} value 641 * @return {!WebInspector.RemoteObjectProperty} 642 */ 643WebInspector.RemoteObjectProperty.fromPrimitiveValue = function(name, value) 644{ 645 return new WebInspector.RemoteObjectProperty(name, WebInspector.RemoteObject.fromPrimitiveValue(value)); 646} 647 648/** 649 * @param {string} name 650 * @param {!WebInspector.RemoteObject} value 651 * @return {!WebInspector.RemoteObjectProperty} 652 */ 653WebInspector.RemoteObjectProperty.fromScopeValue = function(name, value) 654{ 655 var result = new WebInspector.RemoteObjectProperty(name, value); 656 result.writable = false; 657 return result; 658} 659 660// The below is a wrapper around a local object that provides an interface comaptible 661// with RemoteObject, to be used by the UI code (primarily ObjectPropertiesSection). 662// Note that only JSON-compliant objects are currently supported, as there's no provision 663// for traversing prototypes, extracting class names via constuctor, handling properties 664// or functions. 665 666/** 667 * @constructor 668 * @extends {WebInspector.RemoteObject} 669 * @param {*} value 670 */ 671WebInspector.LocalJSONObject = function(value) 672{ 673 this._value = value; 674} 675 676WebInspector.LocalJSONObject.prototype = { 677 /** 678 * @return {string} 679 */ 680 get description() 681 { 682 if (this._cachedDescription) 683 return this._cachedDescription; 684 685 /** 686 * @param {!WebInspector.RemoteObjectProperty} property 687 */ 688 function formatArrayItem(property) 689 { 690 return property.value.description; 691 } 692 693 /** 694 * @param {!WebInspector.RemoteObjectProperty} property 695 */ 696 function formatObjectItem(property) 697 { 698 return property.name + ":" + property.value.description; 699 } 700 701 if (this.type === "object") { 702 switch (this.subtype) { 703 case "array": 704 this._cachedDescription = this._concatenate("[", "]", formatArrayItem); 705 break; 706 case "date": 707 this._cachedDescription = "" + this._value; 708 break; 709 case "null": 710 this._cachedDescription = "null"; 711 break; 712 default: 713 this._cachedDescription = this._concatenate("{", "}", formatObjectItem); 714 } 715 } else 716 this._cachedDescription = String(this._value); 717 718 return this._cachedDescription; 719 }, 720 721 /** 722 * @param {string} prefix 723 * @param {string} suffix 724 * @param {function (!WebInspector.RemoteObjectProperty)} formatProperty 725 * @return {string} 726 */ 727 _concatenate: function(prefix, suffix, formatProperty) 728 { 729 const previewChars = 100; 730 731 var buffer = prefix; 732 var children = this._children(); 733 for (var i = 0; i < children.length; ++i) { 734 var itemDescription = formatProperty(children[i]); 735 if (buffer.length + itemDescription.length > previewChars) { 736 buffer += ",\u2026"; 737 break; 738 } 739 if (i) 740 buffer += ", "; 741 buffer += itemDescription; 742 } 743 buffer += suffix; 744 return buffer; 745 }, 746 747 /** 748 * @return {string} 749 */ 750 get type() 751 { 752 return typeof this._value; 753 }, 754 755 /** 756 * @return {string|undefined} 757 */ 758 get subtype() 759 { 760 if (this._value === null) 761 return "null"; 762 763 if (this._value instanceof Array) 764 return "array"; 765 766 if (this._value instanceof Date) 767 return "date"; 768 769 return undefined; 770 }, 771 772 /** 773 * @return {boolean} 774 */ 775 get hasChildren() 776 { 777 if ((typeof this._value !== "object") || (this._value === null)) 778 return false; 779 return !!Object.keys(/** @type {!Object} */ (this._value)).length; 780 }, 781 782 /** 783 * @param {function(!Array.<!WebInspector.RemoteObjectProperty>)} callback 784 */ 785 getOwnProperties: function(callback) 786 { 787 callback(this._children()); 788 }, 789 790 /** 791 * @param {boolean} accessorPropertiesOnly 792 * @param {function(!Array.<!WebInspector.RemoteObjectProperty>)} callback 793 */ 794 getAllProperties: function(accessorPropertiesOnly, callback) 795 { 796 if (accessorPropertiesOnly) 797 callback([]); 798 else 799 callback(this._children()); 800 }, 801 802 /** 803 * @return {!Array.<!WebInspector.RemoteObjectProperty>} 804 */ 805 _children: function() 806 { 807 if (!this.hasChildren) 808 return []; 809 var value = /** @type {!Object} */ (this._value); 810 811 /** 812 * @param {string} propName 813 * @this {WebInspector.LocalJSONObject} 814 */ 815 function buildProperty(propName) 816 { 817 return new WebInspector.RemoteObjectProperty(propName, new WebInspector.LocalJSONObject(this._value[propName])); 818 } 819 if (!this._cachedChildren) 820 this._cachedChildren = Object.keys(value).map(buildProperty.bind(this)); 821 return this._cachedChildren; 822 }, 823 824 /** 825 * @return {boolean} 826 */ 827 isError: function() 828 { 829 return false; 830 }, 831 832 /** 833 * @return {number} 834 */ 835 arrayLength: function() 836 { 837 return this._value instanceof Array ? this._value.length : 0; 838 }, 839 840 /** 841 * @param {function(this:Object, ...)} functionDeclaration 842 * @param {!Array.<!RuntimeAgent.CallArgument>=} args 843 * @param {function(?WebInspector.RemoteObject, boolean=)=} callback 844 */ 845 callFunction: function(functionDeclaration, args, callback) 846 { 847 var target = /** @type {?Object} */ (this._value); 848 var rawArgs = args ? args.map(function(arg) {return arg.value;}) : []; 849 850 var result; 851 var wasThrown = false; 852 try { 853 result = functionDeclaration.apply(target, rawArgs); 854 } catch (e) { 855 wasThrown = true; 856 } 857 858 if (!callback) 859 return; 860 callback(WebInspector.RemoteObject.fromLocalObject(result), wasThrown); 861 }, 862 863 /** 864 * @param {function(this:Object)} functionDeclaration 865 * @param {!Array.<!RuntimeAgent.CallArgument>|undefined} args 866 * @param {function(*)} callback 867 */ 868 callFunctionJSON: function(functionDeclaration, args, callback) 869 { 870 var target = /** @type {?Object} */ (this._value); 871 var rawArgs = args ? args.map(function(arg) {return arg.value;}) : []; 872 873 var result; 874 try { 875 result = functionDeclaration.apply(target, rawArgs); 876 } catch (e) { 877 result = null; 878 } 879 880 callback(result); 881 } 882} 883