1/* 2 * Copyright (C) 2007 Apple 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 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29//# sourceURL=__WebInspectorInjectedScript__ 30 31/** 32 * @param {InjectedScriptHost} InjectedScriptHost 33 * @param {GlobalObject} inspectedGlobalObject 34 * @param {number} injectedScriptId 35 */ 36(function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) { 37 38// Protect against Object overwritten by the user code. 39var Object = {}.constructor; 40 41/** 42 * @constructor 43 */ 44var InjectedScript = function() 45{ 46 this._lastBoundObjectId = 1; 47 this._idToWrappedObject = {}; 48 this._idToObjectGroupName = {}; 49 this._objectGroups = {}; 50 this._modules = {}; 51} 52 53/** 54 * @type {Object.<string, boolean>} 55 * @const 56 */ 57InjectedScript.primitiveTypes = { 58 undefined: true, 59 boolean: true, 60 number: true, 61 string: true 62} 63 64InjectedScript.prototype = { 65 /** 66 * @param {*} object 67 * @return {boolean} 68 */ 69 isPrimitiveValue: function(object) 70 { 71 // FIXME(33716): typeof document.all is always 'undefined'. 72 return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object); 73 }, 74 75 /** 76 * @param {*} object 77 * @param {string} groupName 78 * @param {boolean} canAccessInspectedGlobalObject 79 * @param {boolean} generatePreview 80 * @return {!RuntimeAgent.RemoteObject} 81 */ 82 wrapObject: function(object, groupName, canAccessInspectedGlobalObject, generatePreview) 83 { 84 if (canAccessInspectedGlobalObject) 85 return this._wrapObject(object, groupName, false, generatePreview); 86 return this._fallbackWrapper(object); 87 }, 88 89 /** 90 * @param {*} object 91 * @return {!RuntimeAgent.RemoteObject} 92 */ 93 _fallbackWrapper: function(object) 94 { 95 var result = {}; 96 result.type = typeof object; 97 if (this.isPrimitiveValue(object)) 98 result.value = object; 99 else 100 result.description = this._toString(object); 101 return /** @type {!RuntimeAgent.RemoteObject} */ (result); 102 }, 103 104 /** 105 * @param {boolean} canAccessInspectedGlobalObject 106 * @param {Object} table 107 * @param {Array.<string>|string|boolean} columns 108 * @return {!RuntimeAgent.RemoteObject} 109 */ 110 wrapTable: function(canAccessInspectedGlobalObject, table, columns) 111 { 112 if (!canAccessInspectedGlobalObject) 113 return this._fallbackWrapper(table); 114 var columnNames = null; 115 if (typeof columns === "string") 116 columns = [columns]; 117 if (InjectedScriptHost.type(columns) == "array") { 118 columnNames = []; 119 for (var i = 0; i < columns.length; ++i) 120 columnNames.push(String(columns[i])); 121 } 122 return this._wrapObject(table, "console", false, true, columnNames); 123 }, 124 125 /** 126 * @param {*} object 127 */ 128 inspectObject: function(object) 129 { 130 if (this._commandLineAPIImpl) 131 this._commandLineAPIImpl.inspect(object); 132 }, 133 134 /** 135 * This method cannot throw. 136 * @param {*} object 137 * @param {string=} objectGroupName 138 * @param {boolean=} forceValueType 139 * @param {boolean=} generatePreview 140 * @param {?Array.<string>=} columnNames 141 * @return {!RuntimeAgent.RemoteObject} 142 * @suppress {checkTypes} 143 */ 144 _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames) 145 { 146 try { 147 return new InjectedScript.RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames); 148 } catch (e) { 149 try { 150 var description = injectedScript._describe(e); 151 } catch (ex) { 152 var description = "<failed to convert exception to string>"; 153 } 154 return new InjectedScript.RemoteObject(description); 155 } 156 }, 157 158 /** 159 * @param {*} object 160 * @param {string=} objectGroupName 161 * @return {string} 162 */ 163 _bind: function(object, objectGroupName) 164 { 165 var id = this._lastBoundObjectId++; 166 this._idToWrappedObject[id] = object; 167 var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}"; 168 if (objectGroupName) { 169 var group = this._objectGroups[objectGroupName]; 170 if (!group) { 171 group = []; 172 this._objectGroups[objectGroupName] = group; 173 } 174 group.push(id); 175 this._idToObjectGroupName[id] = objectGroupName; 176 } 177 return objectId; 178 }, 179 180 /** 181 * @param {string} objectId 182 * @return {Object} 183 */ 184 _parseObjectId: function(objectId) 185 { 186 return InjectedScriptHost.evaluate("(" + objectId + ")"); 187 }, 188 189 /** 190 * @param {string} objectGroupName 191 */ 192 releaseObjectGroup: function(objectGroupName) 193 { 194 var group = this._objectGroups[objectGroupName]; 195 if (!group) 196 return; 197 for (var i = 0; i < group.length; i++) 198 this._releaseObject(group[i]); 199 delete this._objectGroups[objectGroupName]; 200 }, 201 202 /** 203 * @param {string} methodName 204 * @param {string} args 205 * @return {*} 206 */ 207 dispatch: function(methodName, args) 208 { 209 var argsArray = InjectedScriptHost.evaluate("(" + args + ")"); 210 var result = this[methodName].apply(this, argsArray); 211 if (typeof result === "undefined") { 212 if (inspectedGlobalObject.console) 213 inspectedGlobalObject.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName); 214 result = null; 215 } 216 return result; 217 }, 218 219 /** 220 * @param {string} objectId 221 * @param {boolean} ownProperties 222 * @return {Array.<RuntimeAgent.PropertyDescriptor>|boolean} 223 */ 224 getProperties: function(objectId, ownProperties) 225 { 226 var parsedObjectId = this._parseObjectId(objectId); 227 var object = this._objectForId(parsedObjectId); 228 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id]; 229 230 if (!this._isDefined(object)) 231 return false; 232 var descriptors = this._propertyDescriptors(object, ownProperties); 233 234 // Go over properties, wrap object values. 235 for (var i = 0; i < descriptors.length; ++i) { 236 var descriptor = descriptors[i]; 237 if ("get" in descriptor) 238 descriptor.get = this._wrapObject(descriptor.get, objectGroupName); 239 if ("set" in descriptor) 240 descriptor.set = this._wrapObject(descriptor.set, objectGroupName); 241 if ("value" in descriptor) 242 descriptor.value = this._wrapObject(descriptor.value, objectGroupName); 243 if (!("configurable" in descriptor)) 244 descriptor.configurable = false; 245 if (!("enumerable" in descriptor)) 246 descriptor.enumerable = false; 247 } 248 return descriptors; 249 }, 250 251 /** 252 * @param {string} objectId 253 * @return {Array.<Object>|boolean} 254 */ 255 getInternalProperties: function(objectId, ownProperties) 256 { 257 var parsedObjectId = this._parseObjectId(objectId); 258 var object = this._objectForId(parsedObjectId); 259 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id]; 260 if (!this._isDefined(object)) 261 return false; 262 var descriptors = []; 263 var internalProperties = InjectedScriptHost.getInternalProperties(object); 264 if (internalProperties) { 265 for (var i = 0; i < internalProperties.length; i++) { 266 var property = internalProperties[i]; 267 var descriptor = { 268 name: property.name, 269 value: this._wrapObject(property.value, objectGroupName) 270 }; 271 descriptors.push(descriptor); 272 } 273 } 274 return descriptors; 275 }, 276 277 /** 278 * @param {string} functionId 279 * @return {!DebuggerAgent.FunctionDetails|string} 280 */ 281 getFunctionDetails: function(functionId) 282 { 283 var parsedFunctionId = this._parseObjectId(functionId); 284 var func = this._objectForId(parsedFunctionId); 285 if (typeof func !== "function") 286 return "Cannot resolve function by id."; 287 var details = InjectedScriptHost.functionDetails(func); 288 if (!details) 289 return "Cannot resolve function details."; 290 if ("rawScopes" in details) { 291 var objectGroupName = this._idToObjectGroupName[parsedFunctionId.id]; 292 var rawScopes = details.rawScopes; 293 var scopes = []; 294 delete details.rawScopes; 295 for (var i = 0; i < rawScopes.length; i++) 296 scopes.push(InjectedScript.CallFrameProxy._createScopeJson(rawScopes[i].type, rawScopes[i].object, objectGroupName)); 297 details.scopeChain = scopes; 298 } 299 return details; 300 }, 301 302 /** 303 * @param {string} objectId 304 */ 305 releaseObject: function(objectId) 306 { 307 var parsedObjectId = this._parseObjectId(objectId); 308 this._releaseObject(parsedObjectId.id); 309 }, 310 311 /** 312 * @param {string} id 313 */ 314 _releaseObject: function(id) 315 { 316 delete this._idToWrappedObject[id]; 317 delete this._idToObjectGroupName[id]; 318 }, 319 320 /** 321 * @param {Object} object 322 * @param {boolean} ownProperties 323 * @return {Array.<Object>} 324 */ 325 _propertyDescriptors: function(object, ownProperties) 326 { 327 var descriptors = []; 328 var nameProcessed = {}; 329 nameProcessed["__proto__"] = null; 330 for (var o = object; this._isDefined(o); o = o.__proto__) { 331 var names = Object.getOwnPropertyNames(/** @type {!Object} */ (o)); 332 for (var i = 0; i < names.length; ++i) { 333 var name = names[i]; 334 if (nameProcessed[name]) 335 continue; 336 337 try { 338 nameProcessed[name] = true; 339 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */ (object), name); 340 if (!descriptor) { 341 // Not all bindings provide proper descriptors. Fall back to the writable, configurable property. 342 try { 343 descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false}; 344 if (o === object) 345 descriptor.isOwn = true; 346 descriptors.push(descriptor); 347 } catch (e) { 348 // Silent catch. 349 } 350 continue; 351 } 352 if (descriptor.hasOwnProperty("get") && descriptor.hasOwnProperty("set") && !descriptor.get && !descriptor.set) { 353 // Not all bindings provide proper descriptors. Fall back to the writable, configurable property. 354 try { 355 descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false}; 356 if (o === object) 357 descriptor.isOwn = true; 358 descriptors.push(descriptor); 359 } catch (e) { 360 // Silent catch. 361 } 362 continue; 363 } 364 } catch (e) { 365 var descriptor = {}; 366 descriptor.value = e; 367 descriptor.wasThrown = true; 368 } 369 370 descriptor.name = name; 371 if (o === object) 372 descriptor.isOwn = true; 373 descriptors.push(descriptor); 374 } 375 if (ownProperties) { 376 if (object.__proto__) 377 descriptors.push({ name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true}); 378 break; 379 } 380 } 381 return descriptors; 382 }, 383 384 /** 385 * @param {string} expression 386 * @param {string} objectGroup 387 * @param {boolean} injectCommandLineAPI 388 * @param {boolean} returnByValue 389 * @param {boolean} generatePreview 390 * @return {*} 391 */ 392 evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview) 393 { 394 return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview); 395 }, 396 397 /** 398 * @param {string} objectId 399 * @param {string} expression 400 * @param {boolean} returnByValue 401 * @return {Object|string} 402 */ 403 callFunctionOn: function(objectId, expression, args, returnByValue) 404 { 405 var parsedObjectId = this._parseObjectId(objectId); 406 var object = this._objectForId(parsedObjectId); 407 if (!this._isDefined(object)) 408 return "Could not find object with given id"; 409 410 if (args) { 411 var resolvedArgs = []; 412 args = InjectedScriptHost.evaluate(args); 413 for (var i = 0; i < args.length; ++i) { 414 var resolvedCallArgument; 415 try { 416 resolvedCallArgument = this._resolveCallArgument(args[i]); 417 } catch (e) { 418 return String(e); 419 } 420 resolvedArgs.push(resolvedCallArgument) 421 } 422 } 423 424 try { 425 var objectGroup = this._idToObjectGroupName[parsedObjectId.id]; 426 var func = InjectedScriptHost.evaluate("(" + expression + ")"); 427 if (typeof func !== "function") 428 return "Given expression does not evaluate to a function"; 429 430 return { wasThrown: false, 431 result: this._wrapObject(func.apply(object, resolvedArgs), objectGroup, returnByValue) }; 432 } catch (e) { 433 return this._createThrownValue(e, objectGroup); 434 } 435 }, 436 437 /** 438 * Resolves a value from CallArgument description. 439 * @param {RuntimeAgent.CallArgument} callArgumentJson 440 * @return {*} resolved value 441 * @throws {string} error message 442 */ 443 _resolveCallArgument: function(callArgumentJson) { 444 var objectId = callArgumentJson.objectId; 445 if (objectId) { 446 var parsedArgId = this._parseObjectId(objectId); 447 if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId) 448 throw "Arguments should belong to the same JavaScript world as the target object."; 449 450 var resolvedArg = this._objectForId(parsedArgId); 451 if (!this._isDefined(resolvedArg)) 452 throw "Could not find object with given id"; 453 454 return resolvedArg; 455 } else if ("value" in callArgumentJson) 456 return callArgumentJson.value; 457 else 458 return undefined; 459 }, 460 461 /** 462 * @param {Function} evalFunction 463 * @param {Object} object 464 * @param {string} objectGroup 465 * @param {boolean} isEvalOnCallFrame 466 * @param {boolean} injectCommandLineAPI 467 * @param {boolean} returnByValue 468 * @param {boolean} generatePreview 469 * @return {*} 470 */ 471 _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview) 472 { 473 try { 474 return { wasThrown: false, 475 result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI), objectGroup, returnByValue, generatePreview) }; 476 } catch (e) { 477 return this._createThrownValue(e, objectGroup); 478 } 479 }, 480 481 /** 482 * @param {*} value 483 * @param {string} objectGroup 484 * @return {Object} 485 */ 486 _createThrownValue: function(value, objectGroup) 487 { 488 var remoteObject = this._wrapObject(value, objectGroup); 489 try { 490 remoteObject.description = this._toString(value); 491 } catch (e) {} 492 return { wasThrown: true, 493 result: remoteObject }; 494 }, 495 496 /** 497 * @param {Function} evalFunction 498 * @param {Object} object 499 * @param {string} objectGroup 500 * @param {string} expression 501 * @param {boolean} isEvalOnCallFrame 502 * @param {boolean} injectCommandLineAPI 503 * @return {*} 504 */ 505 _evaluateOn: function(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI) 506 { 507 var commandLineAPI = null; 508 if (injectCommandLineAPI) { 509 if (this.CommandLineAPI) 510 commandLineAPI = new this.CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null); 511 else 512 commandLineAPI = new BasicCommandLineAPI; 513 } 514 515 if (isEvalOnCallFrame) { 516 // We can only use this approach if the evaluate function is the true 'eval'. That allows us to use it with 517 // the 'eval' identifier when calling it. Using 'eval' grants access to the local scope of the closure we 518 // create that provides the command line APIs. 519 520 var parameters = [InjectedScriptHost.evaluate, expression]; 521 var expressionFunctionBody = "" + 522 "var global = Function('return this')() || (1, eval)('this');" + 523 "var __originalEval = global.eval; global.eval = __eval;" + 524 "try { return eval(__currentExpression); }" + 525 "finally { global.eval = __originalEval; }"; 526 527 if (commandLineAPI) { 528 // To avoid using a 'with' statement (which fails in strict mode and requires injecting the API object) 529 // we instead create a closure where we evaluate the expression. The command line APIs are passed as 530 // parameters to the closure so they are in scope but not injected. This allows the code evaluated in 531 // the console to stay in strict mode (if is was already set), or to get strict mode by prefixing 532 // expressions with 'use strict';. 533 534 var parameterNames = Object.getOwnPropertyNames(commandLineAPI); 535 for (var i = 0; i < parameterNames.length; ++i) 536 parameters.push(commandLineAPI[parameterNames[i]]); 537 538 var expressionFunctionString = "(function(__eval, __currentExpression, " + parameterNames.join(", ") + ") { " + expressionFunctionBody + " })"; 539 } else { 540 // Use a closure in this case too to keep the same behavior of 'var' being captured by the closure instead 541 // of leaking out into the calling scope. 542 var expressionFunctionString = "(function(__eval, __currentExpression) { " + expressionFunctionBody + " })"; 543 } 544 545 // Bind 'this' to the function expression using another closure instead of Function.prototype.bind. This ensures things will work if the page replaces bind. 546 var boundExpressionFunctionString = "(function(__function, __thisObject) { return function() { return __function.apply(__thisObject, arguments) }; })(" + expressionFunctionString + ", this)"; 547 var expressionFunction = evalFunction.call(object, boundExpressionFunctionString); 548 var result = expressionFunction.apply(null, parameters); 549 550 if (objectGroup === "console") 551 this._lastResult = result; 552 553 return result; 554 } 555 556 // When not evaluating on a call frame we use a 'with' statement to allow var and function statements to leak 557 // into the global scope. This allow them to stick around between evaluations. 558 559 try { 560 if (commandLineAPI) { 561 if (inspectedGlobalObject.console) 562 inspectedGlobalObject.console.__commandLineAPI = commandLineAPI; 563 else 564 inspectedGlobalObject.__commandLineAPI = commandLineAPI; 565 expression = "with ((this && (this.console ? this.console.__commandLineAPI : this.__commandLineAPI)) || {}) { " + expression + "\n}"; 566 } 567 568 var result = evalFunction.call(inspectedGlobalObject, expression); 569 570 if (objectGroup === "console") 571 this._lastResult = result; 572 573 return result; 574 } finally { 575 if (commandLineAPI) { 576 if (inspectedGlobalObject.console) 577 delete inspectedGlobalObject.console.__commandLineAPI; 578 else 579 delete inspectedGlobalObject.__commandLineAPI; 580 } 581 } 582 }, 583 584 /** 585 * @param {Object} callFrame 586 * @return {Array.<InjectedScript.CallFrameProxy>|boolean} 587 */ 588 wrapCallFrames: function(callFrame) 589 { 590 if (!callFrame) 591 return false; 592 593 var result = []; 594 var depth = 0; 595 do { 596 result.push(new InjectedScript.CallFrameProxy(depth++, callFrame)); 597 callFrame = callFrame.caller; 598 } while (callFrame); 599 return result; 600 }, 601 602 /** 603 * @param {Object} topCallFrame 604 * @param {string} callFrameId 605 * @param {string} expression 606 * @param {string} objectGroup 607 * @param {boolean} injectCommandLineAPI 608 * @param {boolean} returnByValue 609 * @param {boolean} generatePreview 610 * @return {*} 611 */ 612 evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview) 613 { 614 var callFrame = this._callFrameForId(topCallFrame, callFrameId); 615 if (!callFrame) 616 return "Could not find call frame with given id"; 617 return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview); 618 }, 619 620 /** 621 * @param {Object} topCallFrame 622 * @param {string} callFrameId 623 * @return {Object} 624 */ 625 _callFrameForId: function(topCallFrame, callFrameId) 626 { 627 var parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")"); 628 var ordinal = parsedCallFrameId["ordinal"]; 629 var callFrame = topCallFrame; 630 while (--ordinal >= 0 && callFrame) 631 callFrame = callFrame.caller; 632 return callFrame; 633 }, 634 635 /** 636 * @param {Object} objectId 637 * @return {Object} 638 */ 639 _objectForId: function(objectId) 640 { 641 return this._idToWrappedObject[objectId.id]; 642 }, 643 644 /** 645 * @param {string} objectId 646 * @return {Object} 647 */ 648 findObjectById: function(objectId) 649 { 650 var parsedObjectId = this._parseObjectId(objectId); 651 return this._objectForId(parsedObjectId); 652 }, 653 654 /** 655 * @param {string} name 656 * @return {Object} 657 */ 658 module: function(name) 659 { 660 return this._modules[name]; 661 }, 662 663 /** 664 * @param {string} name 665 * @param {string} source 666 * @return {Object} 667 */ 668 injectModule: function(name, source, host) 669 { 670 delete this._modules[name]; 671 var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")"); 672 if (typeof moduleFunction !== "function") { 673 if (inspectedGlobalObject.console) 674 inspectedGlobalObject.console.error("Web Inspector error: A function was expected for module %s evaluation", name); 675 return null; 676 } 677 var module = moduleFunction.call(inspectedGlobalObject, InjectedScriptHost, inspectedGlobalObject, injectedScriptId, this, host); 678 this._modules[name] = module; 679 return module; 680 }, 681 682 /** 683 * @param {*} object 684 * @return {boolean} 685 */ 686 _isDefined: function(object) 687 { 688 return !!object || this._isHTMLAllCollection(object); 689 }, 690 691 /** 692 * @param {*} object 693 * @return {boolean} 694 */ 695 _isHTMLAllCollection: function(object) 696 { 697 // document.all is reported as undefined, but we still want to process it. 698 return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCollection(object); 699 }, 700 701 /** 702 * @param {Object=} obj 703 * @return {string?} 704 */ 705 _subtype: function(obj) 706 { 707 if (obj === null) 708 return "null"; 709 710 if (this.isPrimitiveValue(obj)) 711 return null; 712 713 if (this._isHTMLAllCollection(obj)) 714 return "array"; 715 716 var preciseType = InjectedScriptHost.type(obj); 717 if (preciseType) 718 return preciseType; 719 720 // FireBug's array detection. 721 try { 722 if (typeof obj.splice === "function" && isFinite(obj.length)) 723 return "array"; 724 if (Object.prototype.toString.call(obj) === "[object Arguments]" && isFinite(obj.length)) // arguments. 725 return "array"; 726 } catch (e) { 727 } 728 729 // If owning frame has navigated to somewhere else window properties will be undefined. 730 return null; 731 }, 732 733 /** 734 * @param {*} obj 735 * @return {string?} 736 */ 737 _describe: function(obj) 738 { 739 if (this.isPrimitiveValue(obj)) 740 return null; 741 742 obj = /** @type {Object} */ (obj); 743 744 // Type is object, get subtype. 745 var subtype = this._subtype(obj); 746 747 if (subtype === "regexp") 748 return this._toString(obj); 749 750 if (subtype === "date") 751 return this._toString(obj); 752 753 if (subtype === "node") { 754 var description = obj.nodeName.toLowerCase(); 755 switch (obj.nodeType) { 756 case 1 /* Node.ELEMENT_NODE */: 757 description += obj.id ? "#" + obj.id : ""; 758 var className = obj.className; 759 description += className ? "." + className : ""; 760 break; 761 case 10 /*Node.DOCUMENT_TYPE_NODE */: 762 description = "<!DOCTYPE " + description + ">"; 763 break; 764 } 765 return description; 766 } 767 768 var className = InjectedScriptHost.internalConstructorName(obj); 769 if (subtype === "array") { 770 if (typeof obj.length === "number") 771 className += "[" + obj.length + "]"; 772 return className; 773 } 774 775 // NodeList in JSC is a function, check for array prior to this. 776 if (typeof obj === "function") 777 return this._toString(obj); 778 779 if (className === "Object") { 780 // In Chromium DOM wrapper prototypes will have Object as their constructor name, 781 // get the real DOM wrapper name from the constructor property. 782 var constructorName = obj.constructor && obj.constructor.name; 783 if (constructorName) 784 return constructorName; 785 } 786 return className; 787 }, 788 789 /** 790 * @param {*} obj 791 * @return {string} 792 */ 793 _toString: function(obj) 794 { 795 // We don't use String(obj) because inspectedGlobalObject.String is undefined if owning frame navigated to another page. 796 return "" + obj; 797 } 798} 799 800/** 801 * @type {InjectedScript} 802 * @const 803 */ 804var injectedScript = new InjectedScript(); 805 806/** 807 * @constructor 808 * @param {*} object 809 * @param {string=} objectGroupName 810 * @param {boolean=} forceValueType 811 * @param {boolean=} generatePreview 812 * @param {?Array.<string>=} columnNames 813 */ 814InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, generatePreview, columnNames) 815{ 816 this.type = typeof object; 817 if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) { 818 // We don't send undefined values over JSON. 819 if (typeof object !== "undefined") 820 this.value = object; 821 822 // Null object is object with 'null' subtype' 823 if (object === null) 824 this.subtype = "null"; 825 826 // Provide user-friendly number values. 827 if (typeof object === "number") 828 this.description = object + ""; 829 return; 830 } 831 832 object = /** @type {Object} */ (object); 833 834 this.objectId = injectedScript._bind(object, objectGroupName); 835 var subtype = injectedScript._subtype(object); 836 if (subtype) 837 this.subtype = subtype; 838 this.className = InjectedScriptHost.internalConstructorName(object); 839 this.description = injectedScript._describe(object); 840 841 if (generatePreview && (this.type === "object" || injectedScript._isHTMLAllCollection(object))) 842 this.preview = this._generatePreview(object, undefined, columnNames); 843} 844 845InjectedScript.RemoteObject.prototype = { 846 /** 847 * @param {Object} object 848 * @param {Array.<string>=} firstLevelKeys 849 * @param {?Array.<string>=} secondLevelKeys 850 * @return {Object} preview 851 */ 852 _generatePreview: function(object, firstLevelKeys, secondLevelKeys) 853 { 854 var preview = {}; 855 preview.lossless = true; 856 preview.overflow = false; 857 preview.properties = []; 858 859 var isTableRowsRequest = secondLevelKeys === null || secondLevelKeys; 860 var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0; 861 862 var propertiesThreshold = { 863 properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount), 864 indexes: isTableRowsRequest ? 1000 : Math.max(100, firstLevelKeysCount) 865 }; 866 for (var o = object; injectedScript._isDefined(o); o = o.__proto__) 867 this._generateProtoPreview(o, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys); 868 return preview; 869 }, 870 871 /** 872 * @param {Object} object 873 * @param {Object} preview 874 * @param {Object} propertiesThreshold 875 * @param {Array.<string>=} firstLevelKeys 876 * @param {Array.<string>=} secondLevelKeys 877 */ 878 _generateProtoPreview: function(object, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys) 879 { 880 var propertyNames = firstLevelKeys ? firstLevelKeys : Object.keys(/** @type {!Object} */(object)); 881 try { 882 for (var i = 0; i < propertyNames.length; ++i) { 883 if (!propertiesThreshold.properties || !propertiesThreshold.indexes) { 884 preview.overflow = true; 885 preview.lossless = false; 886 break; 887 } 888 var name = propertyNames[i]; 889 if (this.subtype === "array" && name === "length") 890 continue; 891 892 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */(object), name); 893 if (!("value" in descriptor) || !descriptor.enumerable) { 894 preview.lossless = false; 895 continue; 896 } 897 898 var value = descriptor.value; 899 if (value === null) { 900 this._appendPropertyPreview(preview, { name: name, type: "object", value: "null" }, propertiesThreshold); 901 continue; 902 } 903 904 const maxLength = 100; 905 var type = typeof value; 906 907 if (InjectedScript.primitiveTypes[type]) { 908 if (type === "string") { 909 if (value.length > maxLength) { 910 value = this._abbreviateString(value, maxLength, true); 911 preview.lossless = false; 912 } 913 value = value.replace(/\n/g, "\u21B5"); 914 } 915 this._appendPropertyPreview(preview, { name: name, type: type, value: value + "" }, propertiesThreshold); 916 continue; 917 } 918 919 if (secondLevelKeys === null || secondLevelKeys) { 920 var subPreview = this._generatePreview(value, secondLevelKeys || undefined); 921 var property = { name: name, type: type, valuePreview: subPreview }; 922 this._appendPropertyPreview(preview, property, propertiesThreshold); 923 if (!subPreview.lossless) 924 preview.lossless = false; 925 if (subPreview.overflow) 926 preview.overflow = true; 927 continue; 928 } 929 930 preview.lossless = false; 931 932 var subtype = injectedScript._subtype(value); 933 var description = ""; 934 if (type !== "function") 935 description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp"); 936 937 var property = { name: name, type: type, value: description }; 938 if (subtype) 939 property.subtype = subtype; 940 this._appendPropertyPreview(preview, property, propertiesThreshold); 941 } 942 } catch (e) { 943 } 944 }, 945 946 /** 947 * @param {Object} preview 948 * @param {Object} property 949 * @param {Object} propertiesThreshold 950 */ 951 _appendPropertyPreview: function(preview, property, propertiesThreshold) 952 { 953 if (isNaN(property.name)) 954 propertiesThreshold.properties--; 955 else 956 propertiesThreshold.indexes--; 957 preview.properties.push(property); 958 }, 959 960 /** 961 * @param {string} string 962 * @param {number} maxLength 963 * @param {boolean=} middle 964 * @returns 965 */ 966 _abbreviateString: function(string, maxLength, middle) 967 { 968 if (string.length <= maxLength) 969 return string; 970 if (middle) { 971 var leftHalf = maxLength >> 1; 972 var rightHalf = maxLength - leftHalf - 1; 973 return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf); 974 } 975 return string.substr(0, maxLength) + "\u2026"; 976 } 977} 978/** 979 * @constructor 980 * @param {number} ordinal 981 * @param {Object} callFrame 982 */ 983InjectedScript.CallFrameProxy = function(ordinal, callFrame) 984{ 985 this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + injectedScriptId + "}"; 986 this.functionName = (callFrame.type === "function" ? callFrame.functionName : ""); 987 this.location = { scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column }; 988 this.scopeChain = this._wrapScopeChain(callFrame); 989 this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace"); 990} 991 992InjectedScript.CallFrameProxy.prototype = { 993 /** 994 * @param {Object} callFrame 995 * @return {!Array.<DebuggerAgent.Scope>} 996 */ 997 _wrapScopeChain: function(callFrame) 998 { 999 var scopeChain = callFrame.scopeChain; 1000 var scopeChainProxy = []; 1001 for (var i = 0; i < scopeChain.length; i++) { 1002 var scope = InjectedScript.CallFrameProxy._createScopeJson(callFrame.scopeType(i), scopeChain[i], "backtrace"); 1003 scopeChainProxy.push(scope); 1004 } 1005 return scopeChainProxy; 1006 } 1007} 1008 1009/** 1010 * @param {number} scopeTypeCode 1011 * @param {*} scopeObject 1012 * @param {string} groupId 1013 * @return {!DebuggerAgent.Scope} 1014 */ 1015InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeObject, groupId) { 1016 const GLOBAL_SCOPE = 0; 1017 const LOCAL_SCOPE = 1; 1018 const WITH_SCOPE = 2; 1019 const CLOSURE_SCOPE = 3; 1020 const CATCH_SCOPE = 4; 1021 1022 /** @type {!Object.<number, string>} */ 1023 var scopeTypeNames = {}; 1024 scopeTypeNames[GLOBAL_SCOPE] = "global"; 1025 scopeTypeNames[LOCAL_SCOPE] = "local"; 1026 scopeTypeNames[WITH_SCOPE] = "with"; 1027 scopeTypeNames[CLOSURE_SCOPE] = "closure"; 1028 scopeTypeNames[CATCH_SCOPE] = "catch"; 1029 1030 return { 1031 object: injectedScript._wrapObject(scopeObject, groupId), 1032 type: /** @type {DebuggerAgent.ScopeType} */ (scopeTypeNames[scopeTypeCode]) 1033 }; 1034} 1035 1036function BasicCommandLineAPI() 1037{ 1038 this.$_ = injectedScript._lastResult; 1039} 1040 1041return injectedScript; 1042}) 1043