OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (C) 2007 Apple Inc. All rights reserved. |
| 3 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 4 * |
| 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions |
| 7 * are met: |
| 8 * |
| 9 * 1. Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. |
| 11 * 2. Redistributions in binary form must reproduce the above copyright |
| 12 * notice, this list of conditions and the following disclaimer in the |
| 13 * documentation and/or other materials provided with the distribution. |
| 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| 15 * its contributors may be used to endorse or promote products derived |
| 16 * from this software without specific prior written permission. |
| 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 */ |
| 29 |
| 30 /** |
| 31 * @param {InjectedScriptHost} InjectedScriptHost |
| 32 * @param {Window} inspectedWindow |
| 33 * @param {number} injectedScriptId |
| 34 */ |
| 35 (function (InjectedScriptHost, inspectedWindow, injectedScriptId) { |
| 36 |
| 37 /** |
| 38 * Protect against Object overwritten by the user code. |
| 39 * @suppress {duplicate} |
| 40 */ |
| 41 var Object = /** @type {function(new:Object, *=)} */ ({}.constructor); |
| 42 |
| 43 /** |
| 44 * @param {!Array.<T>} array |
| 45 * @param {...} var_args |
| 46 * @template T |
| 47 */ |
| 48 function push(array, var_args) |
| 49 { |
| 50 for (var i = 1; i < arguments.length; ++i) |
| 51 array[array.length] = arguments[i]; |
| 52 } |
| 53 |
| 54 /** |
| 55 * @param {!Arguments.<T>} array |
| 56 * @param {number=} index |
| 57 * @return {!Array.<T>} |
| 58 * @template T |
| 59 */ |
| 60 function slice(array, index) |
| 61 { |
| 62 var result = []; |
| 63 for (var i = index || 0, j = 0; i < array.length; ++i, ++j) |
| 64 result[j] = array[i]; |
| 65 return result; |
| 66 } |
| 67 |
| 68 /** |
| 69 * @param {!Array.<T>} array1 |
| 70 * @param {!Array.<T>} array2 |
| 71 * @return {!Array.<T>} |
| 72 * @template T |
| 73 */ |
| 74 function concat(array1, array2) |
| 75 { |
| 76 var result = []; |
| 77 for (var i = 0; i < array1.length; ++i) |
| 78 push(result, array1[i]); |
| 79 for (var i = 0; i < array2.length; ++i) |
| 80 push(result, array2[i]); |
| 81 return result; |
| 82 } |
| 83 |
| 84 /** |
| 85 * @param {*} obj |
| 86 * @return {string} |
| 87 */ |
| 88 function toString(obj) |
| 89 { |
| 90 // We don't use String(obj) because it could be overriden. |
| 91 return "" + obj; |
| 92 } |
| 93 |
| 94 /** |
| 95 * @param {*} obj |
| 96 * @return {string} |
| 97 */ |
| 98 function toStringDescription(obj) |
| 99 { |
| 100 if (typeof obj === "number" && obj === 0 && 1 / obj < 0) |
| 101 return "-0"; // Negative zero. |
| 102 return "" + obj; |
| 103 } |
| 104 |
| 105 /** |
| 106 * Please use this bind, not the one from Function.prototype |
| 107 * @param {function(...)} func |
| 108 * @param {?Object} thisObject |
| 109 * @param {...} var_args |
| 110 * @return {function(...)} |
| 111 */ |
| 112 function bind(func, thisObject, var_args) |
| 113 { |
| 114 var args = slice(arguments, 2); |
| 115 |
| 116 /** |
| 117 * @param {...} var_args |
| 118 */ |
| 119 function bound(var_args) |
| 120 { |
| 121 return InjectedScriptHost.callFunction(func, thisObject, concat(args, sl
ice(arguments))); |
| 122 } |
| 123 bound.toString = function() |
| 124 { |
| 125 return "bound: " + func; |
| 126 }; |
| 127 return bound; |
| 128 } |
| 129 |
| 130 /** |
| 131 * @param {T} obj |
| 132 * @return {T} |
| 133 * @template T |
| 134 */ |
| 135 function nullifyObjectProto(obj) |
| 136 { |
| 137 if (obj && typeof obj === "object") |
| 138 obj.__proto__ = null; |
| 139 return obj; |
| 140 } |
| 141 |
| 142 /** |
| 143 * @param {*} obj |
| 144 * @return {boolean} |
| 145 */ |
| 146 function isUInt32(obj) |
| 147 { |
| 148 return typeof obj === "number" && obj >>> 0 === obj && (obj > 0 || 1 / obj >
0); |
| 149 } |
| 150 |
| 151 /** |
| 152 * FireBug's array detection. |
| 153 * @param {*} obj |
| 154 * @return {boolean} |
| 155 */ |
| 156 function isArrayLike(obj) |
| 157 { |
| 158 if (typeof obj !== "object") |
| 159 return false; |
| 160 try { |
| 161 if (typeof obj.splice === "function") |
| 162 return isUInt32(obj.length); |
| 163 } catch (e) { |
| 164 } |
| 165 return false; |
| 166 } |
| 167 |
| 168 /** |
| 169 * @param {number} a |
| 170 * @param {number} b |
| 171 * @return {number} |
| 172 */ |
| 173 function max(a, b) |
| 174 { |
| 175 return a > b ? a : b; |
| 176 } |
| 177 |
| 178 /** |
| 179 * FIXME: Remove once ES6 is supported natively by JS compiler. |
| 180 * @param {*} obj |
| 181 * @return {boolean} |
| 182 */ |
| 183 function isSymbol(obj) |
| 184 { |
| 185 var type = typeof obj; |
| 186 return (type === "symbol"); |
| 187 } |
| 188 |
| 189 /** |
| 190 * @constructor |
| 191 */ |
| 192 var InjectedScript = function() |
| 193 { |
| 194 /** @type {number} */ |
| 195 this._lastBoundObjectId = 1; |
| 196 /** @type {!Object.<number, (!Object|symbol)>} */ |
| 197 this._idToWrappedObject = { __proto__: null }; |
| 198 /** @type {!Object.<number, string>} */ |
| 199 this._idToObjectGroupName = { __proto__: null }; |
| 200 /** @type {!Object.<string, !Array.<number>>} */ |
| 201 this._objectGroups = { __proto__: null }; |
| 202 /** @type {!Object.<string, !Object>} */ |
| 203 this._modules = { __proto__: null }; |
| 204 } |
| 205 |
| 206 /** |
| 207 * @type {!Object.<string, boolean>} |
| 208 * @const |
| 209 */ |
| 210 InjectedScript.primitiveTypes = { |
| 211 "undefined": true, |
| 212 "boolean": true, |
| 213 "number": true, |
| 214 "string": true, |
| 215 __proto__: null |
| 216 } |
| 217 |
| 218 InjectedScript.prototype = { |
| 219 /** |
| 220 * @param {*} object |
| 221 * @return {boolean} |
| 222 */ |
| 223 isPrimitiveValue: function(object) |
| 224 { |
| 225 // FIXME(33716): typeof document.all is always 'undefined'. |
| 226 return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllC
ollection(object); |
| 227 }, |
| 228 |
| 229 /** |
| 230 * @param {*} object |
| 231 * @param {string} groupName |
| 232 * @param {boolean} canAccessInspectedWindow |
| 233 * @param {boolean} generatePreview |
| 234 * @return {!RuntimeAgent.RemoteObject} |
| 235 */ |
| 236 wrapObject: function(object, groupName, canAccessInspectedWindow, generatePr
eview) |
| 237 { |
| 238 if (canAccessInspectedWindow) |
| 239 return this._wrapObject(object, groupName, false, generatePreview); |
| 240 return this._fallbackWrapper(object); |
| 241 }, |
| 242 |
| 243 /** |
| 244 * @param {*} object |
| 245 * @return {!RuntimeAgent.RemoteObject} |
| 246 */ |
| 247 _fallbackWrapper: function(object) |
| 248 { |
| 249 var result = { __proto__: null }; |
| 250 result.type = typeof object; |
| 251 if (this.isPrimitiveValue(object)) |
| 252 result.value = object; |
| 253 else |
| 254 result.description = toString(object); |
| 255 return /** @type {!RuntimeAgent.RemoteObject} */ (result); |
| 256 }, |
| 257 |
| 258 /** |
| 259 * @param {boolean} canAccessInspectedWindow |
| 260 * @param {!Object} table |
| 261 * @param {!Array.<string>|string|boolean} columns |
| 262 * @return {!RuntimeAgent.RemoteObject} |
| 263 */ |
| 264 wrapTable: function(canAccessInspectedWindow, table, columns) |
| 265 { |
| 266 if (!canAccessInspectedWindow) |
| 267 return this._fallbackWrapper(table); |
| 268 var columnNames = null; |
| 269 if (typeof columns === "string") |
| 270 columns = [columns]; |
| 271 if (InjectedScriptHost.subtype(columns) === "array") { |
| 272 columnNames = []; |
| 273 for (var i = 0; i < columns.length; ++i) |
| 274 columnNames[i] = toString(columns[i]); |
| 275 } |
| 276 return this._wrapObject(table, "console", false, true, columnNames, true
); |
| 277 }, |
| 278 |
| 279 /** |
| 280 * @param {*} object |
| 281 */ |
| 282 inspectNode: function(object) |
| 283 { |
| 284 this._inspect(object); |
| 285 }, |
| 286 |
| 287 /** |
| 288 * @param {*} object |
| 289 * @return {*} |
| 290 */ |
| 291 _inspect: function(object) |
| 292 { |
| 293 if (arguments.length === 0) |
| 294 return; |
| 295 |
| 296 var objectId = this._wrapObject(object, ""); |
| 297 var hints = { __proto__: null }; |
| 298 |
| 299 InjectedScriptHost.inspect(objectId, hints); |
| 300 return object; |
| 301 }, |
| 302 |
| 303 /** |
| 304 * This method cannot throw. |
| 305 * @param {*} object |
| 306 * @param {string=} objectGroupName |
| 307 * @param {boolean=} forceValueType |
| 308 * @param {boolean=} generatePreview |
| 309 * @param {?Array.<string>=} columnNames |
| 310 * @param {boolean=} isTable |
| 311 * @return {!RuntimeAgent.RemoteObject} |
| 312 * @suppress {checkTypes} |
| 313 */ |
| 314 _wrapObject: function(object, objectGroupName, forceValueType, generatePrevi
ew, columnNames, isTable) |
| 315 { |
| 316 try { |
| 317 return new InjectedScript.RemoteObject(object, objectGroupName, forc
eValueType, generatePreview, columnNames, isTable); |
| 318 } catch (e) { |
| 319 try { |
| 320 var description = injectedScript._describe(e); |
| 321 } catch (ex) { |
| 322 var description = "<failed to convert exception to string>"; |
| 323 } |
| 324 return new InjectedScript.RemoteObject(description); |
| 325 } |
| 326 }, |
| 327 |
| 328 /** |
| 329 * @param {!Object|symbol} object |
| 330 * @param {string=} objectGroupName |
| 331 * @return {string} |
| 332 */ |
| 333 _bind: function(object, objectGroupName) |
| 334 { |
| 335 var id = this._lastBoundObjectId++; |
| 336 this._idToWrappedObject[id] = object; |
| 337 var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":"
+ id + "}"; |
| 338 if (objectGroupName) { |
| 339 var group = this._objectGroups[objectGroupName]; |
| 340 if (!group) { |
| 341 group = []; |
| 342 this._objectGroups[objectGroupName] = group; |
| 343 } |
| 344 push(group, id); |
| 345 this._idToObjectGroupName[id] = objectGroupName; |
| 346 } |
| 347 return objectId; |
| 348 }, |
| 349 |
| 350 /** |
| 351 * @param {string} objectId |
| 352 * @return {!Object} |
| 353 */ |
| 354 _parseObjectId: function(objectId) |
| 355 { |
| 356 return nullifyObjectProto(InjectedScriptHost.eval("(" + objectId + ")"))
; |
| 357 }, |
| 358 |
| 359 /** |
| 360 * @param {string} objectGroupName |
| 361 */ |
| 362 releaseObjectGroup: function(objectGroupName) |
| 363 { |
| 364 if (objectGroupName === "console") |
| 365 delete this._lastResult; |
| 366 var group = this._objectGroups[objectGroupName]; |
| 367 if (!group) |
| 368 return; |
| 369 for (var i = 0; i < group.length; i++) |
| 370 this._releaseObject(group[i]); |
| 371 delete this._objectGroups[objectGroupName]; |
| 372 }, |
| 373 |
| 374 /** |
| 375 * @param {string} methodName |
| 376 * @param {string} args |
| 377 * @return {*} |
| 378 */ |
| 379 dispatch: function(methodName, args) |
| 380 { |
| 381 var argsArray = InjectedScriptHost.eval("(" + args + ")"); |
| 382 var result = InjectedScriptHost.callFunction(this[methodName], this, arg
sArray); |
| 383 if (typeof result === "undefined") { |
| 384 inspectedWindow.console.error("Web Inspector error: InjectedScript.%
s returns undefined", methodName); |
| 385 result = null; |
| 386 } |
| 387 return result; |
| 388 }, |
| 389 |
| 390 /** |
| 391 * @param {string} objectId |
| 392 * @param {boolean} ownProperties |
| 393 * @param {boolean} accessorPropertiesOnly |
| 394 * @return {!Array.<!RuntimeAgent.PropertyDescriptor>|boolean} |
| 395 */ |
| 396 getProperties: function(objectId, ownProperties, accessorPropertiesOnly) |
| 397 { |
| 398 var parsedObjectId = this._parseObjectId(objectId); |
| 399 var object = this._objectForId(parsedObjectId); |
| 400 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id]; |
| 401 |
| 402 if (!this._isDefined(object) || isSymbol(object)) |
| 403 return false; |
| 404 object = /** @type {!Object} */ (object); |
| 405 var descriptors = this._propertyDescriptors(object, ownProperties, acces
sorPropertiesOnly); |
| 406 |
| 407 // Go over properties, wrap object values. |
| 408 for (var i = 0; i < descriptors.length; ++i) { |
| 409 var descriptor = descriptors[i]; |
| 410 if ("get" in descriptor) |
| 411 descriptor.get = this._wrapObject(descriptor.get, objectGroupNam
e); |
| 412 if ("set" in descriptor) |
| 413 descriptor.set = this._wrapObject(descriptor.set, objectGroupNam
e); |
| 414 if ("value" in descriptor) |
| 415 descriptor.value = this._wrapObject(descriptor.value, objectGrou
pName); |
| 416 if (!("configurable" in descriptor)) |
| 417 descriptor.configurable = false; |
| 418 if (!("enumerable" in descriptor)) |
| 419 descriptor.enumerable = false; |
| 420 if ("symbol" in descriptor) |
| 421 descriptor.symbol = this._wrapObject(descriptor.symbol, objectGr
oupName); |
| 422 } |
| 423 return descriptors; |
| 424 }, |
| 425 |
| 426 /** |
| 427 * @param {string} objectId |
| 428 * @return {!Array.<!Object>|boolean} |
| 429 */ |
| 430 getInternalProperties: function(objectId) |
| 431 { |
| 432 var parsedObjectId = this._parseObjectId(objectId); |
| 433 var object = this._objectForId(parsedObjectId); |
| 434 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id]; |
| 435 if (!this._isDefined(object) || isSymbol(object)) |
| 436 return false; |
| 437 object = /** @type {!Object} */ (object); |
| 438 var descriptors = []; |
| 439 var internalProperties = InjectedScriptHost.getInternalProperties(object
); |
| 440 if (internalProperties) { |
| 441 for (var i = 0; i < internalProperties.length; i++) { |
| 442 var property = internalProperties[i]; |
| 443 var descriptor = { |
| 444 name: property.name, |
| 445 value: this._wrapObject(property.value, objectGroupName), |
| 446 __proto__: null |
| 447 }; |
| 448 push(descriptors, descriptor); |
| 449 } |
| 450 } |
| 451 return descriptors; |
| 452 }, |
| 453 |
| 454 /** |
| 455 * @param {string} functionId |
| 456 * @return {!DebuggerAgent.FunctionDetails|string} |
| 457 */ |
| 458 getFunctionDetails: function(functionId) |
| 459 { |
| 460 var parsedFunctionId = this._parseObjectId(functionId); |
| 461 var func = this._objectForId(parsedFunctionId); |
| 462 if (typeof func !== "function") |
| 463 return "Cannot resolve function by id."; |
| 464 var details = nullifyObjectProto(InjectedScriptHost.functionDetails(func
)); |
| 465 if ("rawScopes" in details) { |
| 466 var objectGroupName = this._idToObjectGroupName[parsedFunctionId.id]
; |
| 467 var rawScopes = details.rawScopes; |
| 468 delete details.rawScopes; |
| 469 var scopes = []; |
| 470 for (var i = 0; i < rawScopes.length; ++i) |
| 471 scopes[i] = InjectedScript.CallFrameProxy._createScopeJson(rawSc
opes[i].type, rawScopes[i].object, objectGroupName); |
| 472 details.scopeChain = scopes; |
| 473 } |
| 474 return details; |
| 475 }, |
| 476 |
| 477 /** |
| 478 * @param {string} objectId |
| 479 * @return {!Array.<!Object>|string} |
| 480 */ |
| 481 getCollectionEntries: function(objectId) |
| 482 { |
| 483 var parsedObjectId = this._parseObjectId(objectId); |
| 484 var object = this._objectForId(parsedObjectId); |
| 485 if (!object || typeof object !== "object") |
| 486 return "Could not find object with given id"; |
| 487 var entries = InjectedScriptHost.collectionEntries(object); |
| 488 if (!entries) |
| 489 return "Object with given id is not a collection"; |
| 490 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id]; |
| 491 for (var i = 0; i < entries.length; ++i) { |
| 492 var entry = nullifyObjectProto(entries[i]); |
| 493 if ("key" in entry) |
| 494 entry.key = this._wrapObject(entry.key, objectGroupName); |
| 495 entry.value = this._wrapObject(entry.value, objectGroupName); |
| 496 entries[i] = entry; |
| 497 } |
| 498 return entries; |
| 499 }, |
| 500 |
| 501 /** |
| 502 * @param {string} objectId |
| 503 */ |
| 504 releaseObject: function(objectId) |
| 505 { |
| 506 var parsedObjectId = this._parseObjectId(objectId); |
| 507 this._releaseObject(parsedObjectId.id); |
| 508 }, |
| 509 |
| 510 /** |
| 511 * @param {number} id |
| 512 */ |
| 513 _releaseObject: function(id) |
| 514 { |
| 515 delete this._idToWrappedObject[id]; |
| 516 delete this._idToObjectGroupName[id]; |
| 517 }, |
| 518 |
| 519 /** |
| 520 * @param {!Object} object |
| 521 * @param {boolean=} ownProperties |
| 522 * @param {boolean=} accessorPropertiesOnly |
| 523 * @return {!Array.<!Object>} |
| 524 */ |
| 525 _propertyDescriptors: function(object, ownProperties, accessorPropertiesOnly
) |
| 526 { |
| 527 var descriptors = []; |
| 528 var propertyProcessed = { __proto__: null }; |
| 529 |
| 530 /** |
| 531 * @param {?Object} o |
| 532 * @param {!Array.<string|symbol>} properties |
| 533 */ |
| 534 function process(o, properties) |
| 535 { |
| 536 for (var i = 0; i < properties.length; ++i) { |
| 537 var property = properties[i]; |
| 538 if (propertyProcessed[property]) |
| 539 continue; |
| 540 |
| 541 var name = property; |
| 542 if (isSymbol(property)) |
| 543 name = injectedScript._describe(property); |
| 544 |
| 545 try { |
| 546 propertyProcessed[property] = true; |
| 547 var descriptor = nullifyObjectProto(InjectedScriptHost.suppr
essWarningsAndCallFunction(Object.getOwnPropertyDescriptor, Object, [o, property
])); |
| 548 if (descriptor) { |
| 549 if (accessorPropertiesOnly && !("get" in descriptor || "
set" in descriptor)) |
| 550 continue; |
| 551 } else { |
| 552 // Not all bindings provide proper descriptors. Fall bac
k to the writable, configurable property. |
| 553 if (accessorPropertiesOnly) |
| 554 continue; |
| 555 try { |
| 556 descriptor = { name: name, value: o[property], writa
ble: false, configurable: false, enumerable: false, __proto__: null }; |
| 557 if (o === object) |
| 558 descriptor.isOwn = true; |
| 559 push(descriptors, descriptor); |
| 560 } catch (e) { |
| 561 // Silent catch. |
| 562 } |
| 563 continue; |
| 564 } |
| 565 } catch (e) { |
| 566 if (accessorPropertiesOnly) |
| 567 continue; |
| 568 var descriptor = { __proto__: null }; |
| 569 descriptor.value = e; |
| 570 descriptor.wasThrown = true; |
| 571 } |
| 572 |
| 573 descriptor.name = name; |
| 574 if (o === object) |
| 575 descriptor.isOwn = true; |
| 576 if (isSymbol(property)) |
| 577 descriptor.symbol = property; |
| 578 push(descriptors, descriptor); |
| 579 } |
| 580 } |
| 581 |
| 582 for (var o = object; this._isDefined(o); o = o.__proto__) { |
| 583 // First call Object.keys() to enforce ordering of the property desc
riptors. |
| 584 process(o, Object.keys(/** @type {!Object} */ (o))); |
| 585 process(o, Object.getOwnPropertyNames(/** @type {!Object} */ (o))); |
| 586 if (Object.getOwnPropertySymbols) |
| 587 process(o, Object.getOwnPropertySymbols(/** @type {!Object} */ (
o))); |
| 588 |
| 589 if (ownProperties) { |
| 590 if (object.__proto__ && !accessorPropertiesOnly) |
| 591 push(descriptors, { name: "__proto__", value: object.__proto
__, writable: true, configurable: true, enumerable: false, isOwn: true, __proto_
_: null }); |
| 592 break; |
| 593 } |
| 594 } |
| 595 |
| 596 return descriptors; |
| 597 }, |
| 598 |
| 599 /** |
| 600 * @param {string} expression |
| 601 * @param {string} objectGroup |
| 602 * @param {boolean} injectCommandLineAPI |
| 603 * @param {boolean} returnByValue |
| 604 * @param {boolean} generatePreview |
| 605 * @return {*} |
| 606 */ |
| 607 evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByVa
lue, generatePreview) |
| 608 { |
| 609 return this._evaluateAndWrap(null, null, expression, objectGroup, false,
injectCommandLineAPI, returnByValue, generatePreview); |
| 610 }, |
| 611 |
| 612 /** |
| 613 * @param {string} objectId |
| 614 * @param {string} expression |
| 615 * @param {string} args |
| 616 * @param {boolean} returnByValue |
| 617 * @return {!Object|string} |
| 618 */ |
| 619 callFunctionOn: function(objectId, expression, args, returnByValue) |
| 620 { |
| 621 var parsedObjectId = this._parseObjectId(objectId); |
| 622 var object = this._objectForId(parsedObjectId); |
| 623 if (!this._isDefined(object)) |
| 624 return "Could not find object with given id"; |
| 625 |
| 626 if (args) { |
| 627 var resolvedArgs = []; |
| 628 args = InjectedScriptHost.eval(args); |
| 629 for (var i = 0; i < args.length; ++i) { |
| 630 try { |
| 631 resolvedArgs[i] = this._resolveCallArgument(args[i]); |
| 632 } catch (e) { |
| 633 return toString(e); |
| 634 } |
| 635 } |
| 636 } |
| 637 |
| 638 try { |
| 639 var objectGroup = this._idToObjectGroupName[parsedObjectId.id]; |
| 640 var func = InjectedScriptHost.eval("(" + expression + ")"); |
| 641 if (typeof func !== "function") |
| 642 return "Given expression does not evaluate to a function"; |
| 643 |
| 644 return { wasThrown: false, |
| 645 result: this._wrapObject(InjectedScriptHost.callFunction(fu
nc, object, resolvedArgs), objectGroup, returnByValue), |
| 646 __proto__: null }; |
| 647 } catch (e) { |
| 648 return this._createThrownValue(e, objectGroup, false); |
| 649 } |
| 650 }, |
| 651 |
| 652 /** |
| 653 * Resolves a value from CallArgument description. |
| 654 * @param {!RuntimeAgent.CallArgument} callArgumentJson |
| 655 * @return {*} resolved value |
| 656 * @throws {string} error message |
| 657 */ |
| 658 _resolveCallArgument: function(callArgumentJson) |
| 659 { |
| 660 callArgumentJson = nullifyObjectProto(callArgumentJson); |
| 661 var objectId = callArgumentJson.objectId; |
| 662 if (objectId) { |
| 663 var parsedArgId = this._parseObjectId(objectId); |
| 664 if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScri
ptId) |
| 665 throw "Arguments should belong to the same JavaScript world as t
he target object."; |
| 666 |
| 667 var resolvedArg = this._objectForId(parsedArgId); |
| 668 if (!this._isDefined(resolvedArg)) |
| 669 throw "Could not find object with given id"; |
| 670 |
| 671 return resolvedArg; |
| 672 } else if ("value" in callArgumentJson) { |
| 673 var value = callArgumentJson.value; |
| 674 if (callArgumentJson.type === "number" && typeof value !== "number") |
| 675 value = Number(value); |
| 676 return value; |
| 677 } |
| 678 return undefined; |
| 679 }, |
| 680 |
| 681 /** |
| 682 * @param {?function(string):*} evalFunction |
| 683 * @param {?Object} object |
| 684 * @param {string} expression |
| 685 * @param {string} objectGroup |
| 686 * @param {boolean} isEvalOnCallFrame |
| 687 * @param {boolean} injectCommandLineAPI |
| 688 * @param {boolean} returnByValue |
| 689 * @param {boolean} generatePreview |
| 690 * @param {!Array.<!Object>=} scopeChain |
| 691 * @return {!Object} |
| 692 */ |
| 693 _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, is
EvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview, scopeChai
n) |
| 694 { |
| 695 var wrappedResult = this._evaluateOn(evalFunction, object, objectGroup,
expression, isEvalOnCallFrame, injectCommandLineAPI, scopeChain); |
| 696 if (!wrappedResult.exceptionDetails) { |
| 697 return { wasThrown: false, |
| 698 result: this._wrapObject(wrappedResult.result, objectGroup,
returnByValue, generatePreview), |
| 699 __proto__: null }; |
| 700 } |
| 701 return this._createThrownValue(wrappedResult.result, objectGroup, genera
tePreview, wrappedResult.exceptionDetails); |
| 702 }, |
| 703 |
| 704 /** |
| 705 * @param {*} value |
| 706 * @param {string} objectGroup |
| 707 * @param {boolean} generatePreview |
| 708 * @param {!DebuggerAgent.ExceptionDetails=} exceptionDetails |
| 709 * @return {!Object} |
| 710 */ |
| 711 _createThrownValue: function(value, objectGroup, generatePreview, exceptionD
etails) |
| 712 { |
| 713 var remoteObject = this._wrapObject(value, objectGroup, false, generateP
review && !(value instanceof Error)); |
| 714 if (!remoteObject.description){ |
| 715 try { |
| 716 remoteObject.description = toStringDescription(value); |
| 717 } catch (e) {} |
| 718 } |
| 719 return { wasThrown: true, result: remoteObject, exceptionDetails: except
ionDetails, __proto__: null }; |
| 720 }, |
| 721 |
| 722 /** |
| 723 * @param {?function(string):*} evalFunction |
| 724 * @param {?Object} object |
| 725 * @param {string} objectGroup |
| 726 * @param {string} expression |
| 727 * @param {boolean} isEvalOnCallFrame |
| 728 * @param {boolean} injectCommandLineAPI |
| 729 * @param {!Array.<!Object>=} scopeChain |
| 730 * @return {*} |
| 731 */ |
| 732 _evaluateOn: function(evalFunction, object, objectGroup, expression, isEvalO
nCallFrame, injectCommandLineAPI, scopeChain) |
| 733 { |
| 734 // Only install command line api object for the time of evaluation. |
| 735 // Surround the expression in with statements to inject our command line
API so that |
| 736 // the window object properties still take more precedent than our API f
unctions. |
| 737 |
| 738 injectCommandLineAPI = injectCommandLineAPI && !("__commandLineAPI" in i
nspectedWindow); |
| 739 var injectScopeChain = scopeChain && scopeChain.length && !("__scopeChai
nForEval" in inspectedWindow); |
| 740 |
| 741 try { |
| 742 var prefix = ""; |
| 743 var suffix = ""; |
| 744 if (injectCommandLineAPI) { |
| 745 inspectedWindow.__commandLineAPI = new CommandLineAPI(this._comm
andLineAPIImpl, isEvalOnCallFrame ? object : null); |
| 746 prefix = "with (__commandLineAPI || { __proto__: null }) {"; |
| 747 suffix = "}"; |
| 748 } |
| 749 if (injectScopeChain) { |
| 750 inspectedWindow.__scopeChainForEval = scopeChain; |
| 751 for (var i = 0; i < scopeChain.length; ++i) { |
| 752 prefix = "with (__scopeChainForEval[" + i + "] || { __proto_
_: null }) {" + (suffix ? " " : "") + prefix; |
| 753 if (suffix) |
| 754 suffix += " }"; |
| 755 else |
| 756 suffix = "}"; |
| 757 } |
| 758 } |
| 759 |
| 760 if (prefix) |
| 761 expression = prefix + "\n" + expression + "\n" + suffix; |
| 762 var wrappedResult = evalFunction ? InjectedScriptHost.callFunction(e
valFunction, object, [expression]) : InjectedScriptHost.evaluateWithExceptionDet
ails(expression); |
| 763 if (objectGroup === "console" && !wrappedResult.exceptionDetails) |
| 764 this._lastResult = wrappedResult.result; |
| 765 return wrappedResult; |
| 766 } finally { |
| 767 if (injectCommandLineAPI) |
| 768 delete inspectedWindow.__commandLineAPI; |
| 769 if (injectScopeChain) |
| 770 delete inspectedWindow.__scopeChainForEval; |
| 771 } |
| 772 }, |
| 773 |
| 774 /** |
| 775 * @param {?Object} callFrame |
| 776 * @param {number} asyncOrdinal |
| 777 * @return {!Array.<!InjectedScript.CallFrameProxy>|boolean} |
| 778 */ |
| 779 wrapCallFrames: function(callFrame, asyncOrdinal) |
| 780 { |
| 781 if (!callFrame) |
| 782 return false; |
| 783 |
| 784 var result = []; |
| 785 var depth = 0; |
| 786 do { |
| 787 result[depth] = new InjectedScript.CallFrameProxy(depth, callFrame,
asyncOrdinal); |
| 788 callFrame = callFrame.caller; |
| 789 ++depth; |
| 790 } while (callFrame); |
| 791 return result; |
| 792 }, |
| 793 |
| 794 /** |
| 795 * @param {!Object} topCallFrame |
| 796 * @param {!Array.<!Object>} asyncCallStacks |
| 797 * @param {string} callFrameId |
| 798 * @param {string} expression |
| 799 * @param {string} objectGroup |
| 800 * @param {boolean} injectCommandLineAPI |
| 801 * @param {boolean} returnByValue |
| 802 * @param {boolean} generatePreview |
| 803 * @return {*} |
| 804 */ |
| 805 evaluateOnCallFrame: function(topCallFrame, asyncCallStacks, callFrameId, ex
pression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview) |
| 806 { |
| 807 var parsedCallFrameId = nullifyObjectProto(InjectedScriptHost.eval("(" +
callFrameId + ")")); |
| 808 var callFrame = this._callFrameForParsedId(topCallFrame, parsedCallFrame
Id, asyncCallStacks); |
| 809 if (!callFrame) |
| 810 return "Could not find call frame with given id"; |
| 811 if (parsedCallFrameId["asyncOrdinal"]) |
| 812 return this._evaluateAndWrap(null, null, expression, objectGroup, fa
lse, injectCommandLineAPI, returnByValue, generatePreview, callFrame.scopeChain)
; |
| 813 return this._evaluateAndWrap(callFrame.evaluateWithExceptionDetails, cal
lFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, gene
ratePreview); |
| 814 }, |
| 815 |
| 816 /** |
| 817 * @param {!Object} topCallFrame |
| 818 * @param {string} callFrameId |
| 819 * @return {*} |
| 820 */ |
| 821 restartFrame: function(topCallFrame, callFrameId) |
| 822 { |
| 823 var callFrame = this._callFrameForId(topCallFrame, callFrameId); |
| 824 if (!callFrame) |
| 825 return "Could not find call frame with given id"; |
| 826 var result = callFrame.restart(); |
| 827 if (result === false) |
| 828 result = "Restart frame is not supported"; |
| 829 return result; |
| 830 }, |
| 831 |
| 832 /** |
| 833 * @param {!Object} topCallFrame |
| 834 * @param {string} callFrameId |
| 835 * @return {*} a stepIn position array ready for protocol JSON or a string e
rror |
| 836 */ |
| 837 getStepInPositions: function(topCallFrame, callFrameId) |
| 838 { |
| 839 var callFrame = this._callFrameForId(topCallFrame, callFrameId); |
| 840 if (!callFrame) |
| 841 return "Could not find call frame with given id"; |
| 842 var stepInPositionsUnpacked = JSON.parse(callFrame.stepInPositions); |
| 843 if (typeof stepInPositionsUnpacked !== "object") |
| 844 return "Step in positions not available"; |
| 845 return stepInPositionsUnpacked; |
| 846 }, |
| 847 |
| 848 /** |
| 849 * Either callFrameId or functionObjectId must be specified. |
| 850 * @param {!Object} topCallFrame |
| 851 * @param {string|boolean} callFrameId or false |
| 852 * @param {string|boolean} functionObjectId or false |
| 853 * @param {number} scopeNumber |
| 854 * @param {string} variableName |
| 855 * @param {string} newValueJsonString RuntimeAgent.CallArgument structure se
rialized as string |
| 856 * @return {string|undefined} undefined if success or an error message |
| 857 */ |
| 858 setVariableValue: function(topCallFrame, callFrameId, functionObjectId, scop
eNumber, variableName, newValueJsonString) |
| 859 { |
| 860 var setter; |
| 861 if (typeof callFrameId === "string") { |
| 862 var callFrame = this._callFrameForId(topCallFrame, callFrameId); |
| 863 if (!callFrame) |
| 864 return "Could not find call frame with given id"; |
| 865 setter = bind(callFrame.setVariableValue, callFrame); |
| 866 } else { |
| 867 var parsedFunctionId = this._parseObjectId(/** @type {string} */ (fu
nctionObjectId)); |
| 868 var func = this._objectForId(parsedFunctionId); |
| 869 if (typeof func !== "function") |
| 870 return "Cannot resolve function by id."; |
| 871 setter = bind(InjectedScriptHost.setFunctionVariableValue, InjectedS
criptHost, func); |
| 872 } |
| 873 var newValueJson; |
| 874 try { |
| 875 newValueJson = InjectedScriptHost.eval("(" + newValueJsonString + ")
"); |
| 876 } catch (e) { |
| 877 return "Failed to parse new value JSON " + newValueJsonString + " :
" + e; |
| 878 } |
| 879 var resolvedValue; |
| 880 try { |
| 881 resolvedValue = this._resolveCallArgument(newValueJson); |
| 882 } catch (e) { |
| 883 return toString(e); |
| 884 } |
| 885 try { |
| 886 setter(scopeNumber, variableName, resolvedValue); |
| 887 } catch (e) { |
| 888 return "Failed to change variable value: " + e; |
| 889 } |
| 890 return undefined; |
| 891 }, |
| 892 |
| 893 /** |
| 894 * @param {!Object} topCallFrame |
| 895 * @param {string} callFrameId |
| 896 * @return {?Object} |
| 897 */ |
| 898 _callFrameForId: function(topCallFrame, callFrameId) |
| 899 { |
| 900 var parsedCallFrameId = nullifyObjectProto(InjectedScriptHost.eval("(" +
callFrameId + ")")); |
| 901 return this._callFrameForParsedId(topCallFrame, parsedCallFrameId, []); |
| 902 }, |
| 903 |
| 904 /** |
| 905 * @param {!Object} topCallFrame |
| 906 * @param {!Object} parsedCallFrameId |
| 907 * @param {!Array.<!Object>} asyncCallStacks |
| 908 * @return {?Object} |
| 909 */ |
| 910 _callFrameForParsedId: function(topCallFrame, parsedCallFrameId, asyncCallSt
acks) |
| 911 { |
| 912 var asyncOrdinal = parsedCallFrameId["asyncOrdinal"]; // 1-based index |
| 913 if (asyncOrdinal) |
| 914 topCallFrame = asyncCallStacks[asyncOrdinal - 1]; |
| 915 var ordinal = parsedCallFrameId["ordinal"]; |
| 916 var callFrame = topCallFrame; |
| 917 while (--ordinal >= 0 && callFrame) |
| 918 callFrame = callFrame.caller; |
| 919 return callFrame; |
| 920 }, |
| 921 |
| 922 /** |
| 923 * @param {!Object} objectId |
| 924 * @return {!Object|symbol} |
| 925 */ |
| 926 _objectForId: function(objectId) |
| 927 { |
| 928 return this._idToWrappedObject[objectId.id]; |
| 929 }, |
| 930 |
| 931 /** |
| 932 * @param {string} objectId |
| 933 * @return {!Object|symbol} |
| 934 */ |
| 935 findObjectById: function(objectId) |
| 936 { |
| 937 var parsedObjectId = this._parseObjectId(objectId); |
| 938 return this._objectForId(parsedObjectId); |
| 939 }, |
| 940 |
| 941 /** |
| 942 * @param {string} objectId |
| 943 * @return {?Node} |
| 944 */ |
| 945 nodeForObjectId: function(objectId) |
| 946 { |
| 947 var object = this.findObjectById(objectId); |
| 948 if (!object || this._subtype(object) !== "node") |
| 949 return null; |
| 950 return /** @type {!Node} */ (object); |
| 951 }, |
| 952 |
| 953 /** |
| 954 * @param {string} name |
| 955 * @return {!Object} |
| 956 */ |
| 957 module: function(name) |
| 958 { |
| 959 return this._modules[name]; |
| 960 }, |
| 961 |
| 962 /** |
| 963 * @param {string} name |
| 964 * @param {string} source |
| 965 * @return {?Object} |
| 966 */ |
| 967 injectModule: function(name, source) |
| 968 { |
| 969 delete this._modules[name]; |
| 970 var moduleFunction = InjectedScriptHost.eval("(" + source + ")"); |
| 971 if (typeof moduleFunction !== "function") { |
| 972 inspectedWindow.console.error("Web Inspector error: A function was e
xpected for module %s evaluation", name); |
| 973 return null; |
| 974 } |
| 975 var module = InjectedScriptHost.callFunction(moduleFunction, inspectedWi
ndow, [InjectedScriptHost, inspectedWindow, injectedScriptId, this]); |
| 976 this._modules[name] = module; |
| 977 return module; |
| 978 }, |
| 979 |
| 980 /** |
| 981 * @param {*} object |
| 982 * @return {boolean} |
| 983 */ |
| 984 _isDefined: function(object) |
| 985 { |
| 986 return !!object || this._isHTMLAllCollection(object); |
| 987 }, |
| 988 |
| 989 /** |
| 990 * @param {*} object |
| 991 * @return {boolean} |
| 992 */ |
| 993 _isHTMLAllCollection: function(object) |
| 994 { |
| 995 // document.all is reported as undefined, but we still want to process i
t. |
| 996 return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCo
llection(object); |
| 997 }, |
| 998 |
| 999 /** |
| 1000 * @param {*} obj |
| 1001 * @return {?string} |
| 1002 */ |
| 1003 _subtype: function(obj) |
| 1004 { |
| 1005 if (obj === null) |
| 1006 return "null"; |
| 1007 |
| 1008 if (this.isPrimitiveValue(obj)) |
| 1009 return null; |
| 1010 |
| 1011 var subtype = InjectedScriptHost.subtype(obj); |
| 1012 if (subtype) |
| 1013 return subtype; |
| 1014 |
| 1015 if (isArrayLike(obj)) |
| 1016 return "array"; |
| 1017 |
| 1018 // If owning frame has navigated to somewhere else window properties wil
l be undefined. |
| 1019 return null; |
| 1020 }, |
| 1021 |
| 1022 /** |
| 1023 * @param {*} obj |
| 1024 * @return {?string} |
| 1025 */ |
| 1026 _describe: function(obj) |
| 1027 { |
| 1028 if (this.isPrimitiveValue(obj)) |
| 1029 return null; |
| 1030 |
| 1031 var subtype = this._subtype(obj); |
| 1032 |
| 1033 if (subtype === "regexp") |
| 1034 return toString(obj); |
| 1035 |
| 1036 if (subtype === "date") |
| 1037 return toString(obj); |
| 1038 |
| 1039 if (subtype === "node") { |
| 1040 var description = obj.nodeName.toLowerCase(); |
| 1041 switch (obj.nodeType) { |
| 1042 case 1 /* Node.ELEMENT_NODE */: |
| 1043 description += obj.id ? "#" + obj.id : ""; |
| 1044 var className = obj.className; |
| 1045 description += (className && typeof className === "string") ? ".
" + className.trim().replace(/\s+/g, ".") : ""; |
| 1046 break; |
| 1047 case 10 /*Node.DOCUMENT_TYPE_NODE */: |
| 1048 description = "<!DOCTYPE " + description + ">"; |
| 1049 break; |
| 1050 } |
| 1051 return description; |
| 1052 } |
| 1053 |
| 1054 var className = InjectedScriptHost.internalConstructorName(obj); |
| 1055 if (subtype === "array") { |
| 1056 if (typeof obj.length === "number") |
| 1057 className += "[" + obj.length + "]"; |
| 1058 return className; |
| 1059 } |
| 1060 |
| 1061 // NodeList in JSC is a function, check for array prior to this. |
| 1062 if (typeof obj === "function") |
| 1063 return toString(obj); |
| 1064 |
| 1065 if (isSymbol(obj)) { |
| 1066 try { |
| 1067 return InjectedScriptHost.callFunction(Symbol.prototype.toString
, obj) || "Symbol"; |
| 1068 } catch (e) { |
| 1069 return "Symbol"; |
| 1070 } |
| 1071 } |
| 1072 |
| 1073 if (obj instanceof Error && !!obj.message) |
| 1074 return className + ": " + obj.message; |
| 1075 |
| 1076 return className; |
| 1077 } |
| 1078 } |
| 1079 |
| 1080 /** |
| 1081 * @type {!InjectedScript} |
| 1082 * @const |
| 1083 */ |
| 1084 var injectedScript = new InjectedScript(); |
| 1085 |
| 1086 /** |
| 1087 * @constructor |
| 1088 * @param {*} object |
| 1089 * @param {string=} objectGroupName |
| 1090 * @param {boolean=} forceValueType |
| 1091 * @param {boolean=} generatePreview |
| 1092 * @param {?Array.<string>=} columnNames |
| 1093 * @param {boolean=} isTable |
| 1094 */ |
| 1095 InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType,
generatePreview, columnNames, isTable) |
| 1096 { |
| 1097 this.type = typeof object; |
| 1098 if (injectedScript.isPrimitiveValue(object) || object === null || forceValue
Type) { |
| 1099 // We don't send undefined values over JSON. |
| 1100 if (this.type !== "undefined") |
| 1101 this.value = object; |
| 1102 |
| 1103 // Null object is object with 'null' subtype. |
| 1104 if (object === null) |
| 1105 this.subtype = "null"; |
| 1106 |
| 1107 // Provide user-friendly number values. |
| 1108 if (this.type === "number") { |
| 1109 this.description = toStringDescription(object); |
| 1110 // Override "value" property for values that can not be JSON-stringi
fied. |
| 1111 switch (this.description) { |
| 1112 case "NaN": |
| 1113 case "Infinity": |
| 1114 case "-Infinity": |
| 1115 case "-0": |
| 1116 this.value = this.description; |
| 1117 break; |
| 1118 } |
| 1119 } |
| 1120 |
| 1121 return; |
| 1122 } |
| 1123 |
| 1124 object = /** @type {!Object} */ (object); |
| 1125 |
| 1126 this.objectId = injectedScript._bind(object, objectGroupName); |
| 1127 var subtype = injectedScript._subtype(object); |
| 1128 if (subtype) |
| 1129 this.subtype = subtype; |
| 1130 var className = InjectedScriptHost.internalConstructorName(object); |
| 1131 if (className) |
| 1132 this.className = className; |
| 1133 this.description = injectedScript._describe(object); |
| 1134 |
| 1135 if (generatePreview && (this.type === "object" || injectedScript._isHTMLAllC
ollection(object))) |
| 1136 this.preview = this._generatePreview(object, undefined, columnNames, isT
able); |
| 1137 } |
| 1138 |
| 1139 InjectedScript.RemoteObject.prototype = { |
| 1140 /** |
| 1141 * @param {!Object} object |
| 1142 * @param {?Array.<string>=} firstLevelKeys |
| 1143 * @param {?Array.<string>=} secondLevelKeys |
| 1144 * @param {boolean=} isTable |
| 1145 * @return {!RuntimeAgent.ObjectPreview} preview |
| 1146 */ |
| 1147 _generatePreview: function(object, firstLevelKeys, secondLevelKeys, isTable) |
| 1148 { |
| 1149 var preview = { __proto__: null }; |
| 1150 preview.lossless = true; |
| 1151 preview.overflow = false; |
| 1152 preview.properties = []; |
| 1153 |
| 1154 var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0; |
| 1155 |
| 1156 var propertiesThreshold = { |
| 1157 properties: isTable ? 1000 : max(5, firstLevelKeysCount), |
| 1158 indexes: isTable ? 1000 : max(100, firstLevelKeysCount) |
| 1159 }; |
| 1160 |
| 1161 try { |
| 1162 var descriptors = injectedScript._propertyDescriptors(object); |
| 1163 |
| 1164 if (firstLevelKeys) { |
| 1165 var nameToDescriptors = { __proto__: null }; |
| 1166 for (var i = 0; i < descriptors.length; ++i) { |
| 1167 var descriptor = descriptors[i]; |
| 1168 nameToDescriptors["#" + descriptor.name] = descriptor; |
| 1169 } |
| 1170 descriptors = []; |
| 1171 for (var i = 0; i < firstLevelKeys.length; ++i) |
| 1172 descriptors[i] = nameToDescriptors["#" + firstLevelKeys[i]]; |
| 1173 } |
| 1174 |
| 1175 this._appendPropertyDescriptors(preview, descriptors, propertiesThre
shold, secondLevelKeys, isTable); |
| 1176 if (propertiesThreshold.indexes < 0 || propertiesThreshold.propertie
s < 0) |
| 1177 return preview; |
| 1178 |
| 1179 // Add internal properties to preview. |
| 1180 var internalProperties = InjectedScriptHost.getInternalProperties(ob
ject) || []; |
| 1181 for (var i = 0; i < internalProperties.length; ++i) { |
| 1182 internalProperties[i] = nullifyObjectProto(internalProperties[i]
); |
| 1183 internalProperties[i].enumerable = true; |
| 1184 } |
| 1185 this._appendPropertyDescriptors(preview, internalProperties, propert
iesThreshold, secondLevelKeys, isTable); |
| 1186 |
| 1187 } catch (e) { |
| 1188 preview.lossless = false; |
| 1189 } |
| 1190 |
| 1191 return preview; |
| 1192 }, |
| 1193 |
| 1194 /** |
| 1195 * @param {!RuntimeAgent.ObjectPreview} preview |
| 1196 * @param {!Array.<!Object>} descriptors |
| 1197 * @param {!Object} propertiesThreshold |
| 1198 * @param {?Array.<string>=} secondLevelKeys |
| 1199 * @param {boolean=} isTable |
| 1200 */ |
| 1201 _appendPropertyDescriptors: function(preview, descriptors, propertiesThresho
ld, secondLevelKeys, isTable) |
| 1202 { |
| 1203 for (var i = 0; i < descriptors.length; ++i) { |
| 1204 if (propertiesThreshold.indexes < 0 || propertiesThreshold.propertie
s < 0) |
| 1205 break; |
| 1206 |
| 1207 var descriptor = descriptors[i]; |
| 1208 if (!descriptor) |
| 1209 continue; |
| 1210 if (descriptor.wasThrown) { |
| 1211 preview.lossless = false; |
| 1212 continue; |
| 1213 } |
| 1214 if (!descriptor.enumerable && !descriptor.isOwn) |
| 1215 continue; |
| 1216 |
| 1217 var name = descriptor.name; |
| 1218 if (name === "__proto__") |
| 1219 continue; |
| 1220 if (this.subtype === "array" && name === "length") |
| 1221 continue; |
| 1222 |
| 1223 if (!("value" in descriptor)) { |
| 1224 preview.lossless = false; |
| 1225 this._appendPropertyPreview(preview, { name: name, type: "access
or", __proto__: null }, propertiesThreshold); |
| 1226 continue; |
| 1227 } |
| 1228 |
| 1229 var value = descriptor.value; |
| 1230 if (value === null) { |
| 1231 this._appendPropertyPreview(preview, { name: name, type: "object
", subtype: "null", value: "null", __proto__: null }, propertiesThreshold); |
| 1232 continue; |
| 1233 } |
| 1234 |
| 1235 var type = typeof value; |
| 1236 if (!descriptor.enumerable && type === "function") |
| 1237 continue; |
| 1238 if (type === "undefined" && injectedScript._isHTMLAllCollection(valu
e)) |
| 1239 type = "object"; |
| 1240 |
| 1241 var maxLength = 100; |
| 1242 if (InjectedScript.primitiveTypes[type]) { |
| 1243 if (type === "string" && value.length > maxLength) { |
| 1244 value = this._abbreviateString(value, maxLength, true); |
| 1245 preview.lossless = false; |
| 1246 } |
| 1247 this._appendPropertyPreview(preview, { name: name, type: type, v
alue: toStringDescription(value), __proto__: null }, propertiesThreshold); |
| 1248 continue; |
| 1249 } |
| 1250 |
| 1251 var property = { name: name, type: type, __proto__: null }; |
| 1252 var subtype = injectedScript._subtype(value); |
| 1253 if (subtype) |
| 1254 property.subtype = subtype; |
| 1255 |
| 1256 if (secondLevelKeys === null || secondLevelKeys) { |
| 1257 var subPreview = this._generatePreview(value, secondLevelKeys ||
undefined, undefined, isTable); |
| 1258 property.valuePreview = subPreview; |
| 1259 if (!subPreview.lossless) |
| 1260 preview.lossless = false; |
| 1261 if (subPreview.overflow) |
| 1262 preview.overflow = true; |
| 1263 } else { |
| 1264 var description = ""; |
| 1265 if (type !== "function") |
| 1266 description = this._abbreviateString(/** @type {string} */ (
injectedScript._describe(value)), maxLength, subtype === "regexp"); |
| 1267 property.value = description; |
| 1268 preview.lossless = false; |
| 1269 } |
| 1270 this._appendPropertyPreview(preview, property, propertiesThreshold); |
| 1271 } |
| 1272 }, |
| 1273 |
| 1274 /** |
| 1275 * @param {!RuntimeAgent.ObjectPreview} preview |
| 1276 * @param {!Object} property |
| 1277 * @param {!Object} propertiesThreshold |
| 1278 */ |
| 1279 _appendPropertyPreview: function(preview, property, propertiesThreshold) |
| 1280 { |
| 1281 if (toString(property.name >>> 0) === property.name) |
| 1282 propertiesThreshold.indexes--; |
| 1283 else |
| 1284 propertiesThreshold.properties--; |
| 1285 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties <
0) { |
| 1286 preview.overflow = true; |
| 1287 preview.lossless = false; |
| 1288 } else { |
| 1289 push(preview.properties, property); |
| 1290 } |
| 1291 }, |
| 1292 |
| 1293 /** |
| 1294 * @param {string} string |
| 1295 * @param {number} maxLength |
| 1296 * @param {boolean=} middle |
| 1297 * @return {string} |
| 1298 */ |
| 1299 _abbreviateString: function(string, maxLength, middle) |
| 1300 { |
| 1301 if (string.length <= maxLength) |
| 1302 return string; |
| 1303 if (middle) { |
| 1304 var leftHalf = maxLength >> 1; |
| 1305 var rightHalf = maxLength - leftHalf - 1; |
| 1306 return string.substr(0, leftHalf) + "\u2026" + string.substr(string.
length - rightHalf, rightHalf); |
| 1307 } |
| 1308 return string.substr(0, maxLength) + "\u2026"; |
| 1309 }, |
| 1310 |
| 1311 __proto__: null |
| 1312 } |
| 1313 /** |
| 1314 * @constructor |
| 1315 * @param {number} ordinal |
| 1316 * @param {!Object} callFrame |
| 1317 * @param {number} asyncOrdinal |
| 1318 */ |
| 1319 InjectedScript.CallFrameProxy = function(ordinal, callFrame, asyncOrdinal) |
| 1320 { |
| 1321 this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + in
jectedScriptId + (asyncOrdinal ? ",\"asyncOrdinal\":" + asyncOrdinal : "") + "}"
; |
| 1322 this.functionName = (callFrame.type === "function" ? callFrame.functionName
: ""); |
| 1323 this.location = { scriptId: toString(callFrame.sourceID), lineNumber: callFr
ame.line, columnNumber: callFrame.column, __proto__: null }; |
| 1324 this.scopeChain = this._wrapScopeChain(callFrame); |
| 1325 this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace"); |
| 1326 if (callFrame.isAtReturn) |
| 1327 this.returnValue = injectedScript._wrapObject(callFrame.returnValue, "ba
cktrace"); |
| 1328 } |
| 1329 |
| 1330 InjectedScript.CallFrameProxy.prototype = { |
| 1331 /** |
| 1332 * @param {!Object} callFrame |
| 1333 * @return {!Array.<!DebuggerAgent.Scope>} |
| 1334 */ |
| 1335 _wrapScopeChain: function(callFrame) |
| 1336 { |
| 1337 var scopeChain = callFrame.scopeChain; |
| 1338 var scopeChainProxy = []; |
| 1339 for (var i = 0; i < scopeChain.length; ++i) |
| 1340 scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(
callFrame.scopeType(i), scopeChain[i], "backtrace"); |
| 1341 return scopeChainProxy; |
| 1342 }, |
| 1343 |
| 1344 __proto__: null |
| 1345 } |
| 1346 |
| 1347 /** |
| 1348 * @param {number} scopeTypeCode |
| 1349 * @param {*} scopeObject |
| 1350 * @param {string} groupId |
| 1351 * @return {!DebuggerAgent.Scope} |
| 1352 */ |
| 1353 InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeOb
ject, groupId) |
| 1354 { |
| 1355 var GLOBAL_SCOPE = 0; |
| 1356 var LOCAL_SCOPE = 1; |
| 1357 var WITH_SCOPE = 2; |
| 1358 var CLOSURE_SCOPE = 3; |
| 1359 var CATCH_SCOPE = 4; |
| 1360 |
| 1361 /** @type {!Object.<number, string>} */ |
| 1362 var scopeTypeNames = { __proto__: null }; |
| 1363 scopeTypeNames[GLOBAL_SCOPE] = "global"; |
| 1364 scopeTypeNames[LOCAL_SCOPE] = "local"; |
| 1365 scopeTypeNames[WITH_SCOPE] = "with"; |
| 1366 scopeTypeNames[CLOSURE_SCOPE] = "closure"; |
| 1367 scopeTypeNames[CATCH_SCOPE] = "catch"; |
| 1368 |
| 1369 return { |
| 1370 object: injectedScript._wrapObject(scopeObject, groupId), |
| 1371 type: /** @type {!DebuggerAgent.ScopeType} */ (scopeTypeNames[scopeTypeC
ode]), |
| 1372 __proto__: null |
| 1373 }; |
| 1374 } |
| 1375 |
| 1376 /** |
| 1377 * @constructor |
| 1378 * @param {!CommandLineAPIImpl} commandLineAPIImpl |
| 1379 * @param {?Object} callFrame |
| 1380 */ |
| 1381 function CommandLineAPI(commandLineAPIImpl, callFrame) |
| 1382 { |
| 1383 /** |
| 1384 * @param {string} member |
| 1385 * @return {boolean} |
| 1386 */ |
| 1387 function inScopeVariables(member) |
| 1388 { |
| 1389 if (!callFrame) |
| 1390 return false; |
| 1391 |
| 1392 var scopeChain = callFrame.scopeChain; |
| 1393 for (var i = 0; i < scopeChain.length; ++i) { |
| 1394 if (member in scopeChain[i]) |
| 1395 return true; |
| 1396 } |
| 1397 return false; |
| 1398 } |
| 1399 |
| 1400 /** |
| 1401 * @param {string} name The name of the method for which a toString method s
hould be generated. |
| 1402 * @return {function():string} |
| 1403 */ |
| 1404 function customToStringMethod(name) |
| 1405 { |
| 1406 return function() |
| 1407 { |
| 1408 var funcArgsSyntax = ""; |
| 1409 try { |
| 1410 var funcSyntax = "" + commandLineAPIImpl[name]; |
| 1411 funcSyntax = funcSyntax.replace(/\n/g, " "); |
| 1412 funcSyntax = funcSyntax.replace(/^function[^\(]*\(([^\)]*)\).*$/
, "$1"); |
| 1413 funcSyntax = funcSyntax.replace(/\s*,\s*/g, ", "); |
| 1414 funcSyntax = funcSyntax.replace(/\bopt_(\w+)\b/g, "[$1]"); |
| 1415 funcArgsSyntax = funcSyntax.trim(); |
| 1416 } catch (e) { |
| 1417 } |
| 1418 return "function " + name + "(" + funcArgsSyntax + ") { [Command Lin
e API] }"; |
| 1419 }; |
| 1420 } |
| 1421 |
| 1422 for (var i = 0; i < CommandLineAPI.members_.length; ++i) { |
| 1423 var member = CommandLineAPI.members_[i]; |
| 1424 if (member in inspectedWindow || inScopeVariables(member)) |
| 1425 continue; |
| 1426 |
| 1427 this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl); |
| 1428 this[member].toString = customToStringMethod(member); |
| 1429 } |
| 1430 |
| 1431 for (var i = 0; i < 5; ++i) { |
| 1432 var member = "$" + i; |
| 1433 if (member in inspectedWindow || inScopeVariables(member)) |
| 1434 continue; |
| 1435 |
| 1436 this.__defineGetter__("$" + i, bind(commandLineAPIImpl._inspectedObject,
commandLineAPIImpl, i)); |
| 1437 } |
| 1438 |
| 1439 this.$_ = injectedScript._lastResult; |
| 1440 |
| 1441 this.__proto__ = null; |
| 1442 } |
| 1443 |
| 1444 // NOTE: Please keep the list of API methods below snchronized to that in WebIns
pector.RuntimeModel! |
| 1445 // NOTE: Argument names of these methods will be printed in the console, so use
pretty names! |
| 1446 /** |
| 1447 * @type {!Array.<string>} |
| 1448 * @const |
| 1449 */ |
| 1450 CommandLineAPI.members_ = [ |
| 1451 "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd", |
| 1452 "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventLis
teners", |
| 1453 "debug", "undebug", "monitor", "unmonitor", "table" |
| 1454 ]; |
| 1455 |
| 1456 /** |
| 1457 * @constructor |
| 1458 */ |
| 1459 function CommandLineAPIImpl() |
| 1460 { |
| 1461 } |
| 1462 |
| 1463 CommandLineAPIImpl.prototype = { |
| 1464 /** |
| 1465 * @param {string} selector |
| 1466 * @param {!Node=} opt_startNode |
| 1467 * @return {*} |
| 1468 */ |
| 1469 $: function (selector, opt_startNode) |
| 1470 { |
| 1471 if (this._canQuerySelectorOnNode(opt_startNode)) |
| 1472 return opt_startNode.querySelector(selector); |
| 1473 |
| 1474 return inspectedWindow.document.querySelector(selector); |
| 1475 }, |
| 1476 |
| 1477 /** |
| 1478 * @param {string} selector |
| 1479 * @param {!Node=} opt_startNode |
| 1480 * @return {*} |
| 1481 */ |
| 1482 $$: function (selector, opt_startNode) |
| 1483 { |
| 1484 if (this._canQuerySelectorOnNode(opt_startNode)) |
| 1485 return opt_startNode.querySelectorAll(selector); |
| 1486 return inspectedWindow.document.querySelectorAll(selector); |
| 1487 }, |
| 1488 |
| 1489 /** |
| 1490 * @param {!Node=} node |
| 1491 * @return {boolean} |
| 1492 */ |
| 1493 _canQuerySelectorOnNode: function(node) |
| 1494 { |
| 1495 return !!node && InjectedScriptHost.subtype(node) === "node" && (node.no
deType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nod
eType === Node.DOCUMENT_FRAGMENT_NODE); |
| 1496 }, |
| 1497 |
| 1498 /** |
| 1499 * @param {string} xpath |
| 1500 * @param {!Node=} opt_startNode |
| 1501 * @return {*} |
| 1502 */ |
| 1503 $x: function(xpath, opt_startNode) |
| 1504 { |
| 1505 var doc = (opt_startNode && opt_startNode.ownerDocument) || inspectedWin
dow.document; |
| 1506 var result = doc.evaluate(xpath, opt_startNode || doc, null, XPathResult
.ANY_TYPE, null); |
| 1507 switch (result.resultType) { |
| 1508 case XPathResult.NUMBER_TYPE: |
| 1509 return result.numberValue; |
| 1510 case XPathResult.STRING_TYPE: |
| 1511 return result.stringValue; |
| 1512 case XPathResult.BOOLEAN_TYPE: |
| 1513 return result.booleanValue; |
| 1514 default: |
| 1515 var nodes = []; |
| 1516 var node; |
| 1517 while (node = result.iterateNext()) |
| 1518 push(nodes, node); |
| 1519 return nodes; |
| 1520 } |
| 1521 }, |
| 1522 |
| 1523 /** |
| 1524 * @return {*} |
| 1525 */ |
| 1526 dir: function(var_args) |
| 1527 { |
| 1528 return InjectedScriptHost.callFunction(inspectedWindow.console.dir, insp
ectedWindow.console, slice(arguments)); |
| 1529 }, |
| 1530 |
| 1531 /** |
| 1532 * @return {*} |
| 1533 */ |
| 1534 dirxml: function(var_args) |
| 1535 { |
| 1536 return InjectedScriptHost.callFunction(inspectedWindow.console.dirxml, i
nspectedWindow.console, slice(arguments)); |
| 1537 }, |
| 1538 |
| 1539 /** |
| 1540 * @return {!Array.<string>} |
| 1541 */ |
| 1542 keys: function(object) |
| 1543 { |
| 1544 return Object.keys(object); |
| 1545 }, |
| 1546 |
| 1547 /** |
| 1548 * @return {!Array.<*>} |
| 1549 */ |
| 1550 values: function(object) |
| 1551 { |
| 1552 var result = []; |
| 1553 for (var key in object) |
| 1554 push(result, object[key]); |
| 1555 return result; |
| 1556 }, |
| 1557 |
| 1558 /** |
| 1559 * @return {*} |
| 1560 */ |
| 1561 profile: function(opt_title) |
| 1562 { |
| 1563 return InjectedScriptHost.callFunction(inspectedWindow.console.profile,
inspectedWindow.console, slice(arguments)); |
| 1564 }, |
| 1565 |
| 1566 /** |
| 1567 * @return {*} |
| 1568 */ |
| 1569 profileEnd: function(opt_title) |
| 1570 { |
| 1571 return InjectedScriptHost.callFunction(inspectedWindow.console.profileEn
d, inspectedWindow.console, slice(arguments)); |
| 1572 }, |
| 1573 |
| 1574 /** |
| 1575 * @param {!Object} object |
| 1576 * @param {!Array.<string>|string=} opt_types |
| 1577 */ |
| 1578 monitorEvents: function(object, opt_types) |
| 1579 { |
| 1580 if (!object || !object.addEventListener || !object.removeEventListener) |
| 1581 return; |
| 1582 var types = this._normalizeEventTypes(opt_types); |
| 1583 for (var i = 0; i < types.length; ++i) { |
| 1584 object.removeEventListener(types[i], this._logEvent, false); |
| 1585 object.addEventListener(types[i], this._logEvent, false); |
| 1586 } |
| 1587 }, |
| 1588 |
| 1589 /** |
| 1590 * @param {!Object} object |
| 1591 * @param {!Array.<string>|string=} opt_types |
| 1592 */ |
| 1593 unmonitorEvents: function(object, opt_types) |
| 1594 { |
| 1595 if (!object || !object.addEventListener || !object.removeEventListener) |
| 1596 return; |
| 1597 var types = this._normalizeEventTypes(opt_types); |
| 1598 for (var i = 0; i < types.length; ++i) |
| 1599 object.removeEventListener(types[i], this._logEvent, false); |
| 1600 }, |
| 1601 |
| 1602 /** |
| 1603 * @param {*} object |
| 1604 * @return {*} |
| 1605 */ |
| 1606 inspect: function(object) |
| 1607 { |
| 1608 return injectedScript._inspect(object); |
| 1609 }, |
| 1610 |
| 1611 copy: function(object) |
| 1612 { |
| 1613 var string; |
| 1614 if (injectedScript._subtype(object) === "node") { |
| 1615 string = object.outerHTML; |
| 1616 } else if (injectedScript.isPrimitiveValue(object)) { |
| 1617 string = toString(object); |
| 1618 } else { |
| 1619 try { |
| 1620 string = JSON.stringify(object, null, " "); |
| 1621 } catch (e) { |
| 1622 string = toString(object); |
| 1623 } |
| 1624 } |
| 1625 |
| 1626 var hints = { copyToClipboard: true, __proto__: null }; |
| 1627 var remoteObject = injectedScript._wrapObject(string, "") |
| 1628 InjectedScriptHost.inspect(remoteObject, hints); |
| 1629 }, |
| 1630 |
| 1631 clear: function() |
| 1632 { |
| 1633 InjectedScriptHost.clearConsoleMessages(); |
| 1634 }, |
| 1635 |
| 1636 /** |
| 1637 * @param {!Node} node |
| 1638 * @return {!{type: string, listener: function(), useCapture: boolean, remov
e: function()}|undefined} |
| 1639 */ |
| 1640 getEventListeners: function(node) |
| 1641 { |
| 1642 var result = nullifyObjectProto(InjectedScriptHost.getEventListeners(nod
e)); |
| 1643 if (!result) |
| 1644 return result; |
| 1645 /** @this {{type: string, listener: function(), useCapture: boolean}} */ |
| 1646 var removeFunc = function() |
| 1647 { |
| 1648 node.removeEventListener(this.type, this.listener, this.useCapture); |
| 1649 } |
| 1650 for (var type in result) { |
| 1651 var listeners = result[type]; |
| 1652 for (var i = 0, listener; listener = listeners[i]; ++i) { |
| 1653 listener["type"] = type; |
| 1654 listener["remove"] = removeFunc; |
| 1655 } |
| 1656 } |
| 1657 return result; |
| 1658 }, |
| 1659 |
| 1660 debug: function(fn) |
| 1661 { |
| 1662 InjectedScriptHost.debugFunction(fn); |
| 1663 }, |
| 1664 |
| 1665 undebug: function(fn) |
| 1666 { |
| 1667 InjectedScriptHost.undebugFunction(fn); |
| 1668 }, |
| 1669 |
| 1670 monitor: function(fn) |
| 1671 { |
| 1672 InjectedScriptHost.monitorFunction(fn); |
| 1673 }, |
| 1674 |
| 1675 unmonitor: function(fn) |
| 1676 { |
| 1677 InjectedScriptHost.unmonitorFunction(fn); |
| 1678 }, |
| 1679 |
| 1680 table: function(data, opt_columns) |
| 1681 { |
| 1682 InjectedScriptHost.callFunction(inspectedWindow.console.table, inspected
Window.console, slice(arguments)); |
| 1683 }, |
| 1684 |
| 1685 /** |
| 1686 * @param {number} num |
| 1687 */ |
| 1688 _inspectedObject: function(num) |
| 1689 { |
| 1690 return InjectedScriptHost.inspectedObject(num); |
| 1691 }, |
| 1692 |
| 1693 /** |
| 1694 * @param {!Array.<string>|string=} types |
| 1695 * @return {!Array.<string>} |
| 1696 */ |
| 1697 _normalizeEventTypes: function(types) |
| 1698 { |
| 1699 if (typeof types === "undefined") |
| 1700 types = ["mouse", "key", "touch", "control", "load", "unload", "abor
t", "error", "select", "change", "submit", "reset", "focus", "blur", "resize", "
scroll", "search", "devicemotion", "deviceorientation"]; |
| 1701 else if (typeof types === "string") |
| 1702 types = [types]; |
| 1703 |
| 1704 var result = []; |
| 1705 for (var i = 0; i < types.length; ++i) { |
| 1706 if (types[i] === "mouse") |
| 1707 push(result, "mousedown", "mouseup", "click", "dblclick", "mouse
move", "mouseover", "mouseout", "mousewheel"); |
| 1708 else if (types[i] === "key") |
| 1709 push(result, "keydown", "keyup", "keypress", "textInput"); |
| 1710 else if (types[i] === "touch") |
| 1711 push(result, "touchstart", "touchmove", "touchend", "touchcancel
"); |
| 1712 else if (types[i] === "control") |
| 1713 push(result, "resize", "scroll", "zoom", "focus", "blur", "selec
t", "change", "submit", "reset"); |
| 1714 else |
| 1715 push(result, types[i]); |
| 1716 } |
| 1717 return result; |
| 1718 }, |
| 1719 |
| 1720 /** |
| 1721 * @param {!Event} event |
| 1722 */ |
| 1723 _logEvent: function(event) |
| 1724 { |
| 1725 inspectedWindow.console.log(event.type, event); |
| 1726 } |
| 1727 } |
| 1728 |
| 1729 injectedScript._commandLineAPIImpl = new CommandLineAPIImpl(); |
| 1730 return injectedScript; |
| 1731 }) |
OLD | NEW |