2 * Copyright (C) 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * @param {InjectedScriptHost} InjectedScriptHost
31 * @param {Window} inspectedWindow
32 * @param {number} injectedScriptId
34 (function (InjectedScriptHost, inspectedWindow, injectedScriptId) {
36 // Protect against Object overwritten by the user code.
37 var Object = {}.constructor;
40 * @param {Arguments} array
41 * @param {number=} index
44 function slice(array, index)
47 for (var i = index || 0; i < array.length; ++i)
48 result.push(array[i]);
53 * Please use this bind, not the one from Function.prototype
54 * @param {function(...)} func
55 * @param {Object} thisObject
56 * @param {...number} var_args
58 function bind(func, thisObject, var_args)
60 var args = slice(arguments, 2);
63 * @param {...number} var_args
65 function bound(var_args)
67 return func.apply(thisObject, args.concat(slice(arguments)));
69 bound.toString = function() {
70 return "bound: " + func;
78 var InjectedScript = function()
80 this._idToWrappedObject = {};
81 this._idToObjectGroupName = {};
82 this._objectGroups = {};
87 * @type {Object.<string, boolean>}
90 InjectedScript.primitiveTypes = {
97 InjectedScript.prototype = {
102 isPrimitiveValue: function(object)
104 // FIXME(33716): typeof document.all is always 'undefined'.
105 return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
110 * @param {string} groupName
111 * @param {boolean} canAccessInspectedWindow
112 * @param {boolean} generatePreview
113 * @return {!RuntimeAgent.RemoteObject}
115 wrapObject: function(object, groupName, canAccessInspectedWindow, generatePreview)
117 if (canAccessInspectedWindow)
118 return this._wrapObject(object, groupName, false, generatePreview);
119 return this._fallbackWrapper(object);
124 * @return {!RuntimeAgent.RemoteObject}
126 _fallbackWrapper: function(object)
129 result.type = typeof object;
130 if (this.isPrimitiveValue(object))
131 result.value = object;
133 result.description = this._toString(object);
134 return /** @type {!RuntimeAgent.RemoteObject} */ (result);
138 * @param {boolean} canAccessInspectedWindow
139 * @param {Object} table
140 * @param {Array.<string>|string|boolean} columns
141 * @return {!RuntimeAgent.RemoteObject}
143 wrapTable: function(canAccessInspectedWindow, table, columns)
145 if (!canAccessInspectedWindow)
146 return this._fallbackWrapper(table);
147 var columnNames = null;
148 if (typeof columns === "string")
150 if (InjectedScriptHost.type(columns) == "array") {
152 for (var i = 0; i < columns.length; ++i)
153 columnNames.push(String(columns[i]));
155 return this._wrapObject(table, "console", false, true, columnNames);
161 inspectNode: function(object)
163 this._inspect(object);
170 _inspect: function(object)
172 if (arguments.length === 0)
175 var objectId = this._wrapObject(object, "");
178 switch (injectedScript._describe(object)) {
180 var databaseId = InjectedScriptHost.databaseId(object)
182 hints.databaseId = databaseId;
185 var storageId = InjectedScriptHost.storageId(object)
187 hints.domStorageId = InjectedScriptHost.evaluate("(" + storageId + ")");
190 InjectedScriptHost.inspect(objectId, hints);
195 * This method cannot throw.
197 * @param {string=} objectGroupName
198 * @param {boolean=} forceValueType
199 * @param {boolean=} generatePreview
200 * @param {?Array.<string>=} columnNames
201 * @return {!RuntimeAgent.RemoteObject}
202 * @suppress {checkTypes}
204 _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames)
207 return new InjectedScript.RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
210 var description = injectedScript._describe(e);
212 var description = "<failed to convert exception to string>";
214 return new InjectedScript.RemoteObject(description);
220 * @param {string=} objectGroupName
223 _bind: function(object, objectGroupName)
225 var id = InjectedScriptHost.objectId(object);
226 this._idToWrappedObject[id] = object;
227 var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}";
228 if (objectGroupName) {
229 var group = this._objectGroups[objectGroupName];
232 this._objectGroups[objectGroupName] = group;
235 this._idToObjectGroupName[id] = objectGroupName;
241 * @param {string} objectId
244 _parseObjectId: function(objectId)
246 return InjectedScriptHost.evaluate("(" + objectId + ")");
250 * @param {string} objectGroupName
252 releaseObjectGroup: function(objectGroupName)
254 var group = this._objectGroups[objectGroupName];
257 for (var i = 0; i < group.length; i++)
258 this._releaseObject(group[i]);
259 delete this._objectGroups[objectGroupName];
263 * @param {string} methodName
264 * @param {string} args
267 dispatch: function(methodName, args)
269 var argsArray = InjectedScriptHost.evaluate("(" + args + ")");
270 var result = this[methodName].apply(this, argsArray);
271 if (typeof result === "undefined") {
272 inspectedWindow.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
279 * @param {string} objectId
280 * @param {boolean} ownProperties
281 * @return {Array.<RuntimeAgent.PropertyDescriptor>|boolean}
283 getProperties: function(objectId, ownProperties)
285 var parsedObjectId = this._parseObjectId(objectId);
286 var object = this._objectForId(parsedObjectId);
287 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
289 if (!this._isDefined(object))
291 var descriptors = this._propertyDescriptors(object, ownProperties);
293 // Go over properties, wrap object values.
294 for (var i = 0; i < descriptors.length; ++i) {
295 var descriptor = descriptors[i];
296 if ("get" in descriptor)
297 descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
298 if ("set" in descriptor)
299 descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
300 if ("value" in descriptor)
301 descriptor.value = this._wrapObject(descriptor.value, objectGroupName);
302 if (!("configurable" in descriptor))
303 descriptor.configurable = false;
304 if (!("enumerable" in descriptor))
305 descriptor.enumerable = false;
311 * @param {string} objectId
312 * @return {Array.<Object>|boolean}
314 getInternalProperties: function(objectId, ownProperties)
316 var parsedObjectId = this._parseObjectId(objectId);
317 var object = this._objectForId(parsedObjectId);
318 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
319 if (!this._isDefined(object))
321 var descriptors = [];
322 var internalProperties = InjectedScriptHost.getInternalProperties(object);
323 if (internalProperties) {
324 for (var i = 0; i < internalProperties.length; i++) {
325 var property = internalProperties[i];
328 value: this._wrapObject(property.value, objectGroupName)
330 descriptors.push(descriptor);
337 * @param {string} functionId
338 * @return {!DebuggerAgent.FunctionDetails|string}
340 getFunctionDetails: function(functionId)
342 var parsedFunctionId = this._parseObjectId(functionId);
343 var func = this._objectForId(parsedFunctionId);
344 if (typeof func !== "function")
345 return "Cannot resolve function by id.";
346 var details = InjectedScriptHost.functionDetails(func);
347 if ("rawScopes" in details) {
348 var objectGroupName = this._idToObjectGroupName[parsedFunctionId.id];
349 var rawScopes = details.rawScopes;
351 delete details.rawScopes;
352 for (var i = 0; i < rawScopes.length; i++)
353 scopes.push(InjectedScript.CallFrameProxy._createScopeJson(rawScopes[i].type, rawScopes[i].object, objectGroupName));
354 details.scopeChain = scopes;
360 * @param {string} objectId
362 releaseObject: function(objectId)
364 var parsedObjectId = this._parseObjectId(objectId);
365 this._releaseObject(parsedObjectId.id);
371 _releaseObject: function(id)
373 var object = this._idToWrappedObject[id];
375 InjectedScriptHost.releaseObjectId(object);
376 delete this._idToWrappedObject[id];
377 delete this._idToObjectGroupName[id];
381 * @param {Object} object
382 * @param {boolean} ownProperties
383 * @return {Array.<Object>}
385 _propertyDescriptors: function(object, ownProperties)
387 var descriptors = [];
388 var nameProcessed = {};
389 nameProcessed["__proto__"] = null;
390 for (var o = object; this._isDefined(o); o = o.__proto__) {
391 var names = Object.getOwnPropertyNames(/** @type {!Object} */ (o));
392 for (var i = 0; i < names.length; ++i) {
394 if (nameProcessed[name])
398 nameProcessed[name] = true;
399 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */ (object), name);
401 // Not all bindings provide proper descriptors. Fall back to the writable, configurable property.
403 descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false};
405 descriptor.isOwn = true;
406 descriptors.push(descriptor);
414 descriptor.value = e;
415 descriptor.wasThrown = true;
418 descriptor.name = name;
420 descriptor.isOwn = true;
421 descriptors.push(descriptor);
424 if (object.__proto__)
425 descriptors.push({ name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true});
433 * @param {string} expression
434 * @param {string} objectGroup
435 * @param {boolean} injectCommandLineAPI
436 * @param {boolean} returnByValue
437 * @param {boolean} generatePreview
440 evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
442 return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview);
446 * @param {string} objectId
447 * @param {string} expression
448 * @param {boolean} returnByValue
449 * @return {Object|string}
451 callFunctionOn: function(objectId, expression, args, returnByValue)
453 var parsedObjectId = this._parseObjectId(objectId);
454 var object = this._objectForId(parsedObjectId);
455 if (!this._isDefined(object))
456 return "Could not find object with given id";
459 var resolvedArgs = [];
460 args = InjectedScriptHost.evaluate(args);
461 for (var i = 0; i < args.length; ++i) {
462 var resolvedCallArgument;
464 resolvedCallArgument = this._resolveCallArgument(args[i]);
468 resolvedArgs.push(resolvedCallArgument)
473 var objectGroup = this._idToObjectGroupName[parsedObjectId.id];
474 var func = InjectedScriptHost.evaluate("(" + expression + ")");
475 if (typeof func !== "function")
476 return "Given expression does not evaluate to a function";
478 return { wasThrown: false,
479 result: this._wrapObject(func.apply(object, resolvedArgs), objectGroup, returnByValue) };
481 return this._createThrownValue(e, objectGroup);
486 * Resolves a value from CallArgument description.
487 * @param {RuntimeAgent.CallArgument} callArgumentJson
488 * @return {*} resolved value
489 * @throw {string} error message
491 _resolveCallArgument: function(callArgumentJson) {
492 var objectId = callArgumentJson.objectId;
494 var parsedArgId = this._parseObjectId(objectId);
495 if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
496 throw "Arguments should belong to the same JavaScript world as the target object.";
498 var resolvedArg = this._objectForId(parsedArgId);
499 if (!this._isDefined(resolvedArg))
500 throw "Could not find object with given id";
503 } else if ("value" in callArgumentJson)
504 return callArgumentJson.value;
510 * @param {Function} evalFunction
511 * @param {Object} object
512 * @param {string} objectGroup
513 * @param {boolean} isEvalOnCallFrame
514 * @param {boolean} injectCommandLineAPI
515 * @param {boolean} returnByValue
516 * @param {boolean} generatePreview
519 _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview)
522 return { wasThrown: false,
523 result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI), objectGroup, returnByValue, generatePreview) };
525 return this._createThrownValue(e, objectGroup);
531 * @param {string} objectGroup
534 _createThrownValue: function(value, objectGroup)
536 var remoteObject = this._wrapObject(value, objectGroup);
538 remoteObject.description = this._toString(value);
540 return { wasThrown: true,
541 result: remoteObject };
545 * @param {Function} evalFunction
546 * @param {Object} object
547 * @param {string} objectGroup
548 * @param {string} expression
549 * @param {boolean} isEvalOnCallFrame
550 * @param {boolean} injectCommandLineAPI
553 _evaluateOn: function(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI)
555 // Only install command line api object for the time of evaluation.
556 // Surround the expression in with statements to inject our command line API so that
557 // the window object properties still take more precedent than our API functions.
560 if (injectCommandLineAPI && inspectedWindow.console) {
561 inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
562 expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
564 var result = evalFunction.call(object, expression);
565 if (objectGroup === "console")
566 this._lastResult = result;
569 if (injectCommandLineAPI && inspectedWindow.console)
570 delete inspectedWindow.console._commandLineAPI;
575 * @param {Object} callFrame
576 * @return {Array.<InjectedScript.CallFrameProxy>|boolean}
578 wrapCallFrames: function(callFrame)
586 result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
587 callFrame = callFrame.caller;
593 * @param {Object} topCallFrame
594 * @param {string} callFrameId
595 * @param {string} expression
596 * @param {string} objectGroup
597 * @param {boolean} injectCommandLineAPI
598 * @param {boolean} returnByValue
599 * @param {boolean} generatePreview
602 evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
604 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
606 return "Could not find call frame with given id";
607 return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview);
611 * @param {Object} topCallFrame
612 * @param {string} callFrameId
615 restartFrame: function(topCallFrame, callFrameId)
617 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
619 return "Could not find call frame with given id";
620 var result = callFrame.restart();
621 if (result === false)
622 result = "Restart frame is not supported";
627 * Either callFrameId or functionObjectId must be specified.
628 * @param {Object} topCallFrame
629 * @param {string|boolean} callFrameId or false
630 * @param {string|boolean} functionObjectId or false
631 * @param {number} scopeNumber
632 * @param {string} variableName
633 * @param {string} newValueJsonString RuntimeAgent.CallArgument structure serialized as string
634 * @return {string|undefined} undefined if success or an error message
636 setVariableValue: function(topCallFrame, callFrameId, functionObjectId, scopeNumber, variableName, newValueJsonString)
639 if (typeof callFrameId === "string") {
640 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
642 return "Could not find call frame with given id";
643 setter = callFrame.setVariableValue.bind(callFrame);
645 var parsedFunctionId = this._parseObjectId(/** @type {string} */(functionObjectId));
646 var func = this._objectForId(parsedFunctionId);
647 if (typeof func !== "function")
648 return "Cannot resolve function by id.";
649 setter = InjectedScriptHost.setFunctionVariableValue.bind(InjectedScriptHost, func);
653 newValueJson = InjectedScriptHost.evaluate("(" + newValueJsonString + ")");
655 return "Failed to parse new value JSON " + newValueJsonString + " : " + e;
659 resolvedValue = this._resolveCallArgument(newValueJson);
664 setter(scopeNumber, variableName, resolvedValue);
666 return "Failed to change variable value: " + e;
672 * @param {Object} topCallFrame
673 * @param {string} callFrameId
676 _callFrameForId: function(topCallFrame, callFrameId)
678 var parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
679 var ordinal = parsedCallFrameId["ordinal"];
680 var callFrame = topCallFrame;
681 while (--ordinal >= 0 && callFrame)
682 callFrame = callFrame.caller;
687 * @param {Object} objectId
690 _objectForId: function(objectId)
692 return this._idToWrappedObject[objectId.id];
696 * @param {string} objectId
699 findObjectById: function(objectId)
701 var parsedObjectId = this._parseObjectId(objectId);
702 return this._objectForId(parsedObjectId);
706 * @param {string} objectId
709 nodeForObjectId: function(objectId)
711 var object = this.findObjectById(objectId);
712 if (!object || this._subtype(object) !== "node")
714 return /** @type {Node} */ (object);
718 * @param {string} name
721 module: function(name)
723 return this._modules[name];
727 * @param {string} name
728 * @param {string} source
731 injectModule: function(name, source)
733 delete this._modules[name];
734 var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
735 if (typeof moduleFunction !== "function") {
736 inspectedWindow.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
739 var module = moduleFunction.call(inspectedWindow, InjectedScriptHost, inspectedWindow, injectedScriptId);
740 this._modules[name] = module;
748 _isDefined: function(object)
750 return !!object || this._isHTMLAllCollection(object);
757 _isHTMLAllCollection: function(object)
759 // document.all is reported as undefined, but we still want to process it.
760 return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCollection(object);
764 * @param {Object=} obj
767 _subtype: function(obj)
772 if (this.isPrimitiveValue(obj))
775 if (this._isHTMLAllCollection(obj))
778 var preciseType = InjectedScriptHost.type(obj);
782 // FireBug's array detection.
784 if (typeof obj.splice === "function" && isFinite(obj.length))
786 if (Object.prototype.toString.call(obj) === "[object Arguments]" && isFinite(obj.length)) // arguments.
791 // If owning frame has navigated to somewhere else window properties will be undefined.
799 _describe: function(obj)
801 if (this.isPrimitiveValue(obj))
804 obj = /** @type {Object} */ (obj);
806 // Type is object, get subtype.
807 var subtype = this._subtype(obj);
809 if (subtype === "regexp")
810 return this._toString(obj);
812 if (subtype === "date")
813 return this._toString(obj);
815 if (subtype === "node") {
816 var description = obj.nodeName.toLowerCase();
817 switch (obj.nodeType) {
818 case 1 /* Node.ELEMENT_NODE */:
819 description += obj.id ? "#" + obj.id : "";
820 var className = obj.className;
821 description += className ? "." + className : "";
823 case 10 /*Node.DOCUMENT_TYPE_NODE */:
824 description = "<!DOCTYPE " + description + ">";
830 var className = InjectedScriptHost.internalConstructorName(obj);
831 if (subtype === "array") {
832 if (typeof obj.length === "number")
833 className += "[" + obj.length + "]";
837 // NodeList in JSC is a function, check for array prior to this.
838 if (typeof obj === "function")
839 return this._toString(obj);
841 if (className === "Object") {
842 // In Chromium DOM wrapper prototypes will have Object as their constructor name,
843 // get the real DOM wrapper name from the constructor property.
844 var constructorName = obj.constructor && obj.constructor.name;
846 return constructorName;
855 _toString: function(obj)
857 // We don't use String(obj) because inspectedWindow.String is undefined if owning frame navigated to another page.
863 * @type {InjectedScript}
866 var injectedScript = new InjectedScript();
871 * @param {string=} objectGroupName
872 * @param {boolean=} forceValueType
873 * @param {boolean=} generatePreview
874 * @param {?Array.<string>=} columnNames
876 InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, generatePreview, columnNames)
878 this.type = typeof object;
879 if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
880 // We don't send undefined values over JSON.
881 if (typeof object !== "undefined")
884 // Null object is object with 'null' subtype'
886 this.subtype = "null";
888 // Provide user-friendly number values.
889 if (typeof object === "number")
890 this.description = object + "";
894 object = /** @type {Object} */ (object);
896 this.objectId = injectedScript._bind(object, objectGroupName);
897 var subtype = injectedScript._subtype(object);
899 this.subtype = subtype;
900 this.className = InjectedScriptHost.internalConstructorName(object);
901 this.description = injectedScript._describe(object);
903 if (generatePreview && (this.type === "object" || injectedScript._isHTMLAllCollection(object)))
904 this.preview = this._generatePreview(object, undefined, columnNames);
907 InjectedScript.RemoteObject.prototype = {
909 * @param {Object} object
910 * @param {Array.<string>=} firstLevelKeys
911 * @param {?Array.<string>=} secondLevelKeys
912 * @return {Object} preview
914 _generatePreview: function(object, firstLevelKeys, secondLevelKeys)
917 preview.lossless = true;
918 preview.overflow = false;
919 preview.properties = [];
921 var isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
922 var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
924 var propertiesThreshold = {
925 properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount),
926 indexes: isTableRowsRequest ? 1000 : Math.max(100, firstLevelKeysCount)
928 for (var o = object; injectedScript._isDefined(o); o = o.__proto__)
929 this._generateProtoPreview(o, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys);
934 * @param {Object} object
935 * @param {Object} preview
936 * @param {Object} propertiesThreshold
937 * @param {Array.<string>=} firstLevelKeys
938 * @param {Array.<string>=} secondLevelKeys
940 _generateProtoPreview: function(object, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys)
942 var propertyNames = firstLevelKeys ? firstLevelKeys : Object.keys(/** @type {!Object} */(object));
944 for (var i = 0; i < propertyNames.length; ++i) {
945 if (!propertiesThreshold.properties || !propertiesThreshold.indexes) {
946 preview.overflow = true;
947 preview.lossless = false;
950 var name = propertyNames[i];
951 if (this.subtype === "array" && name === "length")
954 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */(object), name);
955 if (!("value" in descriptor) || !descriptor.enumerable) {
956 preview.lossless = false;
960 var value = descriptor.value;
961 if (value === null) {
962 this._appendPropertyPreview(preview, { name: name, type: "object", value: "null" }, propertiesThreshold);
966 const maxLength = 100;
967 var type = typeof value;
969 if (InjectedScript.primitiveTypes[type]) {
970 if (type === "string") {
971 if (value.length > maxLength) {
972 value = this._abbreviateString(value, maxLength, true);
973 preview.lossless = false;
975 value = value.replace(/\n/g, "\u21B5");
977 this._appendPropertyPreview(preview, { name: name, type: type, value: value + "" }, propertiesThreshold);
981 if (secondLevelKeys === null || secondLevelKeys) {
982 var subPreview = this._generatePreview(value, secondLevelKeys || undefined);
983 var property = { name: name, type: type, valuePreview: subPreview };
984 this._appendPropertyPreview(preview, property, propertiesThreshold);
985 if (!subPreview.lossless)
986 preview.lossless = false;
987 if (subPreview.overflow)
988 preview.overflow = true;
992 preview.lossless = false;
994 var subtype = injectedScript._subtype(value);
995 var description = "";
996 if (type !== "function")
997 description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp");
999 var property = { name: name, type: type, value: description };
1001 property.subtype = subtype;
1002 this._appendPropertyPreview(preview, property, propertiesThreshold);
1009 * @param {Object} preview
1010 * @param {Object} property
1011 * @param {Object} propertiesThreshold
1013 _appendPropertyPreview: function(preview, property, propertiesThreshold)
1015 if (isNaN(property.name))
1016 propertiesThreshold.properties--;
1018 propertiesThreshold.indexes--;
1019 preview.properties.push(property);
1023 * @param {string} string
1024 * @param {number} maxLength
1025 * @param {boolean=} middle
1028 _abbreviateString: function(string, maxLength, middle)
1030 if (string.length <= maxLength)
1033 var leftHalf = maxLength >> 1;
1034 var rightHalf = maxLength - leftHalf - 1;
1035 return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
1037 return string.substr(0, maxLength) + "\u2026";
1042 * @param {number} ordinal
1043 * @param {Object} callFrame
1045 InjectedScript.CallFrameProxy = function(ordinal, callFrame)
1047 this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + injectedScriptId + "}";
1048 this.functionName = (callFrame.type === "function" ? callFrame.functionName : "");
1049 this.location = { scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column };
1050 this.scopeChain = this._wrapScopeChain(callFrame);
1051 this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace");
1054 InjectedScript.CallFrameProxy.prototype = {
1056 * @param {Object} callFrame
1057 * @return {!Array.<DebuggerAgent.Scope>}
1059 _wrapScopeChain: function(callFrame)
1061 var scopeChain = callFrame.scopeChain;
1062 var scopeChainProxy = [];
1063 for (var i = 0; i < scopeChain.length; i++) {
1064 var scope = InjectedScript.CallFrameProxy._createScopeJson(callFrame.scopeType(i), scopeChain[i], "backtrace");
1065 scopeChainProxy.push(scope);
1067 return scopeChainProxy;
1072 * @param {number} scopeTypeCode
1073 * @param {*} scopeObject
1074 * @param {string} groupId
1075 * @return {!DebuggerAgent.Scope}
1077 InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeObject, groupId) {
1078 const GLOBAL_SCOPE = 0;
1079 const LOCAL_SCOPE = 1;
1080 const WITH_SCOPE = 2;
1081 const CLOSURE_SCOPE = 3;
1082 const CATCH_SCOPE = 4;
1084 /** @type {!Object.<number, string>} */
1085 var scopeTypeNames = {};
1086 scopeTypeNames[GLOBAL_SCOPE] = "global";
1087 scopeTypeNames[LOCAL_SCOPE] = "local";
1088 scopeTypeNames[WITH_SCOPE] = "with";
1089 scopeTypeNames[CLOSURE_SCOPE] = "closure";
1090 scopeTypeNames[CATCH_SCOPE] = "catch";
1093 object: injectedScript._wrapObject(scopeObject, groupId),
1094 type: /** @type {DebuggerAgent.ScopeType} */ (scopeTypeNames[scopeTypeCode])
1100 * @param {CommandLineAPIImpl} commandLineAPIImpl
1101 * @param {Object} callFrame
1103 function CommandLineAPI(commandLineAPIImpl, callFrame)
1106 * @param {string} member
1109 function inScopeVariables(member)
1114 var scopeChain = callFrame.scopeChain;
1115 for (var i = 0; i < scopeChain.length; ++i) {
1116 if (member in scopeChain[i])
1123 * @param {string} name The name of the method for which a toString method should be generated.
1124 * @return {function():string}
1126 function customToStringMethod(name)
1128 return function () { return "function " + name + "() { [Command Line API] }"; };
1131 for (var i = 0; i < CommandLineAPI.members_.length; ++i) {
1132 var member = CommandLineAPI.members_[i];
1133 if (member in inspectedWindow || inScopeVariables(member))
1136 this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
1137 this[member].toString = customToStringMethod(member);
1140 for (var i = 0; i < 5; ++i) {
1141 var member = "$" + i;
1142 if (member in inspectedWindow || inScopeVariables(member))
1145 this.__defineGetter__("$" + i, bind(commandLineAPIImpl._inspectedObject, commandLineAPIImpl, i));
1148 this.$_ = injectedScript._lastResult;
1151 // NOTE: Please keep the list of API methods below snchronized to that in WebInspector.RuntimeModel!
1153 * @type {Array.<string>}
1156 CommandLineAPI.members_ = [
1157 "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd",
1158 "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners"
1164 function CommandLineAPIImpl()
1168 CommandLineAPIImpl.prototype = {
1170 * @param {string} selector
1171 * @param {Node=} start
1173 $: function (selector, start)
1175 if (this._canQuerySelectorOnNode(start))
1176 return start.querySelector(selector);
1178 var result = inspectedWindow.document.querySelector(selector);
1181 if (selector && selector[0] !== "#") {
1182 result = inspectedWindow.document.getElementById(selector);
1184 inspectedWindow.console.warn("The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $(\"#%s\")", selector );
1192 * @param {string} selector
1193 * @param {Node=} start
1195 $$: function (selector, start)
1197 if (this._canQuerySelectorOnNode(start))
1198 return start.querySelectorAll(selector);
1199 return inspectedWindow.document.querySelectorAll(selector);
1203 * @param {Node=} node
1206 _canQuerySelectorOnNode: function(node)
1208 return !!node && InjectedScriptHost.type(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
1212 * @param {string} xpath
1213 * @param {Node=} context
1215 $x: function(xpath, context)
1217 var doc = (context && context.ownerDocument) || inspectedWindow.document;
1218 var result = doc.evaluate(xpath, context || doc, null, XPathResult.ANY_TYPE, null);
1219 switch (result.resultType) {
1220 case XPathResult.NUMBER_TYPE:
1221 return result.numberValue;
1222 case XPathResult.STRING_TYPE:
1223 return result.stringValue;
1224 case XPathResult.BOOLEAN_TYPE:
1225 return result.booleanValue;
1229 while (node = result.iterateNext())
1237 return inspectedWindow.console.dir.apply(inspectedWindow.console, arguments)
1242 return inspectedWindow.console.dirxml.apply(inspectedWindow.console, arguments)
1245 keys: function(object)
1247 return Object.keys(object);
1250 values: function(object)
1253 for (var key in object)
1254 result.push(object[key]);
1260 return inspectedWindow.console.profile.apply(inspectedWindow.console, arguments)
1263 profileEnd: function()
1265 return inspectedWindow.console.profileEnd.apply(inspectedWindow.console, arguments)
1269 * @param {Object} object
1270 * @param {Array.<string>|string=} types
1272 monitorEvents: function(object, types)
1274 if (!object || !object.addEventListener || !object.removeEventListener)
1276 types = this._normalizeEventTypes(types);
1277 for (var i = 0; i < types.length; ++i) {
1278 object.removeEventListener(types[i], this._logEvent, false);
1279 object.addEventListener(types[i], this._logEvent, false);
1284 * @param {Object} object
1285 * @param {Array.<string>|string=} types
1287 unmonitorEvents: function(object, types)
1289 if (!object || !object.addEventListener || !object.removeEventListener)
1291 types = this._normalizeEventTypes(types);
1292 for (var i = 0; i < types.length; ++i)
1293 object.removeEventListener(types[i], this._logEvent, false);
1300 inspect: function(object)
1302 return injectedScript._inspect(object);
1305 copy: function(object)
1307 if (injectedScript._subtype(object) === "node")
1308 object = object.outerHTML;
1309 InjectedScriptHost.copyText(object);
1314 InjectedScriptHost.clearConsoleMessages();
1318 * @param {Node} node
1320 getEventListeners: function(node)
1322 return InjectedScriptHost.getEventListeners(node);
1326 * @param {number} num
1328 _inspectedObject: function(num)
1330 return InjectedScriptHost.inspectedObject(num);
1334 * @param {Array.<string>|string=} types
1335 * @return {Array.<string>}
1337 _normalizeEventTypes: function(types)
1339 if (typeof types === "undefined")
1340 types = [ "mouse", "key", "touch", "control", "load", "unload", "abort", "error", "select", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation" ];
1341 else if (typeof types === "string")
1345 for (var i = 0; i < types.length; i++) {
1346 if (types[i] === "mouse")
1347 result.splice(0, 0, "mousedown", "mouseup", "click", "dblclick", "mousemove", "mouseover", "mouseout", "mousewheel");
1348 else if (types[i] === "key")
1349 result.splice(0, 0, "keydown", "keyup", "keypress", "textInput");
1350 else if (types[i] === "touch")
1351 result.splice(0, 0, "touchstart", "touchmove", "touchend", "touchcancel");
1352 else if (types[i] === "control")
1353 result.splice(0, 0, "resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset");
1355 result.push(types[i]);
1361 * @param {Event} event
1363 _logEvent: function(event)
1365 inspectedWindow.console.log(event.type, event);
1369 injectedScript._commandLineAPIImpl = new CommandLineAPIImpl();
1370 return injectedScript;