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._lastBoundObjectId = 1;
81 this._idToWrappedObject = {};
82 this._idToObjectGroupName = {};
83 this._objectGroups = {};
88 * @type {Object.<string, boolean>}
91 InjectedScript.primitiveTypes = {
98 InjectedScript.prototype = {
103 isPrimitiveValue: function(object)
105 // FIXME(33716): typeof document.all is always 'undefined'.
106 return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
111 * @param {string} groupName
112 * @param {boolean} canAccessInspectedWindow
113 * @param {boolean} generatePreview
114 * @return {!RuntimeAgent.RemoteObject}
116 wrapObject: function(object, groupName, canAccessInspectedWindow, generatePreview)
118 if (canAccessInspectedWindow)
119 return this._wrapObject(object, groupName, false, generatePreview);
120 return this._fallbackWrapper(object);
125 * @return {!RuntimeAgent.RemoteObject}
127 _fallbackWrapper: function(object)
130 result.type = typeof object;
131 if (this.isPrimitiveValue(object))
132 result.value = object;
134 result.description = this._toString(object);
135 return /** @type {!RuntimeAgent.RemoteObject} */ (result);
139 * @param {boolean} canAccessInspectedWindow
140 * @param {Object} table
141 * @param {Array.<string>|string|boolean} columns
142 * @return {!RuntimeAgent.RemoteObject}
144 wrapTable: function(canAccessInspectedWindow, table, columns)
146 if (!canAccessInspectedWindow)
147 return this._fallbackWrapper(table);
148 var columnNames = null;
149 if (typeof columns === "string")
151 if (InjectedScriptHost.type(columns) == "array") {
153 for (var i = 0; i < columns.length; ++i)
154 columnNames.push(String(columns[i]));
156 return this._wrapObject(table, "console", false, true, columnNames);
162 inspectNode: function(object)
164 this._inspect(object);
171 _inspect: function(object)
173 if (arguments.length === 0)
176 var objectId = this._wrapObject(object, "");
179 switch (injectedScript._describe(object)) {
181 var databaseId = InjectedScriptHost.databaseId(object)
183 hints.databaseId = databaseId;
186 var storageId = InjectedScriptHost.storageId(object)
188 hints.domStorageId = InjectedScriptHost.evaluate("(" + storageId + ")");
191 InjectedScriptHost.inspect(objectId, hints);
196 * This method cannot throw.
198 * @param {string=} objectGroupName
199 * @param {boolean=} forceValueType
200 * @param {boolean=} generatePreview
201 * @param {?Array.<string>=} columnNames
202 * @return {!RuntimeAgent.RemoteObject}
203 * @suppress {checkTypes}
205 _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames)
208 return new InjectedScript.RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
211 var description = injectedScript._describe(e);
213 var description = "<failed to convert exception to string>";
215 return new InjectedScript.RemoteObject(description);
221 * @param {string=} objectGroupName
224 _bind: function(object, objectGroupName)
226 var id = this._lastBoundObjectId++;
227 this._idToWrappedObject[id] = object;
228 var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}";
229 if (objectGroupName) {
230 var group = this._objectGroups[objectGroupName];
233 this._objectGroups[objectGroupName] = group;
236 this._idToObjectGroupName[id] = objectGroupName;
242 * @param {string} objectId
245 _parseObjectId: function(objectId)
247 return InjectedScriptHost.evaluate("(" + objectId + ")");
251 * @param {string} objectGroupName
253 releaseObjectGroup: function(objectGroupName)
255 var group = this._objectGroups[objectGroupName];
258 for (var i = 0; i < group.length; i++)
259 this._releaseObject(group[i]);
260 delete this._objectGroups[objectGroupName];
264 * @param {string} methodName
265 * @param {string} args
268 dispatch: function(methodName, args)
270 var argsArray = InjectedScriptHost.evaluate("(" + args + ")");
271 var result = this[methodName].apply(this, argsArray);
272 if (typeof result === "undefined") {
273 inspectedWindow.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
280 * @param {string} objectId
281 * @param {boolean} ownProperties
282 * @return {Array.<RuntimeAgent.PropertyDescriptor>|boolean}
284 getProperties: function(objectId, ownProperties)
286 var parsedObjectId = this._parseObjectId(objectId);
287 var object = this._objectForId(parsedObjectId);
288 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
290 if (!this._isDefined(object))
292 var descriptors = this._propertyDescriptors(object, ownProperties);
294 // Go over properties, wrap object values.
295 for (var i = 0; i < descriptors.length; ++i) {
296 var descriptor = descriptors[i];
297 if ("get" in descriptor)
298 descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
299 if ("set" in descriptor)
300 descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
301 if ("value" in descriptor)
302 descriptor.value = this._wrapObject(descriptor.value, objectGroupName);
303 if (!("configurable" in descriptor))
304 descriptor.configurable = false;
305 if (!("enumerable" in descriptor))
306 descriptor.enumerable = false;
312 * @param {string} objectId
313 * @return {Array.<Object>|boolean}
315 getInternalProperties: function(objectId, ownProperties)
317 var parsedObjectId = this._parseObjectId(objectId);
318 var object = this._objectForId(parsedObjectId);
319 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
320 if (!this._isDefined(object))
322 var descriptors = [];
323 var internalProperties = InjectedScriptHost.getInternalProperties(object);
324 if (internalProperties) {
325 for (var i = 0; i < internalProperties.length; i++) {
326 var property = internalProperties[i];
329 value: this._wrapObject(property.value, objectGroupName)
331 descriptors.push(descriptor);
338 * @param {string} functionId
339 * @return {!DebuggerAgent.FunctionDetails|string}
341 getFunctionDetails: function(functionId)
343 var parsedFunctionId = this._parseObjectId(functionId);
344 var func = this._objectForId(parsedFunctionId);
345 if (typeof func !== "function")
346 return "Cannot resolve function by id.";
347 var details = InjectedScriptHost.functionDetails(func);
348 if ("rawScopes" in details) {
349 var objectGroupName = this._idToObjectGroupName[parsedFunctionId.id];
350 var rawScopes = details.rawScopes;
352 delete details.rawScopes;
353 for (var i = 0; i < rawScopes.length; i++)
354 scopes.push(InjectedScript.CallFrameProxy._createScopeJson(rawScopes[i].type, rawScopes[i].object, objectGroupName));
355 details.scopeChain = scopes;
361 * @param {string} objectId
363 releaseObject: function(objectId)
365 var parsedObjectId = this._parseObjectId(objectId);
366 this._releaseObject(parsedObjectId.id);
372 _releaseObject: function(id)
374 delete this._idToWrappedObject[id];
375 delete this._idToObjectGroupName[id];
379 * @param {Object} object
380 * @param {boolean} ownProperties
381 * @return {Array.<Object>}
383 _propertyDescriptors: function(object, ownProperties)
385 var descriptors = [];
386 var nameProcessed = {};
387 nameProcessed["__proto__"] = null;
388 for (var o = object; this._isDefined(o); o = o.__proto__) {
389 var names = Object.getOwnPropertyNames(/** @type {!Object} */ (o));
390 for (var i = 0; i < names.length; ++i) {
392 if (nameProcessed[name])
396 nameProcessed[name] = true;
397 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */ (object), name);
399 // Not all bindings provide proper descriptors. Fall back to the writable, configurable property.
401 descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false};
403 descriptor.isOwn = true;
404 descriptors.push(descriptor);
412 descriptor.value = e;
413 descriptor.wasThrown = true;
416 descriptor.name = name;
418 descriptor.isOwn = true;
419 descriptors.push(descriptor);
422 if (object.__proto__)
423 descriptors.push({ name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true});
431 * @param {string} expression
432 * @param {string} objectGroup
433 * @param {boolean} injectCommandLineAPI
434 * @param {boolean} returnByValue
435 * @param {boolean} generatePreview
438 evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
440 return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview);
444 * @param {string} objectId
445 * @param {string} expression
446 * @param {boolean} returnByValue
447 * @return {Object|string}
449 callFunctionOn: function(objectId, expression, args, returnByValue)
451 var parsedObjectId = this._parseObjectId(objectId);
452 var object = this._objectForId(parsedObjectId);
453 if (!this._isDefined(object))
454 return "Could not find object with given id";
457 var resolvedArgs = [];
458 args = InjectedScriptHost.evaluate(args);
459 for (var i = 0; i < args.length; ++i) {
460 var resolvedCallArgument;
462 resolvedCallArgument = this._resolveCallArgument(args[i]);
466 resolvedArgs.push(resolvedCallArgument)
471 var objectGroup = this._idToObjectGroupName[parsedObjectId.id];
472 var func = InjectedScriptHost.evaluate("(" + expression + ")");
473 if (typeof func !== "function")
474 return "Given expression does not evaluate to a function";
476 return { wasThrown: false,
477 result: this._wrapObject(func.apply(object, resolvedArgs), objectGroup, returnByValue) };
479 return this._createThrownValue(e, objectGroup);
484 * Resolves a value from CallArgument description.
485 * @param {RuntimeAgent.CallArgument} callArgumentJson
486 * @return {*} resolved value
487 * @throw {string} error message
489 _resolveCallArgument: function(callArgumentJson) {
490 var objectId = callArgumentJson.objectId;
492 var parsedArgId = this._parseObjectId(objectId);
493 if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
494 throw "Arguments should belong to the same JavaScript world as the target object.";
496 var resolvedArg = this._objectForId(parsedArgId);
497 if (!this._isDefined(resolvedArg))
498 throw "Could not find object with given id";
501 } else if ("value" in callArgumentJson)
502 return callArgumentJson.value;
508 * @param {Function} evalFunction
509 * @param {Object} object
510 * @param {string} objectGroup
511 * @param {boolean} isEvalOnCallFrame
512 * @param {boolean} injectCommandLineAPI
513 * @param {boolean} returnByValue
514 * @param {boolean} generatePreview
517 _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview)
520 return { wasThrown: false,
521 result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI), objectGroup, returnByValue, generatePreview) };
523 return this._createThrownValue(e, objectGroup);
529 * @param {string} objectGroup
532 _createThrownValue: function(value, objectGroup)
534 var remoteObject = this._wrapObject(value, objectGroup);
536 remoteObject.description = this._toString(value);
538 return { wasThrown: true,
539 result: remoteObject };
543 * @param {Function} evalFunction
544 * @param {Object} object
545 * @param {string} objectGroup
546 * @param {string} expression
547 * @param {boolean} isEvalOnCallFrame
548 * @param {boolean} injectCommandLineAPI
551 _evaluateOn: function(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI)
553 // Only install command line api object for the time of evaluation.
554 // Surround the expression in with statements to inject our command line API so that
555 // the window object properties still take more precedent than our API functions.
558 if (injectCommandLineAPI && inspectedWindow.console) {
559 inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
560 expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
562 var result = evalFunction.call(object, expression);
563 if (objectGroup === "console")
564 this._lastResult = result;
567 if (injectCommandLineAPI && inspectedWindow.console)
568 delete inspectedWindow.console._commandLineAPI;
573 * @param {Object} callFrame
574 * @return {Array.<InjectedScript.CallFrameProxy>|boolean}
576 wrapCallFrames: function(callFrame)
584 result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
585 callFrame = callFrame.caller;
591 * @param {Object} topCallFrame
592 * @param {string} callFrameId
593 * @param {string} expression
594 * @param {string} objectGroup
595 * @param {boolean} injectCommandLineAPI
596 * @param {boolean} returnByValue
597 * @param {boolean} generatePreview
600 evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
602 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
604 return "Could not find call frame with given id";
605 return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview);
609 * @param {Object} topCallFrame
610 * @param {string} callFrameId
613 restartFrame: function(topCallFrame, callFrameId)
615 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
617 return "Could not find call frame with given id";
618 var result = callFrame.restart();
619 if (result === false)
620 result = "Restart frame is not supported";
625 * Either callFrameId or functionObjectId must be specified.
626 * @param {Object} topCallFrame
627 * @param {string|boolean} callFrameId or false
628 * @param {string|boolean} functionObjectId or false
629 * @param {number} scopeNumber
630 * @param {string} variableName
631 * @param {string} newValueJsonString RuntimeAgent.CallArgument structure serialized as string
632 * @return {string|undefined} undefined if success or an error message
634 setVariableValue: function(topCallFrame, callFrameId, functionObjectId, scopeNumber, variableName, newValueJsonString)
637 if (typeof callFrameId === "string") {
638 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
640 return "Could not find call frame with given id";
641 setter = callFrame.setVariableValue.bind(callFrame);
643 var parsedFunctionId = this._parseObjectId(/** @type {string} */(functionObjectId));
644 var func = this._objectForId(parsedFunctionId);
645 if (typeof func !== "function")
646 return "Cannot resolve function by id.";
647 setter = InjectedScriptHost.setFunctionVariableValue.bind(InjectedScriptHost, func);
651 newValueJson = InjectedScriptHost.evaluate("(" + newValueJsonString + ")");
653 return "Failed to parse new value JSON " + newValueJsonString + " : " + e;
657 resolvedValue = this._resolveCallArgument(newValueJson);
662 setter(scopeNumber, variableName, resolvedValue);
664 return "Failed to change variable value: " + e;
670 * @param {Object} topCallFrame
671 * @param {string} callFrameId
674 _callFrameForId: function(topCallFrame, callFrameId)
676 var parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
677 var ordinal = parsedCallFrameId["ordinal"];
678 var callFrame = topCallFrame;
679 while (--ordinal >= 0 && callFrame)
680 callFrame = callFrame.caller;
685 * @param {Object} objectId
688 _objectForId: function(objectId)
690 return this._idToWrappedObject[objectId.id];
694 * @param {string} objectId
697 findObjectById: function(objectId)
699 var parsedObjectId = this._parseObjectId(objectId);
700 return this._objectForId(parsedObjectId);
704 * @param {string} objectId
707 nodeForObjectId: function(objectId)
709 var object = this.findObjectById(objectId);
710 if (!object || this._subtype(object) !== "node")
712 return /** @type {Node} */ (object);
716 * @param {string} name
719 module: function(name)
721 return this._modules[name];
725 * @param {string} name
726 * @param {string} source
729 injectModule: function(name, source)
731 delete this._modules[name];
732 var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
733 if (typeof moduleFunction !== "function") {
734 inspectedWindow.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
737 var module = moduleFunction.call(inspectedWindow, InjectedScriptHost, inspectedWindow, injectedScriptId);
738 this._modules[name] = module;
746 _isDefined: function(object)
748 return !!object || this._isHTMLAllCollection(object);
755 _isHTMLAllCollection: function(object)
757 // document.all is reported as undefined, but we still want to process it.
758 return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCollection(object);
762 * @param {Object=} obj
765 _subtype: function(obj)
770 if (this.isPrimitiveValue(obj))
773 if (this._isHTMLAllCollection(obj))
776 var preciseType = InjectedScriptHost.type(obj);
780 // FireBug's array detection.
782 if (typeof obj.splice === "function" && isFinite(obj.length))
784 if (Object.prototype.toString.call(obj) === "[object Arguments]" && isFinite(obj.length)) // arguments.
789 // If owning frame has navigated to somewhere else window properties will be undefined.
797 _describe: function(obj)
799 if (this.isPrimitiveValue(obj))
802 obj = /** @type {Object} */ (obj);
804 // Type is object, get subtype.
805 var subtype = this._subtype(obj);
807 if (subtype === "regexp")
808 return this._toString(obj);
810 if (subtype === "date")
811 return this._toString(obj);
813 if (subtype === "node") {
814 var description = obj.nodeName.toLowerCase();
815 switch (obj.nodeType) {
816 case 1 /* Node.ELEMENT_NODE */:
817 description += obj.id ? "#" + obj.id : "";
818 var className = obj.className;
819 description += className ? "." + className : "";
821 case 10 /*Node.DOCUMENT_TYPE_NODE */:
822 description = "<!DOCTYPE " + description + ">";
828 var className = InjectedScriptHost.internalConstructorName(obj);
829 if (subtype === "array") {
830 if (typeof obj.length === "number")
831 className += "[" + obj.length + "]";
835 // NodeList in JSC is a function, check for array prior to this.
836 if (typeof obj === "function")
837 return this._toString(obj);
839 if (className === "Object") {
840 // In Chromium DOM wrapper prototypes will have Object as their constructor name,
841 // get the real DOM wrapper name from the constructor property.
842 var constructorName = obj.constructor && obj.constructor.name;
844 return constructorName;
853 _toString: function(obj)
855 // We don't use String(obj) because inspectedWindow.String is undefined if owning frame navigated to another page.
861 * @type {InjectedScript}
864 var injectedScript = new InjectedScript();
869 * @param {string=} objectGroupName
870 * @param {boolean=} forceValueType
871 * @param {boolean=} generatePreview
872 * @param {?Array.<string>=} columnNames
874 InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, generatePreview, columnNames)
876 this.type = typeof object;
877 if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
878 // We don't send undefined values over JSON.
879 if (typeof object !== "undefined")
882 // Null object is object with 'null' subtype'
884 this.subtype = "null";
886 // Provide user-friendly number values.
887 if (typeof object === "number")
888 this.description = object + "";
892 object = /** @type {Object} */ (object);
894 this.objectId = injectedScript._bind(object, objectGroupName);
895 var subtype = injectedScript._subtype(object);
897 this.subtype = subtype;
898 this.className = InjectedScriptHost.internalConstructorName(object);
899 this.description = injectedScript._describe(object);
901 if (generatePreview && (this.type === "object" || injectedScript._isHTMLAllCollection(object)))
902 this.preview = this._generatePreview(object, undefined, columnNames);
905 InjectedScript.RemoteObject.prototype = {
907 * @param {Object} object
908 * @param {Array.<string>=} firstLevelKeys
909 * @param {?Array.<string>=} secondLevelKeys
910 * @return {Object} preview
912 _generatePreview: function(object, firstLevelKeys, secondLevelKeys)
915 preview.lossless = true;
916 preview.overflow = false;
917 preview.properties = [];
919 var isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
920 var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
922 var propertiesThreshold = {
923 properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount),
924 indexes: isTableRowsRequest ? 1000 : Math.max(100, firstLevelKeysCount)
926 for (var o = object; injectedScript._isDefined(o); o = o.__proto__)
927 this._generateProtoPreview(o, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys);
932 * @param {Object} object
933 * @param {Object} preview
934 * @param {Object} propertiesThreshold
935 * @param {Array.<string>=} firstLevelKeys
936 * @param {Array.<string>=} secondLevelKeys
938 _generateProtoPreview: function(object, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys)
940 var propertyNames = firstLevelKeys ? firstLevelKeys : Object.keys(/** @type {!Object} */(object));
942 for (var i = 0; i < propertyNames.length; ++i) {
943 if (!propertiesThreshold.properties || !propertiesThreshold.indexes) {
944 preview.overflow = true;
945 preview.lossless = false;
948 var name = propertyNames[i];
949 if (this.subtype === "array" && name === "length")
952 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */(object), name);
953 if (!("value" in descriptor) || !descriptor.enumerable) {
954 preview.lossless = false;
958 var value = descriptor.value;
959 if (value === null) {
960 this._appendPropertyPreview(preview, { name: name, type: "object", value: "null" }, propertiesThreshold);
964 const maxLength = 100;
965 var type = typeof value;
967 if (InjectedScript.primitiveTypes[type]) {
968 if (type === "string") {
969 if (value.length > maxLength) {
970 value = this._abbreviateString(value, maxLength, true);
971 preview.lossless = false;
973 value = value.replace(/\n/g, "\u21B5");
975 this._appendPropertyPreview(preview, { name: name, type: type, value: value + "" }, propertiesThreshold);
979 if (secondLevelKeys === null || secondLevelKeys) {
980 var subPreview = this._generatePreview(value, secondLevelKeys || undefined);
981 var property = { name: name, type: type, valuePreview: subPreview };
982 this._appendPropertyPreview(preview, property, propertiesThreshold);
983 if (!subPreview.lossless)
984 preview.lossless = false;
985 if (subPreview.overflow)
986 preview.overflow = true;
990 preview.lossless = false;
992 var subtype = injectedScript._subtype(value);
993 var description = "";
994 if (type !== "function")
995 description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp");
997 var property = { name: name, type: type, value: description };
999 property.subtype = subtype;
1000 this._appendPropertyPreview(preview, property, propertiesThreshold);
1007 * @param {Object} preview
1008 * @param {Object} property
1009 * @param {Object} propertiesThreshold
1011 _appendPropertyPreview: function(preview, property, propertiesThreshold)
1013 if (isNaN(property.name))
1014 propertiesThreshold.properties--;
1016 propertiesThreshold.indexes--;
1017 preview.properties.push(property);
1021 * @param {string} string
1022 * @param {number} maxLength
1023 * @param {boolean=} middle
1026 _abbreviateString: function(string, maxLength, middle)
1028 if (string.length <= maxLength)
1031 var leftHalf = maxLength >> 1;
1032 var rightHalf = maxLength - leftHalf - 1;
1033 return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
1035 return string.substr(0, maxLength) + "\u2026";
1040 * @param {number} ordinal
1041 * @param {Object} callFrame
1043 InjectedScript.CallFrameProxy = function(ordinal, callFrame)
1045 this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + injectedScriptId + "}";
1046 this.functionName = (callFrame.type === "function" ? callFrame.functionName : "");
1047 this.location = { scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column };
1048 this.scopeChain = this._wrapScopeChain(callFrame);
1049 this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace");
1052 InjectedScript.CallFrameProxy.prototype = {
1054 * @param {Object} callFrame
1055 * @return {!Array.<DebuggerAgent.Scope>}
1057 _wrapScopeChain: function(callFrame)
1059 var scopeChain = callFrame.scopeChain;
1060 var scopeChainProxy = [];
1061 for (var i = 0; i < scopeChain.length; i++) {
1062 var scope = InjectedScript.CallFrameProxy._createScopeJson(callFrame.scopeType(i), scopeChain[i], "backtrace");
1063 scopeChainProxy.push(scope);
1065 return scopeChainProxy;
1070 * @param {number} scopeTypeCode
1071 * @param {*} scopeObject
1072 * @param {string} groupId
1073 * @return {!DebuggerAgent.Scope}
1075 InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeObject, groupId) {
1076 const GLOBAL_SCOPE = 0;
1077 const LOCAL_SCOPE = 1;
1078 const WITH_SCOPE = 2;
1079 const CLOSURE_SCOPE = 3;
1080 const CATCH_SCOPE = 4;
1082 /** @type {!Object.<number, string>} */
1083 var scopeTypeNames = {};
1084 scopeTypeNames[GLOBAL_SCOPE] = "global";
1085 scopeTypeNames[LOCAL_SCOPE] = "local";
1086 scopeTypeNames[WITH_SCOPE] = "with";
1087 scopeTypeNames[CLOSURE_SCOPE] = "closure";
1088 scopeTypeNames[CATCH_SCOPE] = "catch";
1091 object: injectedScript._wrapObject(scopeObject, groupId),
1092 type: /** @type {DebuggerAgent.ScopeType} */ (scopeTypeNames[scopeTypeCode])
1098 * @param {CommandLineAPIImpl} commandLineAPIImpl
1099 * @param {Object} callFrame
1101 function CommandLineAPI(commandLineAPIImpl, callFrame)
1104 * @param {string} member
1107 function inScopeVariables(member)
1112 var scopeChain = callFrame.scopeChain;
1113 for (var i = 0; i < scopeChain.length; ++i) {
1114 if (member in scopeChain[i])
1121 * @param {string} name The name of the method for which a toString method should be generated.
1122 * @return {function():string}
1124 function customToStringMethod(name)
1126 return function () { return "function " + name + "() { [Command Line API] }"; };
1129 for (var i = 0; i < CommandLineAPI.members_.length; ++i) {
1130 var member = CommandLineAPI.members_[i];
1131 if (member in inspectedWindow || inScopeVariables(member))
1134 this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
1135 this[member].toString = customToStringMethod(member);
1138 for (var i = 0; i < 5; ++i) {
1139 var member = "$" + i;
1140 if (member in inspectedWindow || inScopeVariables(member))
1143 this.__defineGetter__("$" + i, bind(commandLineAPIImpl._inspectedObject, commandLineAPIImpl, i));
1146 this.$_ = injectedScript._lastResult;
1149 // NOTE: Please keep the list of API methods below snchronized to that in WebInspector.RuntimeModel!
1151 * @type {Array.<string>}
1154 CommandLineAPI.members_ = [
1155 "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd",
1156 "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners"
1162 function CommandLineAPIImpl()
1166 CommandLineAPIImpl.prototype = {
1168 * @param {string} selector
1169 * @param {Node=} start
1171 $: function (selector, start)
1173 if (this._canQuerySelectorOnNode(start))
1174 return start.querySelector(selector);
1176 var result = inspectedWindow.document.querySelector(selector);
1179 if (selector && selector[0] !== "#") {
1180 result = inspectedWindow.document.getElementById(selector);
1182 inspectedWindow.console.warn("The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $(\"#%s\")", selector );
1190 * @param {string} selector
1191 * @param {Node=} start
1193 $$: function (selector, start)
1195 if (this._canQuerySelectorOnNode(start))
1196 return start.querySelectorAll(selector);
1197 return inspectedWindow.document.querySelectorAll(selector);
1201 * @param {Node=} node
1204 _canQuerySelectorOnNode: function(node)
1206 return !!node && InjectedScriptHost.type(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
1210 * @param {string} xpath
1211 * @param {Node=} context
1213 $x: function(xpath, context)
1215 var doc = (context && context.ownerDocument) || inspectedWindow.document;
1216 var result = doc.evaluate(xpath, context || doc, null, XPathResult.ANY_TYPE, null);
1217 switch (result.resultType) {
1218 case XPathResult.NUMBER_TYPE:
1219 return result.numberValue;
1220 case XPathResult.STRING_TYPE:
1221 return result.stringValue;
1222 case XPathResult.BOOLEAN_TYPE:
1223 return result.booleanValue;
1227 while (node = result.iterateNext())
1235 return inspectedWindow.console.dir.apply(inspectedWindow.console, arguments)
1240 return inspectedWindow.console.dirxml.apply(inspectedWindow.console, arguments)
1243 keys: function(object)
1245 return Object.keys(object);
1248 values: function(object)
1251 for (var key in object)
1252 result.push(object[key]);
1258 return inspectedWindow.console.profile.apply(inspectedWindow.console, arguments)
1261 profileEnd: function()
1263 return inspectedWindow.console.profileEnd.apply(inspectedWindow.console, arguments)
1267 * @param {Object} object
1268 * @param {Array.<string>|string=} types
1270 monitorEvents: function(object, types)
1272 if (!object || !object.addEventListener || !object.removeEventListener)
1274 types = this._normalizeEventTypes(types);
1275 for (var i = 0; i < types.length; ++i) {
1276 object.removeEventListener(types[i], this._logEvent, false);
1277 object.addEventListener(types[i], this._logEvent, false);
1282 * @param {Object} object
1283 * @param {Array.<string>|string=} types
1285 unmonitorEvents: function(object, types)
1287 if (!object || !object.addEventListener || !object.removeEventListener)
1289 types = this._normalizeEventTypes(types);
1290 for (var i = 0; i < types.length; ++i)
1291 object.removeEventListener(types[i], this._logEvent, false);
1298 inspect: function(object)
1300 return injectedScript._inspect(object);
1303 copy: function(object)
1305 if (injectedScript._subtype(object) === "node")
1306 object = object.outerHTML;
1307 InjectedScriptHost.copyText(object);
1312 InjectedScriptHost.clearConsoleMessages();
1316 * @param {Node} node
1318 getEventListeners: function(node)
1320 return InjectedScriptHost.getEventListeners(node);
1324 * @param {number} num
1326 _inspectedObject: function(num)
1328 return InjectedScriptHost.inspectedObject(num);
1332 * @param {Array.<string>|string=} types
1333 * @return {Array.<string>}
1335 _normalizeEventTypes: function(types)
1337 if (typeof types === "undefined")
1338 types = [ "mouse", "key", "touch", "control", "load", "unload", "abort", "error", "select", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation" ];
1339 else if (typeof types === "string")
1343 for (var i = 0; i < types.length; i++) {
1344 if (types[i] === "mouse")
1345 result.splice(0, 0, "mousedown", "mouseup", "click", "dblclick", "mousemove", "mouseover", "mouseout", "mousewheel");
1346 else if (types[i] === "key")
1347 result.splice(0, 0, "keydown", "keyup", "keypress", "textInput");
1348 else if (types[i] === "touch")
1349 result.splice(0, 0, "touchstart", "touchmove", "touchend", "touchcancel");
1350 else if (types[i] === "control")
1351 result.splice(0, 0, "resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset");
1353 result.push(types[i]);
1359 * @param {Event} event
1361 _logEvent: function(event)
1363 inspectedWindow.console.log(event.type, event);
1367 injectedScript._commandLineAPIImpl = new CommandLineAPIImpl();
1368 return injectedScript;