| 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 "use strict"; | |
| 31 | |
| 32 /** | |
| 33 * @param {!InjectedScriptHostClass} InjectedScriptHost | |
| 34 * @param {!Window|!WorkerGlobalScope} inspectedGlobalObject | |
| 35 * @param {number} injectedScriptId | |
| 36 */ | |
| 37 (function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) { | |
| 38 | |
| 39 /** | |
| 40 * Protect against Object overwritten by the user code. | |
| 41 * @suppress {duplicate} | |
| 42 */ | |
| 43 var Object = /** @type {function(new:Object, *=)} */ ({}.constructor); | |
| 44 | |
| 45 /** | |
| 46 * @param {!Array.<T>} array | |
| 47 * @param {...} var_args | |
| 48 * @template T | |
| 49 */ | |
| 50 function push(array, var_args) | |
| 51 { | |
| 52 for (var i = 1; i < arguments.length; ++i) | |
| 53 array[array.length] = arguments[i]; | |
| 54 } | |
| 55 | |
| 56 /** | |
| 57 * @param {(!Arguments.<T>|!NodeList)} array | |
| 58 * @param {number=} index | |
| 59 * @return {!Array.<T>} | |
| 60 * @template T | |
| 61 */ | |
| 62 function slice(array, index) | |
| 63 { | |
| 64 var result = []; | |
| 65 for (var i = index || 0, j = 0; i < array.length; ++i, ++j) | |
| 66 result[j] = array[i]; | |
| 67 return result; | |
| 68 } | |
| 69 | |
| 70 /** | |
| 71 * @param {!Array.<T>} array1 | |
| 72 * @param {!Array.<T>} array2 | |
| 73 * @return {!Array.<T>} | |
| 74 * @template T | |
| 75 */ | |
| 76 function concat(array1, array2) | |
| 77 { | |
| 78 var result = []; | |
| 79 for (var i = 0; i < array1.length; ++i) | |
| 80 push(result, array1[i]); | |
| 81 for (var i = 0; i < array2.length; ++i) | |
| 82 push(result, array2[i]); | |
| 83 return result; | |
| 84 } | |
| 85 | |
| 86 /** | |
| 87 * @param {*} obj | |
| 88 * @return {string} | |
| 89 * @suppress {uselessCode} | |
| 90 */ | |
| 91 function toString(obj) | |
| 92 { | |
| 93 // We don't use String(obj) because String could be overridden. | |
| 94 // Also the ("" + obj) expression may throw. | |
| 95 try { | |
| 96 return "" + obj; | |
| 97 } catch (e) { | |
| 98 var name = InjectedScriptHost.internalConstructorName(obj) || InjectedSc
riptHost.subtype(obj) || (typeof obj); | |
| 99 return "#<" + name + ">"; | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 /** | |
| 104 * @param {*} obj | |
| 105 * @return {string} | |
| 106 */ | |
| 107 function toStringDescription(obj) | |
| 108 { | |
| 109 if (typeof obj === "number" && obj === 0 && 1 / obj < 0) | |
| 110 return "-0"; // Negative zero. | |
| 111 return toString(obj); | |
| 112 } | |
| 113 | |
| 114 /** | |
| 115 * Please use this bind, not the one from Function.prototype | |
| 116 * @param {function(...)} func | |
| 117 * @param {?Object} thisObject | |
| 118 * @param {...} var_args | |
| 119 * @return {function(...)} | |
| 120 */ | |
| 121 function bind(func, thisObject, var_args) | |
| 122 { | |
| 123 var args = slice(arguments, 2); | |
| 124 | |
| 125 /** | |
| 126 * @param {...} var_args | |
| 127 */ | |
| 128 function bound(var_args) | |
| 129 { | |
| 130 return InjectedScriptHost.callFunction(func, thisObject, concat(args, sl
ice(arguments))); | |
| 131 } | |
| 132 bound.toString = function() | |
| 133 { | |
| 134 return "bound: " + toString(func); | |
| 135 }; | |
| 136 return bound; | |
| 137 } | |
| 138 | |
| 139 /** | |
| 140 * @param {T} obj | |
| 141 * @return {T} | |
| 142 * @template T | |
| 143 */ | |
| 144 function nullifyObjectProto(obj) | |
| 145 { | |
| 146 if (obj && typeof obj === "object") | |
| 147 obj.__proto__ = null; | |
| 148 return obj; | |
| 149 } | |
| 150 | |
| 151 /** | |
| 152 * @param {number|string} obj | |
| 153 * @return {boolean} | |
| 154 */ | |
| 155 function isUInt32(obj) | |
| 156 { | |
| 157 if (typeof obj === "number") | |
| 158 return obj >>> 0 === obj && (obj > 0 || 1 / obj > 0); | |
| 159 return "" + (obj >>> 0) === obj; | |
| 160 } | |
| 161 | |
| 162 /** | |
| 163 * FireBug's array detection. | |
| 164 * @param {*} obj | |
| 165 * @return {boolean} | |
| 166 */ | |
| 167 function isArrayLike(obj) | |
| 168 { | |
| 169 if (typeof obj !== "object") | |
| 170 return false; | |
| 171 try { | |
| 172 if (typeof obj.splice === "function") { | |
| 173 if (!InjectedScriptHost.suppressWarningsAndCallFunction(Object.proto
type.hasOwnProperty, obj, ["length"])) | |
| 174 return false; | |
| 175 var len = obj.length; | |
| 176 return typeof len === "number" && isUInt32(len); | |
| 177 } | |
| 178 } catch (e) { | |
| 179 } | |
| 180 return false; | |
| 181 } | |
| 182 | |
| 183 /** | |
| 184 * @param {number} a | |
| 185 * @param {number} b | |
| 186 * @return {number} | |
| 187 */ | |
| 188 function max(a, b) | |
| 189 { | |
| 190 return a > b ? a : b; | |
| 191 } | |
| 192 | |
| 193 /** | |
| 194 * FIXME: Remove once ES6 is supported natively by JS compiler. | |
| 195 * @param {*} obj | |
| 196 * @return {boolean} | |
| 197 */ | |
| 198 function isSymbol(obj) | |
| 199 { | |
| 200 var type = typeof obj; | |
| 201 return (type === "symbol"); | |
| 202 } | |
| 203 | |
| 204 /** | |
| 205 * @param {string} str | |
| 206 * @param {string} searchElement | |
| 207 * @param {number=} fromIndex | |
| 208 * @return {number} | |
| 209 */ | |
| 210 function indexOf(str, searchElement, fromIndex) | |
| 211 { | |
| 212 var len = str.length; | |
| 213 var n = fromIndex || 0; | |
| 214 var k = max(n >= 0 ? n : len + n, 0); | |
| 215 | |
| 216 while (k < len) { | |
| 217 if (str[k] === searchElement) | |
| 218 return k; | |
| 219 ++k; | |
| 220 } | |
| 221 return -1; | |
| 222 } | |
| 223 | |
| 224 /** | |
| 225 * DOM Attributes which have observable side effect on getter, in the form of | |
| 226 * {interfaceName1: {attributeName1: true, | |
| 227 * attributeName2: true, | |
| 228 * ...}, | |
| 229 * interfaceName2: {...}, | |
| 230 * ...} | |
| 231 * @type {!Object<string, !Object<string, boolean>>} | |
| 232 * @const | |
| 233 */ | |
| 234 var domAttributesWithObservableSideEffectOnGet = nullifyObjectProto({}); | |
| 235 domAttributesWithObservableSideEffectOnGet["Request"] = nullifyObjectProto({}); | |
| 236 domAttributesWithObservableSideEffectOnGet["Request"]["body"] = true; | |
| 237 domAttributesWithObservableSideEffectOnGet["Response"] = nullifyObjectProto({}); | |
| 238 domAttributesWithObservableSideEffectOnGet["Response"]["body"] = true; | |
| 239 | |
| 240 /** | |
| 241 * @param {!Object} object | |
| 242 * @param {string} attribute | |
| 243 * @return {boolean} | |
| 244 */ | |
| 245 function doesAttributeHaveObservableSideEffectOnGet(object, attribute) | |
| 246 { | |
| 247 for (var interfaceName in domAttributesWithObservableSideEffectOnGet) { | |
| 248 var isInstance = InjectedScriptHost.suppressWarningsAndCallFunction(func
tion(object, interfaceName) { | |
| 249 return /* suppressBlacklist */ typeof inspectedGlobalObject[interfac
eName] === "function" && object instanceof inspectedGlobalObject[interfaceName]; | |
| 250 }, null, [object, interfaceName]); | |
| 251 if (isInstance) { | |
| 252 return attribute in domAttributesWithObservableSideEffectOnGet[inter
faceName]; | |
| 253 } | |
| 254 } | |
| 255 return false; | |
| 256 } | |
| 257 | |
| 258 /** | |
| 259 * @constructor | |
| 260 */ | |
| 261 var InjectedScript = function() | |
| 262 { | |
| 263 } | |
| 264 | |
| 265 /** | |
| 266 * @type {!Object.<string, boolean>} | |
| 267 * @const | |
| 268 */ | |
| 269 InjectedScript.primitiveTypes = { | |
| 270 "undefined": true, | |
| 271 "boolean": true, | |
| 272 "number": true, | |
| 273 "string": true, | |
| 274 __proto__: null | |
| 275 } | |
| 276 | |
| 277 InjectedScript.prototype = { | |
| 278 /** | |
| 279 * @param {*} object | |
| 280 * @return {boolean} | |
| 281 */ | |
| 282 isPrimitiveValue: function(object) | |
| 283 { | |
| 284 // FIXME(33716): typeof document.all is always 'undefined'. | |
| 285 return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllC
ollection(object); | |
| 286 }, | |
| 287 | |
| 288 /** | |
| 289 * @param {*} object | |
| 290 * @param {string} groupName | |
| 291 * @param {boolean} canAccessInspectedGlobalObject | |
| 292 * @param {boolean} generatePreview | |
| 293 * @return {!RuntimeAgent.RemoteObject} | |
| 294 */ | |
| 295 wrapObject: function(object, groupName, canAccessInspectedGlobalObject, gene
ratePreview) | |
| 296 { | |
| 297 if (canAccessInspectedGlobalObject) | |
| 298 return this._wrapObject(object, groupName, false, generatePreview); | |
| 299 return this._fallbackWrapper(object); | |
| 300 }, | |
| 301 | |
| 302 /** | |
| 303 * @param {*} object | |
| 304 * @return {!RuntimeAgent.RemoteObject} | |
| 305 */ | |
| 306 _fallbackWrapper: function(object) | |
| 307 { | |
| 308 var result = { __proto__: null }; | |
| 309 result.type = typeof object; | |
| 310 if (this.isPrimitiveValue(object)) | |
| 311 result.value = object; | |
| 312 else | |
| 313 result.description = toString(object); | |
| 314 return /** @type {!RuntimeAgent.RemoteObject} */ (result); | |
| 315 }, | |
| 316 | |
| 317 /** | |
| 318 * @param {boolean} canAccessInspectedGlobalObject | |
| 319 * @param {!Object} table | |
| 320 * @param {!Array.<string>|string|boolean} columns | |
| 321 * @return {!RuntimeAgent.RemoteObject} | |
| 322 */ | |
| 323 wrapTable: function(canAccessInspectedGlobalObject, table, columns) | |
| 324 { | |
| 325 if (!canAccessInspectedGlobalObject) | |
| 326 return this._fallbackWrapper(table); | |
| 327 var columnNames = null; | |
| 328 if (typeof columns === "string") | |
| 329 columns = [columns]; | |
| 330 if (InjectedScriptHost.subtype(columns) === "array") { | |
| 331 columnNames = []; | |
| 332 for (var i = 0; i < columns.length; ++i) | |
| 333 columnNames[i] = toString(columns[i]); | |
| 334 } | |
| 335 return this._wrapObject(table, "console", false, true, columnNames, true
); | |
| 336 }, | |
| 337 | |
| 338 /** | |
| 339 * @param {*} object | |
| 340 * @return {*} | |
| 341 */ | |
| 342 _inspect: function(object) | |
| 343 { | |
| 344 if (arguments.length === 0) | |
| 345 return; | |
| 346 | |
| 347 var objectId = this._wrapObject(object, ""); | |
| 348 var hints = { __proto__: null }; | |
| 349 | |
| 350 InjectedScriptHost.inspect(objectId, hints); | |
| 351 return object; | |
| 352 }, | |
| 353 | |
| 354 /** | |
| 355 * This method cannot throw. | |
| 356 * @param {*} object | |
| 357 * @param {string=} objectGroupName | |
| 358 * @param {boolean=} forceValueType | |
| 359 * @param {boolean=} generatePreview | |
| 360 * @param {?Array.<string>=} columnNames | |
| 361 * @param {boolean=} isTable | |
| 362 * @param {boolean=} doNotBind | |
| 363 * @param {*=} customObjectConfig | |
| 364 * @return {!RuntimeAgent.RemoteObject} | |
| 365 * @suppress {checkTypes} | |
| 366 */ | |
| 367 _wrapObject: function(object, objectGroupName, forceValueType, generatePrevi
ew, columnNames, isTable, doNotBind, customObjectConfig) | |
| 368 { | |
| 369 try { | |
| 370 return new InjectedScript.RemoteObject(object, objectGroupName, doNo
tBind, forceValueType, generatePreview, columnNames, isTable, undefined, customO
bjectConfig); | |
| 371 } catch (e) { | |
| 372 try { | |
| 373 var description = injectedScript._describe(e); | |
| 374 } catch (ex) { | |
| 375 var description = "<failed to convert exception to string>"; | |
| 376 } | |
| 377 return new InjectedScript.RemoteObject(description); | |
| 378 } | |
| 379 }, | |
| 380 | |
| 381 /** | |
| 382 * @param {!Object|symbol} object | |
| 383 * @param {string=} objectGroupName | |
| 384 * @return {string} | |
| 385 */ | |
| 386 _bind: function(object, objectGroupName) | |
| 387 { | |
| 388 var id = InjectedScriptHost.bind(object, objectGroupName || ""); | |
| 389 return "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "
}"; | |
| 390 }, | |
| 391 | |
| 392 /** | |
| 393 * @param {string} objectId | |
| 394 * @return {!Object} | |
| 395 */ | |
| 396 _parseObjectId: function(objectId) | |
| 397 { | |
| 398 return nullifyObjectProto(/** @type {!Object} */ (InjectedScriptHost.eva
l("(" + objectId + ")"))); | |
| 399 }, | |
| 400 | |
| 401 clearLastEvaluationResult: function() | |
| 402 { | |
| 403 delete this._lastResult; | |
| 404 }, | |
| 405 | |
| 406 /** | |
| 407 * @param {string} objectId | |
| 408 * @param {boolean} ownProperties | |
| 409 * @param {boolean} accessorPropertiesOnly | |
| 410 * @param {boolean} generatePreview | |
| 411 * @return {!Array.<!RuntimeAgent.PropertyDescriptor>|boolean} | |
| 412 */ | |
| 413 getProperties: function(objectId, ownProperties, accessorPropertiesOnly, gen
eratePreview) | |
| 414 { | |
| 415 var parsedObjectId = this._parseObjectId(objectId); | |
| 416 var object = this._objectForId(parsedObjectId); | |
| 417 var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedObjec
tId.id); | |
| 418 | |
| 419 if (!this._isDefined(object) || isSymbol(object)) | |
| 420 return false; | |
| 421 object = /** @type {!Object} */ (object); | |
| 422 var descriptors = []; | |
| 423 var iter = this._propertyDescriptors(object, ownProperties, accessorProp
ertiesOnly, undefined); | |
| 424 // Go over properties, wrap object values. | |
| 425 for (var descriptor of iter) { | |
| 426 if ("get" in descriptor) | |
| 427 descriptor.get = this._wrapObject(descriptor.get, objectGroupNam
e); | |
| 428 if ("set" in descriptor) | |
| 429 descriptor.set = this._wrapObject(descriptor.set, objectGroupNam
e); | |
| 430 if ("value" in descriptor) | |
| 431 descriptor.value = this._wrapObject(descriptor.value, objectGrou
pName, false, generatePreview); | |
| 432 if (!("configurable" in descriptor)) | |
| 433 descriptor.configurable = false; | |
| 434 if (!("enumerable" in descriptor)) | |
| 435 descriptor.enumerable = false; | |
| 436 if ("symbol" in descriptor) | |
| 437 descriptor.symbol = this._wrapObject(descriptor.symbol, objectGr
oupName); | |
| 438 push(descriptors, descriptor); | |
| 439 } | |
| 440 return descriptors; | |
| 441 }, | |
| 442 | |
| 443 /** | |
| 444 * @param {string} objectId | |
| 445 * @return {!Array.<!Object>|boolean} | |
| 446 */ | |
| 447 getInternalProperties: function(objectId) | |
| 448 { | |
| 449 var parsedObjectId = this._parseObjectId(objectId); | |
| 450 var object = this._objectForId(parsedObjectId); | |
| 451 var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedObjec
tId.id); | |
| 452 if (!this._isDefined(object) || isSymbol(object)) | |
| 453 return false; | |
| 454 object = /** @type {!Object} */ (object); | |
| 455 var descriptors = []; | |
| 456 var internalProperties = InjectedScriptHost.getInternalProperties(object
); | |
| 457 if (internalProperties) { | |
| 458 for (var i = 0; i < internalProperties.length; i += 2) { | |
| 459 var descriptor = { | |
| 460 name: internalProperties[i], | |
| 461 value: this._wrapObject(internalProperties[i + 1], objectGro
upName), | |
| 462 __proto__: null | |
| 463 }; | |
| 464 push(descriptors, descriptor); | |
| 465 } | |
| 466 } | |
| 467 return descriptors; | |
| 468 }, | |
| 469 | |
| 470 /** | |
| 471 * @param {string} functionId | |
| 472 * @return {!DebuggerAgent.FunctionDetails|string} | |
| 473 */ | |
| 474 getFunctionDetails: function(functionId) | |
| 475 { | |
| 476 var parsedFunctionId = this._parseObjectId(functionId); | |
| 477 var func = this._objectForId(parsedFunctionId); | |
| 478 if (typeof func !== "function") | |
| 479 return "Cannot resolve function by id."; | |
| 480 var details = nullifyObjectProto(/** @type {!DebuggerAgent.FunctionDetai
ls} */ (InjectedScriptHost.functionDetails(func))); | |
| 481 if ("rawScopes" in details) { | |
| 482 var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedF
unctionId.id); | |
| 483 var rawScopes = details["rawScopes"]; | |
| 484 delete details["rawScopes"]; | |
| 485 var scopes = []; | |
| 486 for (var i = 0; i < rawScopes.length; ++i) | |
| 487 scopes[i] = InjectedScript.CallFrameProxy._createScopeJson(rawSc
opes[i].type, rawScopes[i].name, rawScopes[i].object, objectGroupName); | |
| 488 details.scopeChain = scopes; | |
| 489 } | |
| 490 return details; | |
| 491 }, | |
| 492 | |
| 493 /** | |
| 494 * @param {string} objectId | |
| 495 * @return {!DebuggerAgent.GeneratorObjectDetails|string} | |
| 496 */ | |
| 497 getGeneratorObjectDetails: function(objectId) | |
| 498 { | |
| 499 var parsedObjectId = this._parseObjectId(objectId); | |
| 500 var object = this._objectForId(parsedObjectId); | |
| 501 if (!object || typeof object !== "object") | |
| 502 return "Could not find object with given id"; | |
| 503 var details = nullifyObjectProto(/** @type {?DebuggerAgent.GeneratorObje
ctDetails} */ (InjectedScriptHost.generatorObjectDetails(object))); | |
| 504 if (!details) | |
| 505 return "Object is not a generator"; | |
| 506 var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedObjec
tId.id); | |
| 507 details["function"] = this._wrapObject(details["function"], objectGroupN
ame); | |
| 508 return details; | |
| 509 }, | |
| 510 | |
| 511 /** | |
| 512 * @param {string} objectId | |
| 513 * @return {!Array.<!Object>|string} | |
| 514 */ | |
| 515 getCollectionEntries: function(objectId) | |
| 516 { | |
| 517 var parsedObjectId = this._parseObjectId(objectId); | |
| 518 var object = this._objectForId(parsedObjectId); | |
| 519 if (!object || typeof object !== "object") | |
| 520 return "Could not find object with given id"; | |
| 521 var entries = InjectedScriptHost.collectionEntries(object); | |
| 522 if (!entries) | |
| 523 return "Object with given id is not a collection"; | |
| 524 var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedObjec
tId.id); | |
| 525 for (var i = 0; i < entries.length; ++i) { | |
| 526 var entry = nullifyObjectProto(entries[i]); | |
| 527 if ("key" in entry) | |
| 528 entry.key = this._wrapObject(entry.key, objectGroupName); | |
| 529 entry.value = this._wrapObject(entry.value, objectGroupName); | |
| 530 entries[i] = entry; | |
| 531 } | |
| 532 return entries; | |
| 533 }, | |
| 534 | |
| 535 /** | |
| 536 * @param {!Object} object | |
| 537 * @param {boolean=} ownProperties | |
| 538 * @param {boolean=} accessorPropertiesOnly | |
| 539 * @param {?Array.<string>=} propertyNamesOnly | |
| 540 */ | |
| 541 _propertyDescriptors: function*(object, ownProperties, accessorPropertiesOnl
y, propertyNamesOnly) | |
| 542 { | |
| 543 var propertyProcessed = { __proto__: null }; | |
| 544 | |
| 545 /** | |
| 546 * @param {?Object} o | |
| 547 * @param {!Iterable.<string|symbol>|!Array.<string|symbol>} properties | |
| 548 */ | |
| 549 function* process(o, properties) | |
| 550 { | |
| 551 for (var property of properties) { | |
| 552 if (propertyProcessed[property]) | |
| 553 continue; | |
| 554 | |
| 555 var name = property; | |
| 556 if (isSymbol(property)) | |
| 557 name = /** @type {string} */ (injectedScript._describe(prope
rty)); | |
| 558 | |
| 559 try { | |
| 560 propertyProcessed[property] = true; | |
| 561 var descriptor = nullifyObjectProto(InjectedScriptHost.suppr
essWarningsAndCallFunction(Object.getOwnPropertyDescriptor, Object, [o, property
])); | |
| 562 if (descriptor) { | |
| 563 if (accessorPropertiesOnly && !("get" in descriptor || "
set" in descriptor)) | |
| 564 continue; | |
| 565 if ("get" in descriptor && "set" in descriptor && name !
= "__proto__" && InjectedScriptHost.isDOMWrapper(object) && !doesAttributeHaveOb
servableSideEffectOnGet(object, name)) { | |
| 566 descriptor.value = InjectedScriptHost.suppressWarnin
gsAndCallFunction(function(attribute) { return this[attribute]; }, object, [name
]); | |
| 567 descriptor.isOwn = true; | |
| 568 delete descriptor.get; | |
| 569 delete descriptor.set; | |
| 570 } | |
| 571 } else { | |
| 572 // Not all bindings provide proper descriptors. Fall bac
k to the writable, configurable property. | |
| 573 if (accessorPropertiesOnly) | |
| 574 continue; | |
| 575 try { | |
| 576 descriptor = { name: name, value: o[property], writa
ble: false, configurable: false, enumerable: false, __proto__: null }; | |
| 577 if (o === object) | |
| 578 descriptor.isOwn = true; | |
| 579 yield descriptor; | |
| 580 } catch (e) { | |
| 581 // Silent catch. | |
| 582 } | |
| 583 continue; | |
| 584 } | |
| 585 } catch (e) { | |
| 586 if (accessorPropertiesOnly) | |
| 587 continue; | |
| 588 var descriptor = { __proto__: null }; | |
| 589 descriptor.value = e; | |
| 590 descriptor.wasThrown = true; | |
| 591 } | |
| 592 | |
| 593 descriptor.name = name; | |
| 594 if (o === object) | |
| 595 descriptor.isOwn = true; | |
| 596 if (isSymbol(property)) | |
| 597 descriptor.symbol = property; | |
| 598 yield descriptor; | |
| 599 } | |
| 600 } | |
| 601 | |
| 602 /** | |
| 603 * @param {number} length | |
| 604 */ | |
| 605 function* arrayIndexNames(length) | |
| 606 { | |
| 607 for (var i = 0; i < length; ++i) | |
| 608 yield "" + i; | |
| 609 } | |
| 610 | |
| 611 if (propertyNamesOnly) { | |
| 612 for (var i = 0; i < propertyNamesOnly.length; ++i) { | |
| 613 var name = propertyNamesOnly[i]; | |
| 614 for (var o = object; this._isDefined(o); o = o.__proto__) { | |
| 615 if (InjectedScriptHost.suppressWarningsAndCallFunction(Objec
t.prototype.hasOwnProperty, o, [name])) { | |
| 616 for (var descriptor of process(o, [name])) | |
| 617 yield descriptor; | |
| 618 break; | |
| 619 } | |
| 620 if (ownProperties) | |
| 621 break; | |
| 622 } | |
| 623 } | |
| 624 return; | |
| 625 } | |
| 626 | |
| 627 var skipGetOwnPropertyNames; | |
| 628 try { | |
| 629 skipGetOwnPropertyNames = InjectedScriptHost.isTypedArray(object) &&
object.length > 500000; | |
| 630 } catch (e) { | |
| 631 } | |
| 632 | |
| 633 for (var o = object; this._isDefined(o); o = o.__proto__) { | |
| 634 if (skipGetOwnPropertyNames && o === object) { | |
| 635 // Avoid OOM crashes from getting all own property names of a la
rge TypedArray. | |
| 636 for (var descriptor of process(o, arrayIndexNames(o.length))) | |
| 637 yield descriptor; | |
| 638 } else { | |
| 639 // First call Object.keys() to enforce ordering of the property
descriptors. | |
| 640 for (var descriptor of process(o, Object.keys(/** @type {!Object
} */ (o)))) | |
| 641 yield descriptor; | |
| 642 for (var descriptor of process(o, Object.getOwnPropertyNames(/**
@type {!Object} */ (o)))) | |
| 643 yield descriptor; | |
| 644 } | |
| 645 if (Object.getOwnPropertySymbols) { | |
| 646 for (var descriptor of process(o, Object.getOwnPropertySymbols(/
** @type {!Object} */ (o)))) | |
| 647 yield descriptor; | |
| 648 } | |
| 649 if (ownProperties) { | |
| 650 if (object.__proto__ && !accessorPropertiesOnly) | |
| 651 yield { name: "__proto__", value: object.__proto__, writable
: true, configurable: true, enumerable: false, isOwn: true, __proto__: null }; | |
| 652 break; | |
| 653 } | |
| 654 } | |
| 655 }, | |
| 656 | |
| 657 /** | |
| 658 * @param {string} expression | |
| 659 * @param {string} objectGroup | |
| 660 * @param {boolean} injectCommandLineAPI | |
| 661 * @param {boolean} returnByValue | |
| 662 * @param {boolean} generatePreview | |
| 663 * @return {*} | |
| 664 */ | |
| 665 evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByVa
lue, generatePreview) | |
| 666 { | |
| 667 return this._evaluateAndWrap(null, expression, objectGroup, injectComman
dLineAPI, returnByValue, generatePreview); | |
| 668 }, | |
| 669 | |
| 670 /** | |
| 671 * @param {string} objectId | |
| 672 * @param {string} expression | |
| 673 * @param {string} args | |
| 674 * @param {boolean} returnByValue | |
| 675 * @return {!Object|string} | |
| 676 */ | |
| 677 callFunctionOn: function(objectId, expression, args, returnByValue) | |
| 678 { | |
| 679 var parsedObjectId = this._parseObjectId(objectId); | |
| 680 var object = this._objectForId(parsedObjectId); | |
| 681 if (!this._isDefined(object)) | |
| 682 return "Could not find object with given id"; | |
| 683 | |
| 684 if (args) { | |
| 685 var resolvedArgs = []; | |
| 686 var callArgs = /** @type {!Array.<!RuntimeAgent.CallArgument>} */ (I
njectedScriptHost.eval(args)); | |
| 687 for (var i = 0; i < callArgs.length; ++i) { | |
| 688 try { | |
| 689 resolvedArgs[i] = this._resolveCallArgument(callArgs[i]); | |
| 690 } catch (e) { | |
| 691 return toString(e); | |
| 692 } | |
| 693 } | |
| 694 } | |
| 695 | |
| 696 var objectGroup = InjectedScriptHost.idToObjectGroupName(parsedObjectId.
id); | |
| 697 | |
| 698 /** | |
| 699 * @suppressReceiverCheck | |
| 700 * @param {*} object | |
| 701 * @param {boolean=} forceValueType | |
| 702 * @param {boolean=} generatePreview | |
| 703 * @param {?Array.<string>=} columnNames | |
| 704 * @param {boolean=} isTable | |
| 705 * @param {*=} customObjectConfig | |
| 706 * @return {!RuntimeAgent.RemoteObject} | |
| 707 * @this {InjectedScript} | |
| 708 */ | |
| 709 function wrap(object, forceValueType, generatePreview, columnNames, isTa
ble, customObjectConfig) | |
| 710 { | |
| 711 return this._wrapObject(object, objectGroup, forceValueType, generat
ePreview, columnNames, isTable, false, customObjectConfig); | |
| 712 } | |
| 713 | |
| 714 try { | |
| 715 | |
| 716 var remoteObjectAPI = { bindRemoteObject: bind(wrap, this), __proto_
_: null}; | |
| 717 InjectedScriptHost.setNonEnumProperty(inspectedGlobalObject, "__remo
teObjectAPI", remoteObjectAPI); | |
| 718 | |
| 719 var func = InjectedScriptHost.eval("with (typeof __remoteObjectAPI !
== 'undefined' ? __remoteObjectAPI : { __proto__: null }) {(" + expression + ")}
"); | |
| 720 if (typeof func !== "function") | |
| 721 return "Given expression does not evaluate to a function"; | |
| 722 | |
| 723 return { wasThrown: false, | |
| 724 result: this._wrapObject(InjectedScriptHost.callFunction(fu
nc, object, resolvedArgs), objectGroup, returnByValue), | |
| 725 __proto__: null }; | |
| 726 } catch (e) { | |
| 727 return this._createThrownValue(e, objectGroup, false); | |
| 728 } finally { | |
| 729 try { | |
| 730 delete inspectedGlobalObject["__remoteObjectAPI"]; | |
| 731 } catch(e) { | |
| 732 } | |
| 733 } | |
| 734 }, | |
| 735 | |
| 736 /** | |
| 737 * @param {string|undefined} objectGroupName | |
| 738 * @param {*} jsonMLObject | |
| 739 * @throws {string} error message | |
| 740 */ | |
| 741 _substituteObjectTagsInCustomPreview: function(objectGroupName, jsonMLObject
) | |
| 742 { | |
| 743 var maxCustomPreviewRecursionDepth = 20; | |
| 744 this._customPreviewRecursionDepth = (this._customPreviewRecursionDepth |
| 0) + 1 | |
| 745 try { | |
| 746 if (this._customPreviewRecursionDepth >= maxCustomPreviewRecursionDe
pth) | |
| 747 throw new Error("Too deep hierarchy of inlined custom previews")
; | |
| 748 | |
| 749 if (!isArrayLike(jsonMLObject)) | |
| 750 return; | |
| 751 | |
| 752 if (jsonMLObject[0] === "object") { | |
| 753 var attributes = jsonMLObject[1]; | |
| 754 var originObject = attributes["object"]; | |
| 755 var config = attributes["config"]; | |
| 756 if (typeof originObject === "undefined") | |
| 757 throw new Error("Illegal format: obligatory attribute \"obje
ct\" isn't specified"); | |
| 758 | |
| 759 jsonMLObject[1] = this._wrapObject(originObject, objectGroupName
, false, false, null, false, false, config); | |
| 760 return; | |
| 761 } | |
| 762 | |
| 763 for (var i = 0; i < jsonMLObject.length; ++i) | |
| 764 this._substituteObjectTagsInCustomPreview(objectGroupName, jsonM
LObject[i]); | |
| 765 } finally { | |
| 766 this._customPreviewRecursionDepth--; | |
| 767 } | |
| 768 }, | |
| 769 | |
| 770 /** | |
| 771 * Resolves a value from CallArgument description. | |
| 772 * @param {!RuntimeAgent.CallArgument} callArgumentJson | |
| 773 * @return {*} resolved value | |
| 774 * @throws {string} error message | |
| 775 */ | |
| 776 _resolveCallArgument: function(callArgumentJson) | |
| 777 { | |
| 778 callArgumentJson = nullifyObjectProto(callArgumentJson); | |
| 779 var objectId = callArgumentJson.objectId; | |
| 780 if (objectId) { | |
| 781 var parsedArgId = this._parseObjectId(objectId); | |
| 782 if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScri
ptId) | |
| 783 throw "Arguments should belong to the same JavaScript world as t
he target object."; | |
| 784 | |
| 785 var resolvedArg = this._objectForId(parsedArgId); | |
| 786 if (!this._isDefined(resolvedArg)) | |
| 787 throw "Could not find object with given id"; | |
| 788 | |
| 789 return resolvedArg; | |
| 790 } else if ("value" in callArgumentJson) { | |
| 791 var value = callArgumentJson.value; | |
| 792 if (callArgumentJson.type === "number" && typeof value !== "number") | |
| 793 value = Number(value); | |
| 794 return value; | |
| 795 } | |
| 796 return undefined; | |
| 797 }, | |
| 798 | |
| 799 /** | |
| 800 * @param {?JavaScriptCallFrame} callFrame | |
| 801 * @param {string} expression | |
| 802 * @param {string} objectGroup | |
| 803 * @param {boolean} injectCommandLineAPI | |
| 804 * @param {boolean} returnByValue | |
| 805 * @param {boolean} generatePreview | |
| 806 * @param {!Array.<!Object>=} scopeChain | |
| 807 * @return {!Object} | |
| 808 */ | |
| 809 _evaluateAndWrap: function(callFrame, expression, objectGroup, injectCommand
LineAPI, returnByValue, generatePreview, scopeChain) | |
| 810 { | |
| 811 var wrappedResult = this._evaluateOn(callFrame, objectGroup, expression,
injectCommandLineAPI, scopeChain); | |
| 812 if (!wrappedResult.exceptionDetails) { | |
| 813 return { wasThrown: false, | |
| 814 result: this._wrapObject(wrappedResult.result, objectGroup,
returnByValue, generatePreview), | |
| 815 __proto__: null }; | |
| 816 } | |
| 817 return this._createThrownValue(wrappedResult.result, objectGroup, genera
tePreview, wrappedResult.exceptionDetails); | |
| 818 }, | |
| 819 | |
| 820 /** | |
| 821 * @param {*} value | |
| 822 * @param {string|undefined} objectGroup | |
| 823 * @param {boolean} generatePreview | |
| 824 * @param {!DebuggerAgent.ExceptionDetails=} exceptionDetails | |
| 825 * @return {!Object} | |
| 826 */ | |
| 827 _createThrownValue: function(value, objectGroup, generatePreview, exceptionD
etails) | |
| 828 { | |
| 829 var remoteObject = this._wrapObject(value, objectGroup, false, generateP
review && InjectedScriptHost.subtype(value) !== "error"); | |
| 830 if (!remoteObject.description){ | |
| 831 try { | |
| 832 remoteObject.description = toStringDescription(value); | |
| 833 } catch (e) {} | |
| 834 } | |
| 835 return { wasThrown: true, result: remoteObject, exceptionDetails: except
ionDetails, __proto__: null }; | |
| 836 }, | |
| 837 | |
| 838 /** | |
| 839 * @param {?JavaScriptCallFrame} callFrame | |
| 840 * @param {string} objectGroup | |
| 841 * @param {string} expression | |
| 842 * @param {boolean} injectCommandLineAPI | |
| 843 * @param {!Array.<!Object>=} scopeChain | |
| 844 * @return {*} | |
| 845 */ | |
| 846 _evaluateOn: function(callFrame, objectGroup, expression, injectCommandLineA
PI, scopeChain) | |
| 847 { | |
| 848 // Only install command line api object for the time of evaluation. | |
| 849 // Surround the expression in with statements to inject our command line
API so that | |
| 850 // the window object properties still take more precedent than our API f
unctions. | |
| 851 | |
| 852 var scopeExtensionForEval = (callFrame && injectCommandLineAPI) ? new Co
mmandLineAPI(this._commandLineAPIImpl, callFrame) : undefined; | |
| 853 var injectScopeChain = scopeChain && scopeChain.length && !("__scopeChai
nForEval" in inspectedGlobalObject); | |
| 854 | |
| 855 try { | |
| 856 var prefix = ""; | |
| 857 var suffix = ""; | |
| 858 if (injectScopeChain) { | |
| 859 InjectedScriptHost.setNonEnumProperty(inspectedGlobalObject, "__
scopeChainForEval", scopeChain); | |
| 860 for (var i = 0; i < scopeChain.length; ++i) { | |
| 861 prefix = "with (typeof __scopeChainForEval !== 'undefined' ?
__scopeChainForEval[" + i + "] : { __proto__: null }) {" + (suffix ? " " : "")
+ prefix; | |
| 862 if (suffix) | |
| 863 suffix += " }"; | |
| 864 else | |
| 865 suffix = "}"; | |
| 866 } | |
| 867 } | |
| 868 | |
| 869 if (prefix) | |
| 870 expression = prefix + "\n" + expression + "\n" + suffix; | |
| 871 var wrappedResult = callFrame ? callFrame.evaluateWithExceptionDetai
ls(expression, scopeExtensionForEval) : InjectedScriptHost.evaluateWithException
Details(expression, injectCommandLineAPI ? new CommandLineAPI(this._commandLineA
PIImpl, callFrame) : undefined); | |
| 872 if (objectGroup === "console" && !wrappedResult.exceptionDetails) | |
| 873 this._lastResult = wrappedResult.result; | |
| 874 return wrappedResult; | |
| 875 } finally { | |
| 876 if (injectScopeChain) { | |
| 877 try { | |
| 878 delete inspectedGlobalObject["__scopeChainForEval"]; | |
| 879 } catch(e) { | |
| 880 } | |
| 881 } | |
| 882 } | |
| 883 }, | |
| 884 | |
| 885 /** | |
| 886 * @param {?Object} callFrame | |
| 887 * @param {number} asyncOrdinal | |
| 888 * @return {!Array.<!InjectedScript.CallFrameProxy>|boolean} | |
| 889 */ | |
| 890 wrapCallFrames: function(callFrame, asyncOrdinal) | |
| 891 { | |
| 892 if (!callFrame) | |
| 893 return false; | |
| 894 | |
| 895 var result = []; | |
| 896 var depth = 0; | |
| 897 do { | |
| 898 result[depth] = new InjectedScript.CallFrameProxy(depth, callFrame,
asyncOrdinal); | |
| 899 callFrame = callFrame.caller; | |
| 900 ++depth; | |
| 901 } while (callFrame); | |
| 902 return result; | |
| 903 }, | |
| 904 | |
| 905 /** | |
| 906 * @param {!JavaScriptCallFrame} topCallFrame | |
| 907 * @param {boolean} isAsyncStack | |
| 908 * @param {string} callFrameId | |
| 909 * @param {string} expression | |
| 910 * @param {string} objectGroup | |
| 911 * @param {boolean} injectCommandLineAPI | |
| 912 * @param {boolean} returnByValue | |
| 913 * @param {boolean} generatePreview | |
| 914 * @return {*} | |
| 915 */ | |
| 916 evaluateOnCallFrame: function(topCallFrame, isAsyncStack, callFrameId, expre
ssion, objectGroup, injectCommandLineAPI, returnByValue, generatePreview) | |
| 917 { | |
| 918 var callFrame = this._callFrameForId(topCallFrame, callFrameId); | |
| 919 if (!callFrame) | |
| 920 return "Could not find call frame with given id"; | |
| 921 if (isAsyncStack) | |
| 922 return this._evaluateAndWrap(null, expression, objectGroup, injectCo
mmandLineAPI, returnByValue, generatePreview, callFrame.scopeChain); | |
| 923 return this._evaluateAndWrap(callFrame, expression, objectGroup, injectC
ommandLineAPI, returnByValue, generatePreview); | |
| 924 }, | |
| 925 | |
| 926 /** | |
| 927 * @param {!JavaScriptCallFrame} topCallFrame | |
| 928 * @param {string} callFrameId | |
| 929 * @return {*} | |
| 930 */ | |
| 931 restartFrame: function(topCallFrame, callFrameId) | |
| 932 { | |
| 933 var callFrame = this._callFrameForId(topCallFrame, callFrameId); | |
| 934 if (!callFrame) | |
| 935 return "Could not find call frame with given id"; | |
| 936 return callFrame.restart(); | |
| 937 }, | |
| 938 | |
| 939 /** | |
| 940 * @param {!JavaScriptCallFrame} topCallFrame | |
| 941 * @param {string} callFrameId | |
| 942 * @return {*} a stepIn position array ready for protocol JSON or a string e
rror | |
| 943 */ | |
| 944 getStepInPositions: function(topCallFrame, callFrameId) | |
| 945 { | |
| 946 var callFrame = this._callFrameForId(topCallFrame, callFrameId); | |
| 947 if (!callFrame) | |
| 948 return "Could not find call frame with given id"; | |
| 949 var stepInPositionsUnpacked = JSON.parse(callFrame.stepInPositions); | |
| 950 if (typeof stepInPositionsUnpacked !== "object") | |
| 951 return "Step in positions not available"; | |
| 952 return stepInPositionsUnpacked; | |
| 953 }, | |
| 954 | |
| 955 /** | |
| 956 * Either callFrameId or functionObjectId must be specified. | |
| 957 * @param {!JavaScriptCallFrame} topCallFrame | |
| 958 * @param {string|boolean} callFrameId or false | |
| 959 * @param {string|boolean} functionObjectId or false | |
| 960 * @param {number} scopeNumber | |
| 961 * @param {string} variableName | |
| 962 * @param {string} newValueJsonString RuntimeAgent.CallArgument structure se
rialized as string | |
| 963 * @return {string|undefined} undefined if success or an error message | |
| 964 */ | |
| 965 setVariableValue: function(topCallFrame, callFrameId, functionObjectId, scop
eNumber, variableName, newValueJsonString) | |
| 966 { | |
| 967 try { | |
| 968 var newValueJson = /** @type {!RuntimeAgent.CallArgument} */ (Inject
edScriptHost.eval("(" + newValueJsonString + ")")); | |
| 969 var resolvedValue = this._resolveCallArgument(newValueJson); | |
| 970 if (typeof callFrameId === "string") { | |
| 971 var callFrame = this._callFrameForId(topCallFrame, callFrameId); | |
| 972 if (!callFrame) | |
| 973 return "Could not find call frame with given id"; | |
| 974 callFrame.setVariableValue(scopeNumber, variableName, resolvedVa
lue) | |
| 975 } else { | |
| 976 var parsedFunctionId = this._parseObjectId(/** @type {string} */
(functionObjectId)); | |
| 977 var func = this._objectForId(parsedFunctionId); | |
| 978 if (typeof func !== "function") | |
| 979 return "Could not resolve function by id"; | |
| 980 InjectedScriptHost.setFunctionVariableValue(func, scopeNumber, v
ariableName, resolvedValue); | |
| 981 } | |
| 982 } catch (e) { | |
| 983 return toString(e); | |
| 984 } | |
| 985 return undefined; | |
| 986 }, | |
| 987 | |
| 988 /** | |
| 989 * @param {!JavaScriptCallFrame} topCallFrame | |
| 990 * @param {string} callFrameId | |
| 991 * @return {?JavaScriptCallFrame} | |
| 992 */ | |
| 993 _callFrameForId: function(topCallFrame, callFrameId) | |
| 994 { | |
| 995 var parsedCallFrameId = nullifyObjectProto(/** @type {!Object} */ (Injec
tedScriptHost.eval("(" + callFrameId + ")"))); | |
| 996 var ordinal = parsedCallFrameId["ordinal"]; | |
| 997 var callFrame = topCallFrame; | |
| 998 while (--ordinal >= 0 && callFrame) | |
| 999 callFrame = callFrame.caller; | |
| 1000 return callFrame; | |
| 1001 }, | |
| 1002 | |
| 1003 /** | |
| 1004 * @param {!Object} objectId | |
| 1005 * @return {!Object|symbol|undefined} | |
| 1006 */ | |
| 1007 _objectForId: function(objectId) | |
| 1008 { | |
| 1009 return objectId.injectedScriptId === injectedScriptId ? /** @type{!Objec
t|symbol|undefined} */ (InjectedScriptHost.objectForId(objectId.id)) : void 0; | |
| 1010 }, | |
| 1011 | |
| 1012 /** | |
| 1013 * @param {*} object | |
| 1014 * @return {boolean} | |
| 1015 */ | |
| 1016 _isDefined: function(object) | |
| 1017 { | |
| 1018 return !!object || this._isHTMLAllCollection(object); | |
| 1019 }, | |
| 1020 | |
| 1021 /** | |
| 1022 * @param {*} object | |
| 1023 * @return {boolean} | |
| 1024 */ | |
| 1025 _isHTMLAllCollection: function(object) | |
| 1026 { | |
| 1027 // document.all is reported as undefined, but we still want to process i
t. | |
| 1028 return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCo
llection(object); | |
| 1029 }, | |
| 1030 | |
| 1031 /** | |
| 1032 * @param {*} obj | |
| 1033 * @return {?string} | |
| 1034 */ | |
| 1035 _subtype: function(obj) | |
| 1036 { | |
| 1037 if (obj === null) | |
| 1038 return "null"; | |
| 1039 | |
| 1040 if (this.isPrimitiveValue(obj)) | |
| 1041 return null; | |
| 1042 | |
| 1043 var subtype = InjectedScriptHost.subtype(obj); | |
| 1044 if (subtype) | |
| 1045 return subtype; | |
| 1046 | |
| 1047 if (isArrayLike(obj)) | |
| 1048 return "array"; | |
| 1049 | |
| 1050 // If owning frame has navigated to somewhere else window properties wil
l be undefined. | |
| 1051 return null; | |
| 1052 }, | |
| 1053 | |
| 1054 /** | |
| 1055 * @param {*} obj | |
| 1056 * @return {?string} | |
| 1057 */ | |
| 1058 _describe: function(obj) | |
| 1059 { | |
| 1060 if (this.isPrimitiveValue(obj)) | |
| 1061 return null; | |
| 1062 | |
| 1063 var subtype = this._subtype(obj); | |
| 1064 | |
| 1065 if (subtype === "regexp") | |
| 1066 return toString(obj); | |
| 1067 | |
| 1068 if (subtype === "date") | |
| 1069 return toString(obj); | |
| 1070 | |
| 1071 if (subtype === "node") { | |
| 1072 var description = obj.nodeName.toLowerCase(); | |
| 1073 switch (obj.nodeType) { | |
| 1074 case 1 /* Node.ELEMENT_NODE */: | |
| 1075 description += obj.id ? "#" + obj.id : ""; | |
| 1076 var className = obj.className; | |
| 1077 description += (className && typeof className === "string") ? ".
" + className.trim().replace(/\s+/g, ".") : ""; | |
| 1078 break; | |
| 1079 case 10 /*Node.DOCUMENT_TYPE_NODE */: | |
| 1080 description = "<!DOCTYPE " + description + ">"; | |
| 1081 break; | |
| 1082 } | |
| 1083 return description; | |
| 1084 } | |
| 1085 | |
| 1086 var className = InjectedScriptHost.internalConstructorName(obj); | |
| 1087 if (subtype === "array") { | |
| 1088 if (typeof obj.length === "number") | |
| 1089 className += "[" + obj.length + "]"; | |
| 1090 return className; | |
| 1091 } | |
| 1092 | |
| 1093 // NodeList in JSC is a function, check for array prior to this. | |
| 1094 if (typeof obj === "function") | |
| 1095 return toString(obj); | |
| 1096 | |
| 1097 if (isSymbol(obj)) { | |
| 1098 try { | |
| 1099 return /** @type {string} */ (InjectedScriptHost.callFunction(Sy
mbol.prototype.toString, obj)) || "Symbol"; | |
| 1100 } catch (e) { | |
| 1101 return "Symbol"; | |
| 1102 } | |
| 1103 } | |
| 1104 | |
| 1105 if (InjectedScriptHost.subtype(obj) === "error") { | |
| 1106 try { | |
| 1107 var stack = obj.stack; | |
| 1108 var message = obj.message && obj.message.length ? ": " + obj.mes
sage : ""; | |
| 1109 var firstCallFrame = /^\s+at\s/m.exec(stack); | |
| 1110 var stackMessageEnd = firstCallFrame ? firstCallFrame.index : -1
; | |
| 1111 if (stackMessageEnd !== -1) { | |
| 1112 var stackTrace = stack.substr(stackMessageEnd); | |
| 1113 return className + message + "\n" + stackTrace; | |
| 1114 } | |
| 1115 return className + message; | |
| 1116 } catch(e) { | |
| 1117 } | |
| 1118 } | |
| 1119 | |
| 1120 return className; | |
| 1121 }, | |
| 1122 | |
| 1123 /** | |
| 1124 * @param {boolean} enabled | |
| 1125 */ | |
| 1126 setCustomObjectFormatterEnabled: function(enabled) | |
| 1127 { | |
| 1128 this._customObjectFormatterEnabled = enabled; | |
| 1129 } | |
| 1130 } | |
| 1131 | |
| 1132 /** | |
| 1133 * @type {!InjectedScript} | |
| 1134 * @const | |
| 1135 */ | |
| 1136 var injectedScript = new InjectedScript(); | |
| 1137 | |
| 1138 /** | |
| 1139 * @constructor | |
| 1140 * @param {*} object | |
| 1141 * @param {string=} objectGroupName | |
| 1142 * @param {boolean=} doNotBind | |
| 1143 * @param {boolean=} forceValueType | |
| 1144 * @param {boolean=} generatePreview | |
| 1145 * @param {?Array.<string>=} columnNames | |
| 1146 * @param {boolean=} isTable | |
| 1147 * @param {boolean=} skipEntriesPreview | |
| 1148 * @param {*=} customObjectConfig | |
| 1149 */ | |
| 1150 InjectedScript.RemoteObject = function(object, objectGroupName, doNotBind, force
ValueType, generatePreview, columnNames, isTable, skipEntriesPreview, customObje
ctConfig) | |
| 1151 { | |
| 1152 this.type = typeof object; | |
| 1153 if (this.type === "undefined" && injectedScript._isHTMLAllCollection(object)
) | |
| 1154 this.type = "object"; | |
| 1155 | |
| 1156 if (injectedScript.isPrimitiveValue(object) || object === null || forceValue
Type) { | |
| 1157 // We don't send undefined values over JSON. | |
| 1158 if (this.type !== "undefined") | |
| 1159 this.value = object; | |
| 1160 | |
| 1161 // Null object is object with 'null' subtype. | |
| 1162 if (object === null) | |
| 1163 this.subtype = "null"; | |
| 1164 | |
| 1165 // Provide user-friendly number values. | |
| 1166 if (this.type === "number") { | |
| 1167 this.description = toStringDescription(object); | |
| 1168 // Override "value" property for values that can not be JSON-stringi
fied. | |
| 1169 switch (this.description) { | |
| 1170 case "NaN": | |
| 1171 case "Infinity": | |
| 1172 case "-Infinity": | |
| 1173 case "-0": | |
| 1174 this.value = this.description; | |
| 1175 break; | |
| 1176 } | |
| 1177 } | |
| 1178 | |
| 1179 return; | |
| 1180 } | |
| 1181 | |
| 1182 object = /** @type {!Object} */ (object); | |
| 1183 | |
| 1184 if (!doNotBind) | |
| 1185 this.objectId = injectedScript._bind(object, objectGroupName); | |
| 1186 var subtype = injectedScript._subtype(object); | |
| 1187 if (subtype) | |
| 1188 this.subtype = subtype; | |
| 1189 var className = InjectedScriptHost.internalConstructorName(object); | |
| 1190 if (className) | |
| 1191 this.className = className; | |
| 1192 this.description = injectedScript._describe(object); | |
| 1193 | |
| 1194 if (generatePreview && this.type === "object" && this.subtype !== "node") | |
| 1195 this.preview = this._generatePreview(object, undefined, columnNames, isT
able, skipEntriesPreview); | |
| 1196 | |
| 1197 if (injectedScript._customObjectFormatterEnabled) { | |
| 1198 var customPreview = this._customPreview(object, objectGroupName, customO
bjectConfig); | |
| 1199 if (customPreview) | |
| 1200 this.customPreview = customPreview; | |
| 1201 } | |
| 1202 } | |
| 1203 | |
| 1204 InjectedScript.RemoteObject.prototype = { | |
| 1205 | |
| 1206 /** | |
| 1207 * @param {*} object | |
| 1208 * @param {string=} objectGroupName | |
| 1209 * @param {*=} customObjectConfig | |
| 1210 * @return {?RuntimeAgent.CustomPreview} | |
| 1211 */ | |
| 1212 _customPreview: function(object, objectGroupName, customObjectConfig) | |
| 1213 { | |
| 1214 /** | |
| 1215 * @param {!Error} error | |
| 1216 */ | |
| 1217 function logError(error) | |
| 1218 { | |
| 1219 Promise.resolve().then(inspectedGlobalObject.console.error.bind(insp
ectedGlobalObject.console, "Custom Formatter Failed: " + error.message)); | |
| 1220 } | |
| 1221 | |
| 1222 try { | |
| 1223 var formatters = inspectedGlobalObject["devtoolsFormatters"]; | |
| 1224 if (!formatters || !isArrayLike(formatters)) | |
| 1225 return null; | |
| 1226 | |
| 1227 for (var i = 0; i < formatters.length; ++i) { | |
| 1228 try { | |
| 1229 var formatted = formatters[i].header(object, customObjectCon
fig); | |
| 1230 if (!formatted) | |
| 1231 continue; | |
| 1232 | |
| 1233 var hasBody = formatters[i].hasBody(object, customObjectConf
ig); | |
| 1234 injectedScript._substituteObjectTagsInCustomPreview(objectGr
oupName, formatted); | |
| 1235 var formatterObjectId = injectedScript._bind(formatters[i],
objectGroupName); | |
| 1236 var result = {header: JSON.stringify(formatted), hasBody: !!
hasBody, formatterObjectId: formatterObjectId}; | |
| 1237 if (customObjectConfig) | |
| 1238 result["configObjectId"] = injectedScript._bind(customOb
jectConfig, objectGroupName); | |
| 1239 return result; | |
| 1240 } catch (e) { | |
| 1241 logError(e); | |
| 1242 } | |
| 1243 } | |
| 1244 } catch (e) { | |
| 1245 logError(e); | |
| 1246 } | |
| 1247 return null; | |
| 1248 }, | |
| 1249 | |
| 1250 /** | |
| 1251 * @return {!RuntimeAgent.ObjectPreview} preview | |
| 1252 */ | |
| 1253 _createEmptyPreview: function() | |
| 1254 { | |
| 1255 var preview = { | |
| 1256 type: /** @type {!RuntimeAgent.ObjectPreviewType.<string>} */ (this.
type), | |
| 1257 description: this.description || toStringDescription(this.value), | |
| 1258 lossless: true, | |
| 1259 overflow: false, | |
| 1260 properties: [], | |
| 1261 __proto__: null | |
| 1262 }; | |
| 1263 if (this.subtype) | |
| 1264 preview.subtype = /** @type {!RuntimeAgent.ObjectPreviewSubtype.<str
ing>} */ (this.subtype); | |
| 1265 return preview; | |
| 1266 }, | |
| 1267 | |
| 1268 /** | |
| 1269 * @param {!Object} object | |
| 1270 * @param {?Array.<string>=} firstLevelKeys | |
| 1271 * @param {?Array.<string>=} secondLevelKeys | |
| 1272 * @param {boolean=} isTable | |
| 1273 * @param {boolean=} skipEntriesPreview | |
| 1274 * @return {!RuntimeAgent.ObjectPreview} preview | |
| 1275 */ | |
| 1276 _generatePreview: function(object, firstLevelKeys, secondLevelKeys, isTable,
skipEntriesPreview) | |
| 1277 { | |
| 1278 var preview = this._createEmptyPreview(); | |
| 1279 var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0; | |
| 1280 | |
| 1281 var propertiesThreshold = { | |
| 1282 properties: isTable ? 1000 : max(5, firstLevelKeysCount), | |
| 1283 indexes: isTable ? 1000 : max(100, firstLevelKeysCount), | |
| 1284 __proto__: null | |
| 1285 }; | |
| 1286 | |
| 1287 try { | |
| 1288 var descriptors = injectedScript._propertyDescriptors(object, undefi
ned, undefined, firstLevelKeys); | |
| 1289 | |
| 1290 this._appendPropertyDescriptors(preview, descriptors, propertiesThre
shold, secondLevelKeys, isTable); | |
| 1291 if (propertiesThreshold.indexes < 0 || propertiesThreshold.propertie
s < 0) | |
| 1292 return preview; | |
| 1293 | |
| 1294 // Add internal properties to preview. | |
| 1295 var rawInternalProperties = InjectedScriptHost.getInternalProperties
(object) || []; | |
| 1296 var internalProperties = []; | |
| 1297 for (var i = 0; i < rawInternalProperties.length; i += 2) { | |
| 1298 push(internalProperties, { | |
| 1299 name: rawInternalProperties[i], | |
| 1300 value: rawInternalProperties[i + 1], | |
| 1301 isOwn: true, | |
| 1302 enumerable: true, | |
| 1303 __proto__: null | |
| 1304 }); | |
| 1305 } | |
| 1306 this._appendPropertyDescriptors(preview, internalProperties, propert
iesThreshold, secondLevelKeys, isTable); | |
| 1307 | |
| 1308 if (this.subtype === "map" || this.subtype === "set" || this.subtype
=== "iterator") | |
| 1309 this._appendEntriesPreview(object, preview, skipEntriesPreview); | |
| 1310 | |
| 1311 } catch (e) { | |
| 1312 preview.lossless = false; | |
| 1313 } | |
| 1314 | |
| 1315 return preview; | |
| 1316 }, | |
| 1317 | |
| 1318 /** | |
| 1319 * @param {!RuntimeAgent.ObjectPreview} preview | |
| 1320 * @param {!Array.<*>|!Iterable.<*>} descriptors | |
| 1321 * @param {!Object} propertiesThreshold | |
| 1322 * @param {?Array.<string>=} secondLevelKeys | |
| 1323 * @param {boolean=} isTable | |
| 1324 */ | |
| 1325 _appendPropertyDescriptors: function(preview, descriptors, propertiesThresho
ld, secondLevelKeys, isTable) | |
| 1326 { | |
| 1327 for (var descriptor of descriptors) { | |
| 1328 if (propertiesThreshold.indexes < 0 || propertiesThreshold.propertie
s < 0) | |
| 1329 break; | |
| 1330 if (!descriptor) | |
| 1331 continue; | |
| 1332 if (descriptor.wasThrown) { | |
| 1333 preview.lossless = false; | |
| 1334 continue; | |
| 1335 } | |
| 1336 | |
| 1337 var name = descriptor.name; | |
| 1338 | |
| 1339 // Ignore __proto__ property, stay lossless. | |
| 1340 if (name === "__proto__") | |
| 1341 continue; | |
| 1342 | |
| 1343 // Ignore non-enumerable members on prototype, stay lossless. | |
| 1344 if (!descriptor.isOwn && !descriptor.enumerable) | |
| 1345 continue; | |
| 1346 | |
| 1347 // Ignore length property of array, stay lossless. | |
| 1348 if (this.subtype === "array" && name === "length") | |
| 1349 continue; | |
| 1350 | |
| 1351 // Ignore size property of map, set, stay lossless. | |
| 1352 if ((this.subtype === "map" || this.subtype === "set") && name === "
size") | |
| 1353 continue; | |
| 1354 | |
| 1355 // Never preview prototype properties, turn lossy. | |
| 1356 if (!descriptor.isOwn) { | |
| 1357 preview.lossless = false; | |
| 1358 continue; | |
| 1359 } | |
| 1360 | |
| 1361 // Ignore computed properties, turn lossy. | |
| 1362 if (!("value" in descriptor)) { | |
| 1363 preview.lossless = false; | |
| 1364 continue; | |
| 1365 } | |
| 1366 | |
| 1367 var value = descriptor.value; | |
| 1368 var type = typeof value; | |
| 1369 | |
| 1370 // Never render functions in object preview, turn lossy | |
| 1371 if (type === "function" && (this.subtype !== "array" || !isUInt32(na
me))) { | |
| 1372 preview.lossless = false; | |
| 1373 continue; | |
| 1374 } | |
| 1375 | |
| 1376 // Special-case HTMLAll. | |
| 1377 if (type === "undefined" && injectedScript._isHTMLAllCollection(valu
e)) | |
| 1378 type = "object"; | |
| 1379 | |
| 1380 // Render own properties. | |
| 1381 if (value === null) { | |
| 1382 this._appendPropertyPreview(preview, { name: name, type: "object
", subtype: "null", value: "null", __proto__: null }, propertiesThreshold); | |
| 1383 continue; | |
| 1384 } | |
| 1385 | |
| 1386 var maxLength = 100; | |
| 1387 if (InjectedScript.primitiveTypes[type]) { | |
| 1388 if (type === "string" && value.length > maxLength) { | |
| 1389 value = this._abbreviateString(value, maxLength, true); | |
| 1390 preview.lossless = false; | |
| 1391 } | |
| 1392 this._appendPropertyPreview(preview, { name: name, type: type, v
alue: toStringDescription(value), __proto__: null }, propertiesThreshold); | |
| 1393 continue; | |
| 1394 } | |
| 1395 | |
| 1396 var property = { name: name, type: type, __proto__: null }; | |
| 1397 var subtype = injectedScript._subtype(value); | |
| 1398 if (subtype) | |
| 1399 property.subtype = subtype; | |
| 1400 | |
| 1401 if (secondLevelKeys === null || secondLevelKeys) { | |
| 1402 var subPreview = this._generatePreview(value, secondLevelKeys ||
undefined, undefined, isTable); | |
| 1403 property.valuePreview = subPreview; | |
| 1404 if (!subPreview.lossless) | |
| 1405 preview.lossless = false; | |
| 1406 if (subPreview.overflow) | |
| 1407 preview.overflow = true; | |
| 1408 } else { | |
| 1409 var description = ""; | |
| 1410 if (type !== "function") | |
| 1411 description = this._abbreviateString(/** @type {string} */ (
injectedScript._describe(value)), maxLength, subtype === "regexp"); | |
| 1412 property.value = description; | |
| 1413 preview.lossless = false; | |
| 1414 } | |
| 1415 this._appendPropertyPreview(preview, property, propertiesThreshold); | |
| 1416 } | |
| 1417 }, | |
| 1418 | |
| 1419 /** | |
| 1420 * @param {!RuntimeAgent.ObjectPreview} preview | |
| 1421 * @param {!Object} property | |
| 1422 * @param {!Object} propertiesThreshold | |
| 1423 */ | |
| 1424 _appendPropertyPreview: function(preview, property, propertiesThreshold) | |
| 1425 { | |
| 1426 if (toString(property.name >>> 0) === property.name) | |
| 1427 propertiesThreshold.indexes--; | |
| 1428 else | |
| 1429 propertiesThreshold.properties--; | |
| 1430 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties <
0) { | |
| 1431 preview.overflow = true; | |
| 1432 preview.lossless = false; | |
| 1433 } else { | |
| 1434 push(preview.properties, property); | |
| 1435 } | |
| 1436 }, | |
| 1437 | |
| 1438 /** | |
| 1439 * @param {!Object} object | |
| 1440 * @param {!RuntimeAgent.ObjectPreview} preview | |
| 1441 * @param {boolean=} skipEntriesPreview | |
| 1442 */ | |
| 1443 _appendEntriesPreview: function(object, preview, skipEntriesPreview) | |
| 1444 { | |
| 1445 var entries = InjectedScriptHost.collectionEntries(object); | |
| 1446 if (!entries) | |
| 1447 return; | |
| 1448 if (skipEntriesPreview) { | |
| 1449 if (entries.length) { | |
| 1450 preview.overflow = true; | |
| 1451 preview.lossless = false; | |
| 1452 } | |
| 1453 return; | |
| 1454 } | |
| 1455 preview.entries = []; | |
| 1456 var entriesThreshold = 5; | |
| 1457 for (var i = 0; i < entries.length; ++i) { | |
| 1458 if (preview.entries.length >= entriesThreshold) { | |
| 1459 preview.overflow = true; | |
| 1460 preview.lossless = false; | |
| 1461 break; | |
| 1462 } | |
| 1463 var entry = nullifyObjectProto(entries[i]); | |
| 1464 var previewEntry = { | |
| 1465 value: generateValuePreview(entry.value), | |
| 1466 __proto__: null | |
| 1467 }; | |
| 1468 if ("key" in entry) | |
| 1469 previewEntry.key = generateValuePreview(entry.key); | |
| 1470 push(preview.entries, previewEntry); | |
| 1471 } | |
| 1472 | |
| 1473 /** | |
| 1474 * @param {*} value | |
| 1475 * @return {!RuntimeAgent.ObjectPreview} | |
| 1476 */ | |
| 1477 function generateValuePreview(value) | |
| 1478 { | |
| 1479 var remoteObject = new InjectedScript.RemoteObject(value, undefined,
true, undefined, true, undefined, undefined, true); | |
| 1480 var valuePreview = remoteObject.preview || remoteObject._createEmpty
Preview(); | |
| 1481 if (!valuePreview.lossless) | |
| 1482 preview.lossless = false; | |
| 1483 return valuePreview; | |
| 1484 } | |
| 1485 }, | |
| 1486 | |
| 1487 /** | |
| 1488 * @param {string} string | |
| 1489 * @param {number} maxLength | |
| 1490 * @param {boolean=} middle | |
| 1491 * @return {string} | |
| 1492 */ | |
| 1493 _abbreviateString: function(string, maxLength, middle) | |
| 1494 { | |
| 1495 if (string.length <= maxLength) | |
| 1496 return string; | |
| 1497 if (middle) { | |
| 1498 var leftHalf = maxLength >> 1; | |
| 1499 var rightHalf = maxLength - leftHalf - 1; | |
| 1500 return string.substr(0, leftHalf) + "\u2026" + string.substr(string.
length - rightHalf, rightHalf); | |
| 1501 } | |
| 1502 return string.substr(0, maxLength) + "\u2026"; | |
| 1503 }, | |
| 1504 | |
| 1505 __proto__: null | |
| 1506 } | |
| 1507 | |
| 1508 /** | |
| 1509 * @constructor | |
| 1510 * @param {number} ordinal | |
| 1511 * @param {!JavaScriptCallFrame} callFrame | |
| 1512 * @param {number} asyncOrdinal | |
| 1513 */ | |
| 1514 InjectedScript.CallFrameProxy = function(ordinal, callFrame, asyncOrdinal) | |
| 1515 { | |
| 1516 this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + in
jectedScriptId + (asyncOrdinal ? ",\"asyncOrdinal\":" + asyncOrdinal : "") + "}"
; | |
| 1517 this.functionName = callFrame.functionName; | |
| 1518 this.functionLocation = { scriptId: toString(callFrame.sourceID), lineNumber
: callFrame.functionLine, columnNumber: callFrame.functionColumn, __proto__: nul
l }; | |
| 1519 this.location = { scriptId: toString(callFrame.sourceID), lineNumber: callFr
ame.line, columnNumber: callFrame.column, __proto__: null }; | |
| 1520 this.scopeChain = this._wrapScopeChain(callFrame); | |
| 1521 this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace"); | |
| 1522 if (callFrame.isAtReturn) | |
| 1523 this.returnValue = injectedScript._wrapObject(callFrame.returnValue, "ba
cktrace"); | |
| 1524 } | |
| 1525 | |
| 1526 InjectedScript.CallFrameProxy.prototype = { | |
| 1527 /** | |
| 1528 * @param {!JavaScriptCallFrame} callFrame | |
| 1529 * @return {!Array.<!DebuggerAgent.Scope>} | |
| 1530 */ | |
| 1531 _wrapScopeChain: function(callFrame) | |
| 1532 { | |
| 1533 var scopeChain = callFrame.scopeChain; | |
| 1534 var scopeChainProxy = []; | |
| 1535 for (var i = 0; i < scopeChain.length; ++i) | |
| 1536 scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(
callFrame.scopeType(i), callFrame.scopeName(i), scopeChain[i], "backtrace"); | |
| 1537 return scopeChainProxy; | |
| 1538 }, | |
| 1539 | |
| 1540 __proto__: null | |
| 1541 } | |
| 1542 | |
| 1543 /** | |
| 1544 * @const | |
| 1545 * @type {!Object.<number, !DebuggerAgent.ScopeType>} | |
| 1546 */ | |
| 1547 InjectedScript.CallFrameProxy._scopeTypeNames = { | |
| 1548 0: "global", | |
| 1549 1: "local", | |
| 1550 2: "with", | |
| 1551 3: "closure", | |
| 1552 4: "catch", | |
| 1553 5: "block", | |
| 1554 6: "script", | |
| 1555 __proto__: null | |
| 1556 }; | |
| 1557 | |
| 1558 /** | |
| 1559 * @param {number} scopeTypeCode | |
| 1560 * @param {string} scopeName | |
| 1561 * @param {*} scopeObject | |
| 1562 * @param {string} groupId | |
| 1563 * @return {!DebuggerAgent.Scope} | |
| 1564 */ | |
| 1565 InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeNa
me, scopeObject, groupId) | |
| 1566 { | |
| 1567 var scope = { | |
| 1568 object: injectedScript._wrapObject(scopeObject, groupId), | |
| 1569 type: InjectedScript.CallFrameProxy._scopeTypeNames[scopeTypeCode], | |
| 1570 __proto__: null | |
| 1571 }; | |
| 1572 if (scopeName) | |
| 1573 scope.name = scopeName; | |
| 1574 return scope; | |
| 1575 } | |
| 1576 | |
| 1577 /** | |
| 1578 * @constructor | |
| 1579 * @param {!CommandLineAPIImpl} commandLineAPIImpl | |
| 1580 * @param {?JavaScriptCallFrame} callFrame | |
| 1581 */ | |
| 1582 function CommandLineAPI(commandLineAPIImpl, callFrame) | |
| 1583 { | |
| 1584 /** | |
| 1585 * @param {string} member | |
| 1586 * @return {boolean} | |
| 1587 */ | |
| 1588 function inScopeVariables(member) | |
| 1589 { | |
| 1590 if (!callFrame) | |
| 1591 return (member in inspectedGlobalObject); | |
| 1592 | |
| 1593 var scopeChain = callFrame.scopeChain; | |
| 1594 for (var i = 0; i < scopeChain.length; ++i) { | |
| 1595 if (member in scopeChain[i]) | |
| 1596 return true; | |
| 1597 } | |
| 1598 return false; | |
| 1599 } | |
| 1600 | |
| 1601 /** | |
| 1602 * @param {string} name The name of the method for which a toString method s
hould be generated. | |
| 1603 * @return {function():string} | |
| 1604 */ | |
| 1605 function customToStringMethod(name) | |
| 1606 { | |
| 1607 return function() | |
| 1608 { | |
| 1609 var funcArgsSyntax = ""; | |
| 1610 try { | |
| 1611 var funcSyntax = "" + commandLineAPIImpl[name]; | |
| 1612 funcSyntax = funcSyntax.replace(/\n/g, " "); | |
| 1613 funcSyntax = funcSyntax.replace(/^function[^\(]*\(([^\)]*)\).*$/
, "$1"); | |
| 1614 funcSyntax = funcSyntax.replace(/\s*,\s*/g, ", "); | |
| 1615 funcSyntax = funcSyntax.replace(/\bopt_(\w+)\b/g, "[$1]"); | |
| 1616 funcArgsSyntax = funcSyntax.trim(); | |
| 1617 } catch (e) { | |
| 1618 } | |
| 1619 return "function " + name + "(" + funcArgsSyntax + ") { [Command Lin
e API] }"; | |
| 1620 }; | |
| 1621 } | |
| 1622 | |
| 1623 for (var i = 0; i < CommandLineAPI.members_.length; ++i) { | |
| 1624 var member = CommandLineAPI.members_[i]; | |
| 1625 if (inScopeVariables(member)) | |
| 1626 continue; | |
| 1627 | |
| 1628 this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl); | |
| 1629 this[member].toString = customToStringMethod(member); | |
| 1630 } | |
| 1631 | |
| 1632 for (var i = 0; i < 5; ++i) { | |
| 1633 var member = "$" + i; | |
| 1634 if (inScopeVariables(member)) | |
| 1635 continue; | |
| 1636 | |
| 1637 this.__defineGetter__("$" + i, bind(commandLineAPIImpl._inspectedObject,
commandLineAPIImpl, i)); | |
| 1638 } | |
| 1639 | |
| 1640 this.$_ = injectedScript._lastResult; | |
| 1641 | |
| 1642 this.__proto__ = null; | |
| 1643 } | |
| 1644 | |
| 1645 // NOTE: Please keep the list of API methods below synchronized to that in WebIn
spector.RuntimeModel | |
| 1646 // and V8InjectedScriptHost! | |
| 1647 // NOTE: Argument names of these methods will be printed in the console, so use
pretty names! | |
| 1648 /** | |
| 1649 * @type {!Array.<string>} | |
| 1650 * @const | |
| 1651 */ | |
| 1652 CommandLineAPI.members_ = [ | |
| 1653 "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd", | |
| 1654 "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventLis
teners", | |
| 1655 "debug", "undebug", "monitor", "unmonitor", "table" | |
| 1656 ]; | |
| 1657 | |
| 1658 /** | |
| 1659 * @constructor | |
| 1660 */ | |
| 1661 function CommandLineAPIImpl() | |
| 1662 { | |
| 1663 } | |
| 1664 | |
| 1665 CommandLineAPIImpl.prototype = { | |
| 1666 /** | |
| 1667 * @param {string} selector | |
| 1668 * @param {!Node=} opt_startNode | |
| 1669 * @return {*} | |
| 1670 */ | |
| 1671 $: function (selector, opt_startNode) | |
| 1672 { | |
| 1673 if (this._canQuerySelectorOnNode(opt_startNode)) | |
| 1674 return opt_startNode.querySelector(selector); | |
| 1675 | |
| 1676 return inspectedGlobalObject.document.querySelector(selector); | |
| 1677 }, | |
| 1678 | |
| 1679 /** | |
| 1680 * @param {string} selector | |
| 1681 * @param {!Node=} opt_startNode | |
| 1682 * @return {*} | |
| 1683 */ | |
| 1684 $$: function (selector, opt_startNode) | |
| 1685 { | |
| 1686 if (this._canQuerySelectorOnNode(opt_startNode)) | |
| 1687 return slice(opt_startNode.querySelectorAll(selector)); | |
| 1688 return slice(inspectedGlobalObject.document.querySelectorAll(selector)); | |
| 1689 }, | |
| 1690 | |
| 1691 /** | |
| 1692 * @param {!Node=} node | |
| 1693 * @return {boolean} | |
| 1694 */ | |
| 1695 _canQuerySelectorOnNode: function(node) | |
| 1696 { | |
| 1697 return !!node && InjectedScriptHost.subtype(node) === "node" && (node.no
deType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nod
eType === Node.DOCUMENT_FRAGMENT_NODE); | |
| 1698 }, | |
| 1699 | |
| 1700 /** | |
| 1701 * @param {string} xpath | |
| 1702 * @param {!Node=} opt_startNode | |
| 1703 * @return {*} | |
| 1704 */ | |
| 1705 $x: function(xpath, opt_startNode) | |
| 1706 { | |
| 1707 var doc = (opt_startNode && opt_startNode.ownerDocument) || inspectedGlo
balObject.document; | |
| 1708 var result = doc.evaluate(xpath, opt_startNode || doc, null, XPathResult
.ANY_TYPE, null); | |
| 1709 switch (result.resultType) { | |
| 1710 case XPathResult.NUMBER_TYPE: | |
| 1711 return result.numberValue; | |
| 1712 case XPathResult.STRING_TYPE: | |
| 1713 return result.stringValue; | |
| 1714 case XPathResult.BOOLEAN_TYPE: | |
| 1715 return result.booleanValue; | |
| 1716 default: | |
| 1717 var nodes = []; | |
| 1718 var node; | |
| 1719 while (node = result.iterateNext()) | |
| 1720 push(nodes, node); | |
| 1721 return nodes; | |
| 1722 } | |
| 1723 }, | |
| 1724 | |
| 1725 /** | |
| 1726 * @return {*} | |
| 1727 */ | |
| 1728 dir: function(var_args) | |
| 1729 { | |
| 1730 return InjectedScriptHost.callFunction(inspectedGlobalObject.console.dir
, inspectedGlobalObject.console, slice(arguments)); | |
| 1731 }, | |
| 1732 | |
| 1733 /** | |
| 1734 * @return {*} | |
| 1735 */ | |
| 1736 dirxml: function(var_args) | |
| 1737 { | |
| 1738 return InjectedScriptHost.callFunction(inspectedGlobalObject.console.dir
xml, inspectedGlobalObject.console, slice(arguments)); | |
| 1739 }, | |
| 1740 | |
| 1741 /** | |
| 1742 * @return {!Array.<string>} | |
| 1743 */ | |
| 1744 keys: function(object) | |
| 1745 { | |
| 1746 return Object.keys(object); | |
| 1747 }, | |
| 1748 | |
| 1749 /** | |
| 1750 * @return {!Array.<*>} | |
| 1751 */ | |
| 1752 values: function(object) | |
| 1753 { | |
| 1754 var result = []; | |
| 1755 for (var key in object) | |
| 1756 push(result, object[key]); | |
| 1757 return result; | |
| 1758 }, | |
| 1759 | |
| 1760 /** | |
| 1761 * @return {*} | |
| 1762 */ | |
| 1763 profile: function(opt_title) | |
| 1764 { | |
| 1765 return InjectedScriptHost.callFunction(inspectedGlobalObject.console.pro
file, inspectedGlobalObject.console, slice(arguments)); | |
| 1766 }, | |
| 1767 | |
| 1768 /** | |
| 1769 * @return {*} | |
| 1770 */ | |
| 1771 profileEnd: function(opt_title) | |
| 1772 { | |
| 1773 return InjectedScriptHost.callFunction(inspectedGlobalObject.console.pro
fileEnd, inspectedGlobalObject.console, slice(arguments)); | |
| 1774 }, | |
| 1775 | |
| 1776 /** | |
| 1777 * @param {!Object} object | |
| 1778 * @param {!Array.<string>|string=} opt_types | |
| 1779 */ | |
| 1780 monitorEvents: function(object, opt_types) | |
| 1781 { | |
| 1782 if (!object || !object.addEventListener || !object.removeEventListener) | |
| 1783 return; | |
| 1784 var types = this._normalizeEventTypes(opt_types); | |
| 1785 for (var i = 0; i < types.length; ++i) { | |
| 1786 object.removeEventListener(types[i], this._logEvent, false); | |
| 1787 object.addEventListener(types[i], this._logEvent, false); | |
| 1788 } | |
| 1789 }, | |
| 1790 | |
| 1791 /** | |
| 1792 * @param {!Object} object | |
| 1793 * @param {!Array.<string>|string=} opt_types | |
| 1794 */ | |
| 1795 unmonitorEvents: function(object, opt_types) | |
| 1796 { | |
| 1797 if (!object || !object.addEventListener || !object.removeEventListener) | |
| 1798 return; | |
| 1799 var types = this._normalizeEventTypes(opt_types); | |
| 1800 for (var i = 0; i < types.length; ++i) | |
| 1801 object.removeEventListener(types[i], this._logEvent, false); | |
| 1802 }, | |
| 1803 | |
| 1804 /** | |
| 1805 * @param {*} object | |
| 1806 * @return {*} | |
| 1807 */ | |
| 1808 inspect: function(object) | |
| 1809 { | |
| 1810 return injectedScript._inspect(object); | |
| 1811 }, | |
| 1812 | |
| 1813 copy: function(object) | |
| 1814 { | |
| 1815 var string; | |
| 1816 if (injectedScript._subtype(object) === "node") { | |
| 1817 string = object.outerHTML; | |
| 1818 } else if (injectedScript.isPrimitiveValue(object)) { | |
| 1819 string = toString(object); | |
| 1820 } else { | |
| 1821 try { | |
| 1822 string = JSON.stringify(object, null, " "); | |
| 1823 } catch (e) { | |
| 1824 string = toString(object); | |
| 1825 } | |
| 1826 } | |
| 1827 | |
| 1828 var hints = { copyToClipboard: true, __proto__: null }; | |
| 1829 var remoteObject = injectedScript._wrapObject(string, "") | |
| 1830 InjectedScriptHost.inspect(remoteObject, hints); | |
| 1831 }, | |
| 1832 | |
| 1833 clear: function() | |
| 1834 { | |
| 1835 InjectedScriptHost.clearConsoleMessages(); | |
| 1836 }, | |
| 1837 | |
| 1838 /** | |
| 1839 * @param {!Node} node | |
| 1840 * @return {!Array.<!{type: string, listener: function(), useCapture: boolea
n, remove: function()}>|undefined} | |
| 1841 */ | |
| 1842 getEventListeners: function(node) | |
| 1843 { | |
| 1844 var result = nullifyObjectProto(InjectedScriptHost.getEventListeners(nod
e)); | |
| 1845 if (!result) | |
| 1846 return result; | |
| 1847 /** @this {{type: string, listener: function(), useCapture: boolean}} */ | |
| 1848 var removeFunc = function() | |
| 1849 { | |
| 1850 node.removeEventListener(this.type, this.listener, this.useCapture); | |
| 1851 } | |
| 1852 for (var type in result) { | |
| 1853 var listeners = result[type]; | |
| 1854 for (var i = 0, listener; listener = listeners[i]; ++i) { | |
| 1855 listener["type"] = type; | |
| 1856 listener["remove"] = removeFunc; | |
| 1857 } | |
| 1858 } | |
| 1859 return result; | |
| 1860 }, | |
| 1861 | |
| 1862 debug: function(fn) | |
| 1863 { | |
| 1864 InjectedScriptHost.debugFunction(fn); | |
| 1865 }, | |
| 1866 | |
| 1867 undebug: function(fn) | |
| 1868 { | |
| 1869 InjectedScriptHost.undebugFunction(fn); | |
| 1870 }, | |
| 1871 | |
| 1872 monitor: function(fn) | |
| 1873 { | |
| 1874 InjectedScriptHost.monitorFunction(fn); | |
| 1875 }, | |
| 1876 | |
| 1877 unmonitor: function(fn) | |
| 1878 { | |
| 1879 InjectedScriptHost.unmonitorFunction(fn); | |
| 1880 }, | |
| 1881 | |
| 1882 table: function(data, opt_columns) | |
| 1883 { | |
| 1884 InjectedScriptHost.callFunction(inspectedGlobalObject.console.table, ins
pectedGlobalObject.console, slice(arguments)); | |
| 1885 }, | |
| 1886 | |
| 1887 /** | |
| 1888 * @param {number} num | |
| 1889 */ | |
| 1890 _inspectedObject: function(num) | |
| 1891 { | |
| 1892 return InjectedScriptHost.inspectedObject(num); | |
| 1893 }, | |
| 1894 | |
| 1895 /** | |
| 1896 * @param {!Array.<string>|string=} types | |
| 1897 * @return {!Array.<string>} | |
| 1898 */ | |
| 1899 _normalizeEventTypes: function(types) | |
| 1900 { | |
| 1901 if (typeof types === "undefined") | |
| 1902 types = ["mouse", "key", "touch", "pointer", "control", "load", "unl
oad", "abort", "error", "select", "input", "change", "submit", "reset", "focus",
"blur", "resize", "scroll", "search", "devicemotion", "deviceorientation"]; | |
| 1903 else if (typeof types === "string") | |
| 1904 types = [types]; | |
| 1905 | |
| 1906 var result = []; | |
| 1907 for (var i = 0; i < types.length; ++i) { | |
| 1908 if (types[i] === "mouse") | |
| 1909 push(result, "click", "dblclick", "mousedown", "mouseeenter", "m
ouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "mouseleave", "mous
ewheel"); | |
| 1910 else if (types[i] === "key") | |
| 1911 push(result, "keydown", "keyup", "keypress", "textInput"); | |
| 1912 else if (types[i] === "touch") | |
| 1913 push(result, "touchstart", "touchmove", "touchend", "touchcancel
"); | |
| 1914 else if (types[i] === "pointer") | |
| 1915 push(result, "pointerover", "pointerout", "pointerenter", "point
erleave", "pointerdown", "pointerup", "pointermove", "pointercancel", "gotpointe
rcapture", "lostpointercapture"); | |
| 1916 else if (types[i] === "control") | |
| 1917 push(result, "resize", "scroll", "zoom", "focus", "blur", "selec
t", "input", "change", "submit", "reset"); | |
| 1918 else | |
| 1919 push(result, types[i]); | |
| 1920 } | |
| 1921 return result; | |
| 1922 }, | |
| 1923 | |
| 1924 /** | |
| 1925 * @param {!Event} event | |
| 1926 */ | |
| 1927 _logEvent: function(event) | |
| 1928 { | |
| 1929 inspectedGlobalObject.console.log(event.type, event); | |
| 1930 } | |
| 1931 } | |
| 1932 | |
| 1933 injectedScript._commandLineAPIImpl = new CommandLineAPIImpl(); | |
| 1934 return injectedScript; | |
| 1935 }) | |
| OLD | NEW |