Home | History | Annotate | Download | only in inspector
      1 /*
      2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
      3  * Copyright (C) 2013 Google Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 /**
     31  * @param {InjectedScriptHost} InjectedScriptHost
     32  * @param {Window} inspectedWindow
     33  * @param {number} injectedScriptId
     34  */
     35 (function (InjectedScriptHost, inspectedWindow, injectedScriptId) {
     36 
     37 /**
     38  * Protect against Object overwritten by the user code.
     39  * @suppress {duplicate}
     40  */
     41 var Object = /** @type {function(new:Object, *=)} */ ({}.constructor);
     42 
     43 /**
     44  * @param {Arguments} array
     45  * @param {number=} index
     46  * @return {Array.<*>}
     47  */
     48 function slice(array, index)
     49 {
     50     var result = [];
     51     for (var i = index || 0; i < array.length; ++i)
     52         result.push(array[i]);
     53     return result;
     54 }
     55 
     56 /**
     57  * Please use this bind, not the one from Function.prototype
     58  * @param {function(...)} func
     59  * @param {Object} thisObject
     60  * @param {...number} var_args
     61  */
     62 function bind(func, thisObject, var_args)
     63 {
     64     var args = slice(arguments, 2);
     65 
     66     /**
     67      * @param {...number} var_args
     68      */
     69     function bound(var_args)
     70     {
     71         return func.apply(thisObject, args.concat(slice(arguments)));
     72     }
     73     bound.toString = function() {
     74         return "bound: " + func;
     75     };
     76     return bound;
     77 }
     78 
     79 /**
     80  * @constructor
     81  */
     82 var InjectedScript = function()
     83 {
     84     /** @type {number} */
     85     this._lastBoundObjectId = 1;
     86     /** @type {!Object.<number, Object>} */
     87     this._idToWrappedObject = {};
     88     /** @type {!Object.<number, string>} */
     89     this._idToObjectGroupName = {};
     90     /** @type {!Object.<string, Array.<number>>} */
     91     this._objectGroups = {};
     92     /** @type {!Object.<string, Object>} */
     93     this._modules = {};
     94 }
     95 
     96 /**
     97  * @type {Object.<string, boolean>}
     98  * @const
     99  */
    100 InjectedScript.primitiveTypes = {
    101     undefined: true,
    102     boolean: true,
    103     number: true,
    104     string: true
    105 }
    106 
    107 InjectedScript.prototype = {
    108     /**
    109      * @param {*} object
    110      * @return {boolean}
    111      */
    112     isPrimitiveValue: function(object)
    113     {
    114         // FIXME(33716): typeof document.all is always 'undefined'.
    115         return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
    116     },
    117 
    118     /**
    119      * @param {*} object
    120      * @param {string} groupName
    121      * @param {boolean} canAccessInspectedWindow
    122      * @param {boolean} generatePreview
    123      * @return {!RuntimeAgent.RemoteObject}
    124      */
    125     wrapObject: function(object, groupName, canAccessInspectedWindow, generatePreview)
    126     {
    127         if (canAccessInspectedWindow)
    128             return this._wrapObject(object, groupName, false, generatePreview);
    129         return this._fallbackWrapper(object);
    130     },
    131 
    132     /**
    133      * @param {*} object
    134      * @return {!RuntimeAgent.RemoteObject}
    135      */
    136     _fallbackWrapper: function(object)
    137     {
    138         var result = {};
    139         result.type = typeof object;
    140         if (this.isPrimitiveValue(object))
    141             result.value = object;
    142         else
    143             result.description = this._toString(object);
    144         return /** @type {!RuntimeAgent.RemoteObject} */ (result);
    145     },
    146 
    147     /**
    148      * @param {boolean} canAccessInspectedWindow
    149      * @param {Object} table
    150      * @param {Array.<string>|string|boolean} columns
    151      * @return {!RuntimeAgent.RemoteObject}
    152      */
    153     wrapTable: function(canAccessInspectedWindow, table, columns)
    154     {
    155         if (!canAccessInspectedWindow)
    156             return this._fallbackWrapper(table);
    157         var columnNames = null;
    158         if (typeof columns === "string")
    159             columns = [columns];
    160         if (InjectedScriptHost.type(columns) == "array") {
    161             columnNames = [];
    162             for (var i = 0; i < columns.length; ++i)
    163                 columnNames.push(String(columns[i]));
    164         }
    165         return this._wrapObject(table, "console", false, true, columnNames);
    166     },
    167 
    168     /**
    169      * @param {*} object
    170      */
    171     inspectNode: function(object)
    172     {
    173         this._inspect(object);
    174     },
    175 
    176     /**
    177      * @param {*} object
    178      * @return {*}
    179      */
    180     _inspect: function(object)
    181     {
    182         if (arguments.length === 0)
    183             return;
    184 
    185         var objectId = this._wrapObject(object, "");
    186         var hints = {};
    187 
    188         switch (injectedScript._describe(object)) {
    189             case "Database":
    190                 var databaseId = InjectedScriptHost.databaseId(object)
    191                 if (databaseId)
    192                     hints.databaseId = databaseId;
    193                 break;
    194             case "Storage":
    195                 var storageId = InjectedScriptHost.storageId(object)
    196                 if (storageId)
    197                     hints.domStorageId = InjectedScriptHost.evaluate("(" + storageId + ")");
    198                 break;
    199         }
    200         InjectedScriptHost.inspect(objectId, hints);
    201         return object;
    202     },
    203 
    204     /**
    205      * This method cannot throw.
    206      * @param {*} object
    207      * @param {string=} objectGroupName
    208      * @param {boolean=} forceValueType
    209      * @param {boolean=} generatePreview
    210      * @param {?Array.<string>=} columnNames
    211      * @return {!RuntimeAgent.RemoteObject}
    212      * @suppress {checkTypes}
    213      */
    214     _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames)
    215     {
    216         try {
    217             return new InjectedScript.RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
    218         } catch (e) {
    219             try {
    220                 var description = injectedScript._describe(e);
    221             } catch (ex) {
    222                 var description = "<failed to convert exception to string>";
    223             }
    224             return new InjectedScript.RemoteObject(description);
    225         }
    226     },
    227 
    228     /**
    229      * @param {Object} object
    230      * @param {string=} objectGroupName
    231      * @return {string}
    232      */
    233     _bind: function(object, objectGroupName)
    234     {
    235         var id = this._lastBoundObjectId++;
    236         this._idToWrappedObject[id] = object;
    237         var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}";
    238         if (objectGroupName) {
    239             var group = this._objectGroups[objectGroupName];
    240             if (!group) {
    241                 group = [];
    242                 this._objectGroups[objectGroupName] = group;
    243             }
    244             group.push(id);
    245             this._idToObjectGroupName[id] = objectGroupName;
    246         }
    247         return objectId;
    248     },
    249 
    250     /**
    251      * @param {string} objectId
    252      * @return {Object}
    253      */
    254     _parseObjectId: function(objectId)
    255     {
    256         return InjectedScriptHost.evaluate("(" + objectId + ")");
    257     },
    258 
    259     /**
    260      * @param {string} objectGroupName
    261      */
    262     releaseObjectGroup: function(objectGroupName)
    263     {
    264         var group = this._objectGroups[objectGroupName];
    265         if (!group)
    266             return;
    267         for (var i = 0; i < group.length; i++)
    268             this._releaseObject(group[i]);
    269         delete this._objectGroups[objectGroupName];
    270     },
    271 
    272     /**
    273      * @param {string} methodName
    274      * @param {string} args
    275      * @return {*}
    276      */
    277     dispatch: function(methodName, args)
    278     {
    279         var argsArray = InjectedScriptHost.evaluate("(" + args + ")");
    280         var result = this[methodName].apply(this, argsArray);
    281         if (typeof result === "undefined") {
    282             inspectedWindow.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
    283             result = null;
    284         }
    285         return result;
    286     },
    287 
    288     /**
    289      * @param {string} objectId
    290      * @param {boolean} ownProperties
    291      * @param {boolean} accessorPropertiesOnly
    292      * @return {Array.<RuntimeAgent.PropertyDescriptor>|boolean}
    293      */
    294     getProperties: function(objectId, ownProperties, accessorPropertiesOnly)
    295     {
    296         var parsedObjectId = this._parseObjectId(objectId);
    297         var object = this._objectForId(parsedObjectId);
    298         var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
    299 
    300         if (!this._isDefined(object))
    301             return false;
    302         var descriptors = this._propertyDescriptors(object, ownProperties, accessorPropertiesOnly);
    303 
    304         // Go over properties, wrap object values.
    305         for (var i = 0; i < descriptors.length; ++i) {
    306             var descriptor = descriptors[i];
    307             if ("get" in descriptor)
    308                 descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
    309             if ("set" in descriptor)
    310                 descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
    311             if ("value" in descriptor)
    312                 descriptor.value = this._wrapObject(descriptor.value, objectGroupName);
    313             if (!("configurable" in descriptor))
    314                 descriptor.configurable = false;
    315             if (!("enumerable" in descriptor))
    316                 descriptor.enumerable = false;
    317         }
    318         return descriptors;
    319     },
    320 
    321     /**
    322      * @param {string} objectId
    323      * @return {Array.<Object>|boolean}
    324      */
    325     getInternalProperties: function(objectId, ownProperties)
    326     {
    327         var parsedObjectId = this._parseObjectId(objectId);
    328         var object = this._objectForId(parsedObjectId);
    329         var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
    330         if (!this._isDefined(object))
    331             return false;
    332         var descriptors = [];
    333         var internalProperties = InjectedScriptHost.getInternalProperties(object);
    334         if (internalProperties) {
    335             for (var i = 0; i < internalProperties.length; i++) {
    336                 var property = internalProperties[i];
    337                 var descriptor = {
    338                     name: property.name,
    339                     value: this._wrapObject(property.value, objectGroupName)
    340                 };
    341                 descriptors.push(descriptor);
    342             }
    343         }
    344         return descriptors;
    345     },
    346 
    347     /**
    348      * @param {string} functionId
    349      * @return {!DebuggerAgent.FunctionDetails|string}
    350      */
    351     getFunctionDetails: function(functionId)
    352     {
    353         var parsedFunctionId = this._parseObjectId(functionId);
    354         var func = this._objectForId(parsedFunctionId);
    355         if (typeof func !== "function")
    356             return "Cannot resolve function by id.";
    357         var details = InjectedScriptHost.functionDetails(func);
    358         if ("rawScopes" in details) {
    359             var objectGroupName = this._idToObjectGroupName[parsedFunctionId.id];
    360             var rawScopes = details.rawScopes;
    361             var scopes = [];
    362             delete details.rawScopes;
    363             for (var i = 0; i < rawScopes.length; i++)
    364                 scopes.push(InjectedScript.CallFrameProxy._createScopeJson(rawScopes[i].type, rawScopes[i].object, objectGroupName));
    365             details.scopeChain = scopes;
    366         }
    367         return details;
    368     },
    369 
    370     /**
    371      * @param {string} objectId
    372      */
    373     releaseObject: function(objectId)
    374     {
    375         var parsedObjectId = this._parseObjectId(objectId);
    376         this._releaseObject(parsedObjectId.id);
    377     },
    378 
    379     /**
    380      * @param {number} id
    381      */
    382     _releaseObject: function(id)
    383     {
    384         delete this._idToWrappedObject[id];
    385         delete this._idToObjectGroupName[id];
    386     },
    387 
    388     /**
    389      * @param {Object} object
    390      * @param {boolean} ownProperties
    391      * @param {boolean} accessorPropertiesOnly
    392      * @return {Array.<Object>}
    393      */
    394     _propertyDescriptors: function(object, ownProperties, accessorPropertiesOnly)
    395     {
    396         var descriptors = [];
    397         var nameProcessed = {};
    398         nameProcessed["__proto__"] = null;
    399         for (var o = object; this._isDefined(o); o = o.__proto__) {
    400             var names = Object.getOwnPropertyNames(/** @type {!Object} */ (o));
    401             for (var i = 0; i < names.length; ++i) {
    402                 var name = names[i];
    403                 if (nameProcessed[name])
    404                     continue;
    405 
    406                 try {
    407                     nameProcessed[name] = true;
    408                     var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */ (o), name);
    409                     if (descriptor) {
    410                         if (accessorPropertiesOnly && !("get" in descriptor || "set" in descriptor))
    411                             continue;
    412                     } else {
    413                         // Not all bindings provide proper descriptors. Fall back to the writable, configurable property.
    414                         if (accessorPropertiesOnly)
    415                             continue;
    416                         try {
    417                             descriptor = { name: name, value: o[name], writable: false, configurable: false, enumerable: false};
    418                             if (o === object)
    419                                 descriptor.isOwn = true;
    420                             descriptors.push(descriptor);
    421                         } catch (e) {
    422                             // Silent catch.
    423                         }
    424                         continue;
    425                     }
    426                 } catch (e) {
    427                     if (accessorPropertiesOnly)
    428                         continue;
    429                     var descriptor = {};
    430                     descriptor.value = e;
    431                     descriptor.wasThrown = true;
    432                 }
    433 
    434                 descriptor.name = name;
    435                 if (o === object)
    436                     descriptor.isOwn = true;
    437                 descriptors.push(descriptor);
    438             }
    439             if (ownProperties) {
    440                 if (object.__proto__ && !accessorPropertiesOnly)
    441                     descriptors.push({ name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true});
    442                 break;
    443             }
    444         }
    445         return descriptors;
    446     },
    447 
    448     /**
    449      * @param {string} expression
    450      * @param {string} objectGroup
    451      * @param {boolean} injectCommandLineAPI
    452      * @param {boolean} returnByValue
    453      * @param {boolean} generatePreview
    454      * @return {*}
    455      */
    456     evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
    457     {
    458         return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview);
    459     },
    460 
    461     /**
    462      * @param {string} objectId
    463      * @param {string} expression
    464      * @param {boolean} returnByValue
    465      * @return {Object|string}
    466      */
    467     callFunctionOn: function(objectId, expression, args, returnByValue)
    468     {
    469         var parsedObjectId = this._parseObjectId(objectId);
    470         var object = this._objectForId(parsedObjectId);
    471         if (!this._isDefined(object))
    472             return "Could not find object with given id";
    473 
    474         if (args) {
    475             var resolvedArgs = [];
    476             args = InjectedScriptHost.evaluate(args);
    477             for (var i = 0; i < args.length; ++i) {
    478                 var resolvedCallArgument;
    479                 try {
    480                     resolvedCallArgument = this._resolveCallArgument(args[i]);
    481                 } catch (e) {
    482                     return String(e);
    483                 }
    484                 resolvedArgs.push(resolvedCallArgument)
    485             }
    486         }
    487 
    488         try {
    489             var objectGroup = this._idToObjectGroupName[parsedObjectId.id];
    490             var func = InjectedScriptHost.evaluate("(" + expression + ")");
    491             if (typeof func !== "function")
    492                 return "Given expression does not evaluate to a function";
    493 
    494             return { wasThrown: false,
    495                      result: this._wrapObject(func.apply(object, resolvedArgs), objectGroup, returnByValue) };
    496         } catch (e) {
    497             return this._createThrownValue(e, objectGroup);
    498         }
    499     },
    500 
    501     /**
    502      * Resolves a value from CallArgument description.
    503      * @param {RuntimeAgent.CallArgument} callArgumentJson
    504      * @return {*} resolved value
    505      * @throws {string} error message
    506      */
    507     _resolveCallArgument: function(callArgumentJson) {
    508         var objectId = callArgumentJson.objectId;
    509         if (objectId) {
    510             var parsedArgId = this._parseObjectId(objectId);
    511             if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
    512                 throw "Arguments should belong to the same JavaScript world as the target object.";
    513 
    514             var resolvedArg = this._objectForId(parsedArgId);
    515             if (!this._isDefined(resolvedArg))
    516                 throw "Could not find object with given id";
    517 
    518             return resolvedArg;
    519         } else if ("value" in callArgumentJson)
    520             return callArgumentJson.value;
    521         else
    522             return undefined;
    523     },
    524 
    525     /**
    526      * @param {Function} evalFunction
    527      * @param {Object} object
    528      * @param {string} objectGroup
    529      * @param {boolean} isEvalOnCallFrame
    530      * @param {boolean} injectCommandLineAPI
    531      * @param {boolean} returnByValue
    532      * @param {boolean} generatePreview
    533      * @return {*}
    534      */
    535     _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview)
    536     {
    537         try {
    538             return { wasThrown: false,
    539                      result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI), objectGroup, returnByValue, generatePreview) };
    540         } catch (e) {
    541             return this._createThrownValue(e, objectGroup);
    542         }
    543     },
    544 
    545     /**
    546      * @param {*} value
    547      * @param {string} objectGroup
    548      * @return {Object}
    549      */
    550     _createThrownValue: function(value, objectGroup)
    551     {
    552         var remoteObject = this._wrapObject(value, objectGroup);
    553         try {
    554             remoteObject.description = this._toString(value);
    555         } catch (e) {}
    556         return { wasThrown: true,
    557                  result: remoteObject };
    558     },
    559 
    560     /**
    561      * @param {Function} evalFunction
    562      * @param {Object} object
    563      * @param {string} objectGroup
    564      * @param {string} expression
    565      * @param {boolean} isEvalOnCallFrame
    566      * @param {boolean} injectCommandLineAPI
    567      * @return {*}
    568      */
    569     _evaluateOn: function(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI)
    570     {
    571         // Only install command line api object for the time of evaluation.
    572         // Surround the expression in with statements to inject our command line API so that
    573         // the window object properties still take more precedent than our API functions.
    574 
    575         try {
    576             if (injectCommandLineAPI && inspectedWindow.console) {
    577                 inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
    578                 expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
    579             }
    580             var result = evalFunction.call(object, expression);
    581             if (objectGroup === "console")
    582                 this._lastResult = result;
    583             return result;
    584         } finally {
    585             if (injectCommandLineAPI && inspectedWindow.console)
    586                 delete inspectedWindow.console._commandLineAPI;
    587         }
    588     },
    589 
    590     /**
    591      * @param {Object} callFrame
    592      * @return {Array.<InjectedScript.CallFrameProxy>|boolean}
    593      */
    594     wrapCallFrames: function(callFrame)
    595     {
    596         if (!callFrame)
    597             return false;
    598 
    599         var result = [];
    600         var depth = 0;
    601         do {
    602             result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
    603             callFrame = callFrame.caller;
    604         } while (callFrame);
    605         return result;
    606     },
    607 
    608     /**
    609      * @param {Object} topCallFrame
    610      * @param {string} callFrameId
    611      * @param {string} expression
    612      * @param {string} objectGroup
    613      * @param {boolean} injectCommandLineAPI
    614      * @param {boolean} returnByValue
    615      * @param {boolean} generatePreview
    616      * @return {*}
    617      */
    618     evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
    619     {
    620         var callFrame = this._callFrameForId(topCallFrame, callFrameId);
    621         if (!callFrame)
    622             return "Could not find call frame with given id";
    623         return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview);
    624     },
    625 
    626     /**
    627      * @param {Object} topCallFrame
    628      * @param {string} callFrameId
    629      * @return {*}
    630      */
    631     restartFrame: function(topCallFrame, callFrameId)
    632     {
    633         var callFrame = this._callFrameForId(topCallFrame, callFrameId);
    634         if (!callFrame)
    635             return "Could not find call frame with given id";
    636         var result = callFrame.restart();
    637         if (result === false)
    638             result = "Restart frame is not supported";
    639         return result;
    640     },
    641 
    642     /**
    643      * @param {Object} topCallFrame
    644      * @param {string} callFrameId
    645      * @return {*} a stepIn position array ready for protocol JSON or a string error
    646      */
    647     getStepInPositions: function(topCallFrame, callFrameId)
    648     {
    649         var callFrame = this._callFrameForId(topCallFrame, callFrameId);
    650         if (!callFrame)
    651             return "Could not find call frame with given id";
    652         var stepInPositionsUnpacked = JSON.parse(callFrame.stepInPositions);
    653         if (typeof stepInPositionsUnpacked !== "object")
    654             return "Step in positions not available";
    655         return stepInPositionsUnpacked;
    656     },
    657 
    658     /**
    659      * Either callFrameId or functionObjectId must be specified.
    660      * @param {Object} topCallFrame
    661      * @param {string|boolean} callFrameId or false
    662      * @param {string|boolean} functionObjectId or false
    663      * @param {number} scopeNumber
    664      * @param {string} variableName
    665      * @param {string} newValueJsonString RuntimeAgent.CallArgument structure serialized as string
    666      * @return {string|undefined} undefined if success or an error message
    667      */
    668     setVariableValue: function(topCallFrame, callFrameId, functionObjectId, scopeNumber, variableName, newValueJsonString)
    669     {
    670         var setter;
    671         if (typeof callFrameId === "string") {
    672             var callFrame = this._callFrameForId(topCallFrame, callFrameId);
    673             if (!callFrame)
    674                 return "Could not find call frame with given id";
    675             setter = callFrame.setVariableValue.bind(callFrame);
    676         } else {
    677             var parsedFunctionId = this._parseObjectId(/** @type {string} */ (functionObjectId));
    678             var func = this._objectForId(parsedFunctionId);
    679             if (typeof func !== "function")
    680                 return "Cannot resolve function by id.";
    681             setter = InjectedScriptHost.setFunctionVariableValue.bind(InjectedScriptHost, func);
    682         }
    683         var newValueJson;
    684         try {
    685             newValueJson = InjectedScriptHost.evaluate("(" + newValueJsonString + ")");
    686         } catch (e) {
    687             return "Failed to parse new value JSON " + newValueJsonString + " : " + e;
    688         }
    689         var resolvedValue;
    690         try {
    691             resolvedValue = this._resolveCallArgument(newValueJson);
    692         } catch (e) {
    693             return String(e);
    694         }
    695         try {
    696             setter(scopeNumber, variableName, resolvedValue);
    697         } catch (e) {
    698             return "Failed to change variable value: " + e;
    699         }
    700         return undefined;
    701     },
    702 
    703     /**
    704      * @param {Object} topCallFrame
    705      * @param {string} callFrameId
    706      * @return {Object}
    707      */
    708     _callFrameForId: function(topCallFrame, callFrameId)
    709     {
    710         var parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
    711         var ordinal = parsedCallFrameId["ordinal"];
    712         var callFrame = topCallFrame;
    713         while (--ordinal >= 0 && callFrame)
    714             callFrame = callFrame.caller;
    715         return callFrame;
    716     },
    717 
    718     /**
    719      * @param {Object} objectId
    720      * @return {Object}
    721      */
    722     _objectForId: function(objectId)
    723     {
    724         return this._idToWrappedObject[objectId.id];
    725     },
    726 
    727     /**
    728      * @param {string} objectId
    729      * @return {Object}
    730      */
    731     findObjectById: function(objectId)
    732     {
    733         var parsedObjectId = this._parseObjectId(objectId);
    734         return this._objectForId(parsedObjectId);
    735     },
    736 
    737     /**
    738      * @param {string} objectId
    739      * @return {Node}
    740      */
    741     nodeForObjectId: function(objectId)
    742     {
    743         var object = this.findObjectById(objectId);
    744         if (!object || this._subtype(object) !== "node")
    745             return null;
    746         return /** @type {Node} */ (object);
    747     },
    748 
    749     /**
    750      * @param {string} name
    751      * @return {Object}
    752      */
    753     module: function(name)
    754     {
    755         return this._modules[name];
    756     },
    757 
    758     /**
    759      * @param {string} name
    760      * @param {string} source
    761      * @return {Object}
    762      */
    763     injectModule: function(name, source)
    764     {
    765         delete this._modules[name];
    766         var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
    767         if (typeof moduleFunction !== "function") {
    768             inspectedWindow.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
    769             return null;
    770         }
    771         var module = moduleFunction.call(inspectedWindow, InjectedScriptHost, inspectedWindow, injectedScriptId, this);
    772         this._modules[name] = module;
    773         return module;
    774     },
    775 
    776     /**
    777      * @param {*} object
    778      * @return {boolean}
    779      */
    780     _isDefined: function(object)
    781     {
    782         return !!object || this._isHTMLAllCollection(object);
    783     },
    784 
    785     /**
    786      * @param {*} object
    787      * @return {boolean}
    788      */
    789     _isHTMLAllCollection: function(object)
    790     {
    791         // document.all is reported as undefined, but we still want to process it.
    792         return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCollection(object);
    793     },
    794 
    795     /**
    796      * @param {*} obj
    797      * @return {string?}
    798      */
    799     _subtype: function(obj)
    800     {
    801         if (obj === null)
    802             return "null";
    803 
    804         if (this.isPrimitiveValue(obj))
    805             return null;
    806 
    807         if (this._isHTMLAllCollection(obj))
    808             return "array";
    809 
    810         var preciseType = InjectedScriptHost.type(obj);
    811         if (preciseType)
    812             return preciseType;
    813 
    814         // FireBug's array detection.
    815         try {
    816             if (typeof obj.splice === "function" && isFinite(obj.length))
    817                 return "array";
    818             if (Object.prototype.toString.call(obj) === "[object Arguments]" && isFinite(obj.length)) // arguments.
    819                 return "array";
    820         } catch (e) {
    821         }
    822 
    823         // If owning frame has navigated to somewhere else window properties will be undefined.
    824         return null;
    825     },
    826 
    827     /**
    828      * @param {*} obj
    829      * @return {string?}
    830      */
    831     _describe: function(obj)
    832     {
    833         if (this.isPrimitiveValue(obj))
    834             return null;
    835 
    836         // Type is object, get subtype.
    837         var subtype = this._subtype(obj);
    838 
    839         if (subtype === "regexp")
    840             return this._toString(obj);
    841 
    842         if (subtype === "date")
    843             return this._toString(obj);
    844 
    845         if (subtype === "node") {
    846             var description = obj.nodeName.toLowerCase();
    847             switch (obj.nodeType) {
    848             case 1 /* Node.ELEMENT_NODE */:
    849                 description += obj.id ? "#" + obj.id : "";
    850                 var className = obj.className;
    851                 description += className ? "." + className : "";
    852                 break;
    853             case 10 /*Node.DOCUMENT_TYPE_NODE */:
    854                 description = "<!DOCTYPE " + description + ">";
    855                 break;
    856             }
    857             return description;
    858         }
    859 
    860         var className = InjectedScriptHost.internalConstructorName(obj);
    861         if (subtype === "array") {
    862             if (typeof obj.length === "number")
    863                 className += "[" + obj.length + "]";
    864             return className;
    865         }
    866 
    867         // NodeList in JSC is a function, check for array prior to this.
    868         if (typeof obj === "function")
    869             return this._toString(obj);
    870 
    871         if (className === "Object") {
    872             // In Chromium DOM wrapper prototypes will have Object as their constructor name,
    873             // get the real DOM wrapper name from the constructor property.
    874             var constructorName = obj.constructor && obj.constructor.name;
    875             if (constructorName)
    876                 return constructorName;
    877         }
    878         return className;
    879     },
    880 
    881     /**
    882      * @param {*} obj
    883      * @return {string}
    884      */
    885     _toString: function(obj)
    886     {
    887         // We don't use String(obj) because inspectedWindow.String is undefined if owning frame navigated to another page.
    888         return "" + obj;
    889     }
    890 }
    891 
    892 /**
    893  * @type {!InjectedScript}
    894  * @const
    895  */
    896 var injectedScript = new InjectedScript();
    897 
    898 /**
    899  * @constructor
    900  * @param {*} object
    901  * @param {string=} objectGroupName
    902  * @param {boolean=} forceValueType
    903  * @param {boolean=} generatePreview
    904  * @param {?Array.<string>=} columnNames
    905  */
    906 InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, generatePreview, columnNames)
    907 {
    908     this.type = typeof object;
    909     if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
    910         // We don't send undefined values over JSON.
    911         if (this.type !== "undefined")
    912             this.value = object;
    913 
    914         // Null object is object with 'null' subtype.
    915         if (object === null)
    916             this.subtype = "null";
    917 
    918         // Provide user-friendly number values.
    919         if (this.type === "number")
    920             this.description = object + "";
    921         return;
    922     }
    923 
    924     object = /** @type {Object} */ (object);
    925 
    926     this.objectId = injectedScript._bind(object, objectGroupName);
    927     var subtype = injectedScript._subtype(object);
    928     if (subtype)
    929         this.subtype = subtype;
    930     this.className = InjectedScriptHost.internalConstructorName(object);
    931     this.description = injectedScript._describe(object);
    932 
    933     if (generatePreview && (this.type === "object" || injectedScript._isHTMLAllCollection(object)))
    934         this.preview = this._generatePreview(object, undefined, columnNames);
    935 }
    936 
    937 InjectedScript.RemoteObject.prototype = {
    938     /**
    939      * @param {Object} object
    940      * @param {Array.<string>=} firstLevelKeys
    941      * @param {?Array.<string>=} secondLevelKeys
    942      * @return {Object} preview
    943      */
    944     _generatePreview: function(object, firstLevelKeys, secondLevelKeys)
    945     {
    946         var preview = {};
    947         preview.lossless = true;
    948         preview.overflow = false;
    949         preview.properties = [];
    950 
    951         var isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
    952         var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
    953 
    954         var propertiesThreshold = {
    955             properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount),
    956             indexes: isTableRowsRequest ? 1000 : Math.max(100, firstLevelKeysCount)
    957         };
    958         for (var o = object; injectedScript._isDefined(o); o = o.__proto__)
    959             this._generateProtoPreview(o, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys);
    960         return preview;
    961     },
    962 
    963     /**
    964      * @param {Object} object
    965      * @param {Object} preview
    966      * @param {Object} propertiesThreshold
    967      * @param {Array.<string>=} firstLevelKeys
    968      * @param {Array.<string>=} secondLevelKeys
    969      */
    970     _generateProtoPreview: function(object, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys)
    971     {
    972         var propertyNames = firstLevelKeys ? firstLevelKeys : Object.keys(/** @type {!Object} */ (object));
    973         try {
    974             for (var i = 0; i < propertyNames.length; ++i) {
    975                 if (!propertiesThreshold.properties || !propertiesThreshold.indexes) {
    976                     preview.overflow = true;
    977                     preview.lossless = false;
    978                     break;
    979                 }
    980                 var name = propertyNames[i];
    981                 if (this.subtype === "array" && name === "length")
    982                     continue;
    983 
    984                 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */ (object), name);
    985                 if (!("value" in descriptor) || !descriptor.enumerable) {
    986                     preview.lossless = false;
    987                     continue;
    988                 }
    989 
    990                 var value = descriptor.value;
    991                 if (value === null) {
    992                     this._appendPropertyPreview(preview, { name: name, type: "object", value: "null" }, propertiesThreshold);
    993                     continue;
    994                 }
    995 
    996                 const maxLength = 100;
    997                 var type = typeof value;
    998 
    999                 if (InjectedScript.primitiveTypes[type]) {
   1000                     if (type === "string") {
   1001                         if (value.length > maxLength) {
   1002                             value = this._abbreviateString(value, maxLength, true);
   1003                             preview.lossless = false;
   1004                         }
   1005                         value = value.replace(/\n/g, "\u21B5");
   1006                     }
   1007                     this._appendPropertyPreview(preview, { name: name, type: type, value: value + "" }, propertiesThreshold);
   1008                     continue;
   1009                 }
   1010 
   1011                 if (secondLevelKeys === null || secondLevelKeys) {
   1012                     var subPreview = this._generatePreview(value, secondLevelKeys || undefined);
   1013                     var property = { name: name, type: type, valuePreview: subPreview };
   1014                     this._appendPropertyPreview(preview, property, propertiesThreshold);
   1015                     if (!subPreview.lossless)
   1016                         preview.lossless = false;
   1017                     if (subPreview.overflow)
   1018                         preview.overflow = true;
   1019                     continue;
   1020                 }
   1021 
   1022                 preview.lossless = false;
   1023 
   1024                 var subtype = injectedScript._subtype(value);
   1025                 var description = "";
   1026                 if (type !== "function")
   1027                     description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp");
   1028 
   1029                 var property = { name: name, type: type, value: description };
   1030                 if (subtype)
   1031                     property.subtype = subtype;
   1032                 this._appendPropertyPreview(preview, property, propertiesThreshold);
   1033             }
   1034         } catch (e) {
   1035         }
   1036     },
   1037 
   1038     /**
   1039      * @param {Object} preview
   1040      * @param {Object} property
   1041      * @param {Object} propertiesThreshold
   1042      */
   1043     _appendPropertyPreview: function(preview, property, propertiesThreshold)
   1044     {
   1045         if (isNaN(property.name))
   1046             propertiesThreshold.properties--;
   1047         else
   1048             propertiesThreshold.indexes--;
   1049         preview.properties.push(property);
   1050     },
   1051 
   1052     /**
   1053      * @param {string} string
   1054      * @param {number} maxLength
   1055      * @param {boolean=} middle
   1056      * @returns
   1057      */
   1058     _abbreviateString: function(string, maxLength, middle)
   1059     {
   1060         if (string.length <= maxLength)
   1061             return string;
   1062         if (middle) {
   1063             var leftHalf = maxLength >> 1;
   1064             var rightHalf = maxLength - leftHalf - 1;
   1065             return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
   1066         }
   1067         return string.substr(0, maxLength) + "\u2026";
   1068     }
   1069 }
   1070 /**
   1071  * @constructor
   1072  * @param {number} ordinal
   1073  * @param {Object} callFrame
   1074  */
   1075 InjectedScript.CallFrameProxy = function(ordinal, callFrame)
   1076 {
   1077     this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + injectedScriptId + "}";
   1078     this.functionName = (callFrame.type === "function" ? callFrame.functionName : "");
   1079     this.location = { scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column };
   1080     this.scopeChain = this._wrapScopeChain(callFrame);
   1081     this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace");
   1082 }
   1083 
   1084 InjectedScript.CallFrameProxy.prototype = {
   1085     /**
   1086      * @param {Object} callFrame
   1087      * @return {!Array.<DebuggerAgent.Scope>}
   1088      */
   1089     _wrapScopeChain: function(callFrame)
   1090     {
   1091         var scopeChain = callFrame.scopeChain;
   1092         var scopeChainProxy = [];
   1093         for (var i = 0; i < scopeChain.length; i++) {
   1094             var scope = InjectedScript.CallFrameProxy._createScopeJson(callFrame.scopeType(i), scopeChain[i], "backtrace");
   1095             scopeChainProxy.push(scope);
   1096         }
   1097         return scopeChainProxy;
   1098     }
   1099 }
   1100 
   1101 /**
   1102  * @param {number} scopeTypeCode
   1103  * @param {*} scopeObject
   1104  * @param {string} groupId
   1105  * @return {!DebuggerAgent.Scope}
   1106  */
   1107 InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeObject, groupId) {
   1108     const GLOBAL_SCOPE = 0;
   1109     const LOCAL_SCOPE = 1;
   1110     const WITH_SCOPE = 2;
   1111     const CLOSURE_SCOPE = 3;
   1112     const CATCH_SCOPE = 4;
   1113 
   1114     /** @type {!Object.<number, string>} */
   1115     var scopeTypeNames = {};
   1116     scopeTypeNames[GLOBAL_SCOPE] = "global";
   1117     scopeTypeNames[LOCAL_SCOPE] = "local";
   1118     scopeTypeNames[WITH_SCOPE] = "with";
   1119     scopeTypeNames[CLOSURE_SCOPE] = "closure";
   1120     scopeTypeNames[CATCH_SCOPE] = "catch";
   1121 
   1122     return {
   1123         object: injectedScript._wrapObject(scopeObject, groupId),
   1124         type: /** @type {DebuggerAgent.ScopeType} */ (scopeTypeNames[scopeTypeCode])
   1125     };
   1126 }
   1127 
   1128 /**
   1129  * @constructor
   1130  * @param {CommandLineAPIImpl} commandLineAPIImpl
   1131  * @param {Object} callFrame
   1132  */
   1133 function CommandLineAPI(commandLineAPIImpl, callFrame)
   1134 {
   1135     /**
   1136      * @param {string} member
   1137      * @return {boolean}
   1138      */
   1139     function inScopeVariables(member)
   1140     {
   1141         if (!callFrame)
   1142             return false;
   1143 
   1144         var scopeChain = callFrame.scopeChain;
   1145         for (var i = 0; i < scopeChain.length; ++i) {
   1146             if (member in scopeChain[i])
   1147                 return true;
   1148         }
   1149         return false;
   1150     }
   1151 
   1152     /**
   1153      * @param {string} name The name of the method for which a toString method should be generated.
   1154      * @return {function():string}
   1155      */
   1156     function customToStringMethod(name)
   1157     {
   1158         return function () { return "function " + name + "() { [Command Line API] }"; };
   1159     }
   1160 
   1161     for (var i = 0; i < CommandLineAPI.members_.length; ++i) {
   1162         var member = CommandLineAPI.members_[i];
   1163         if (member in inspectedWindow || inScopeVariables(member))
   1164             continue;
   1165 
   1166         this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
   1167         this[member].toString = customToStringMethod(member);
   1168     }
   1169 
   1170     for (var i = 0; i < 5; ++i) {
   1171         var member = "$" + i;
   1172         if (member in inspectedWindow || inScopeVariables(member))
   1173             continue;
   1174 
   1175         this.__defineGetter__("$" + i, bind(commandLineAPIImpl._inspectedObject, commandLineAPIImpl, i));
   1176     }
   1177 
   1178     this.$_ = injectedScript._lastResult;
   1179 }
   1180 
   1181 // NOTE: Please keep the list of API methods below snchronized to that in WebInspector.RuntimeModel!
   1182 /**
   1183  * @type {Array.<string>}
   1184  * @const
   1185  */
   1186 CommandLineAPI.members_ = [
   1187     "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd",
   1188     "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners",
   1189     "debug", "undebug", "monitor", "unmonitor", "table"
   1190 ];
   1191 
   1192 /**
   1193  * @constructor
   1194  */
   1195 function CommandLineAPIImpl()
   1196 {
   1197 }
   1198 
   1199 CommandLineAPIImpl.prototype = {
   1200     /**
   1201      * @param {string} selector
   1202      * @param {Node=} start
   1203      */
   1204     $: function (selector, start)
   1205     {
   1206         if (this._canQuerySelectorOnNode(start))
   1207             return start.querySelector(selector);
   1208 
   1209         return inspectedWindow.document.querySelector(selector);
   1210     },
   1211 
   1212     /**
   1213      * @param {string} selector
   1214      * @param {Node=} start
   1215      */
   1216     $$: function (selector, start)
   1217     {
   1218         if (this._canQuerySelectorOnNode(start))
   1219             return start.querySelectorAll(selector);
   1220         return inspectedWindow.document.querySelectorAll(selector);
   1221     },
   1222 
   1223     /**
   1224      * @param {Node=} node
   1225      * @return {boolean}
   1226      */
   1227     _canQuerySelectorOnNode: function(node)
   1228     {
   1229         return !!node && InjectedScriptHost.type(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
   1230     },
   1231 
   1232     /**
   1233      * @param {string} xpath
   1234      * @param {Node=} context
   1235      */
   1236     $x: function(xpath, context)
   1237     {
   1238         var doc = (context && context.ownerDocument) || inspectedWindow.document;
   1239         var result = doc.evaluate(xpath, context || doc, null, XPathResult.ANY_TYPE, null);
   1240         switch (result.resultType) {
   1241         case XPathResult.NUMBER_TYPE:
   1242             return result.numberValue;
   1243         case XPathResult.STRING_TYPE:
   1244             return result.stringValue;
   1245         case XPathResult.BOOLEAN_TYPE:
   1246             return result.booleanValue;
   1247         default:
   1248             var nodes = [];
   1249             var node;
   1250             while (node = result.iterateNext())
   1251                 nodes.push(node);
   1252             return nodes;
   1253         }
   1254     },
   1255 
   1256     dir: function()
   1257     {
   1258         return inspectedWindow.console.dir.apply(inspectedWindow.console, arguments)
   1259     },
   1260 
   1261     dirxml: function()
   1262     {
   1263         return inspectedWindow.console.dirxml.apply(inspectedWindow.console, arguments)
   1264     },
   1265 
   1266     keys: function(object)
   1267     {
   1268         return Object.keys(object);
   1269     },
   1270 
   1271     values: function(object)
   1272     {
   1273         var result = [];
   1274         for (var key in object)
   1275             result.push(object[key]);
   1276         return result;
   1277     },
   1278 
   1279     profile: function()
   1280     {
   1281         return inspectedWindow.console.profile.apply(inspectedWindow.console, arguments)
   1282     },
   1283 
   1284     profileEnd: function()
   1285     {
   1286         return inspectedWindow.console.profileEnd.apply(inspectedWindow.console, arguments)
   1287     },
   1288 
   1289     /**
   1290      * @param {Object} object
   1291      * @param {Array.<string>|string=} types
   1292      */
   1293     monitorEvents: function(object, types)
   1294     {
   1295         if (!object || !object.addEventListener || !object.removeEventListener)
   1296             return;
   1297         types = this._normalizeEventTypes(types);
   1298         for (var i = 0; i < types.length; ++i) {
   1299             object.removeEventListener(types[i], this._logEvent, false);
   1300             object.addEventListener(types[i], this._logEvent, false);
   1301         }
   1302     },
   1303 
   1304     /**
   1305      * @param {Object} object
   1306      * @param {Array.<string>|string=} types
   1307      */
   1308     unmonitorEvents: function(object, types)
   1309     {
   1310         if (!object || !object.addEventListener || !object.removeEventListener)
   1311             return;
   1312         types = this._normalizeEventTypes(types);
   1313         for (var i = 0; i < types.length; ++i)
   1314             object.removeEventListener(types[i], this._logEvent, false);
   1315     },
   1316 
   1317     /**
   1318      * @param {*} object
   1319      * @return {*}
   1320      */
   1321     inspect: function(object)
   1322     {
   1323         return injectedScript._inspect(object);
   1324     },
   1325 
   1326     copy: function(object)
   1327     {
   1328         if (injectedScript._subtype(object) === "node")
   1329             object = object.outerHTML;
   1330         var string = object + "";
   1331         var hints = { copyToClipboard: true };
   1332         var remoteObject = injectedScript._wrapObject(string, "")
   1333         InjectedScriptHost.inspect(remoteObject, hints);
   1334     },
   1335 
   1336     clear: function()
   1337     {
   1338         InjectedScriptHost.clearConsoleMessages();
   1339     },
   1340 
   1341     /**
   1342      * @param {Node} node
   1343      */
   1344     getEventListeners: function(node)
   1345     {
   1346         return InjectedScriptHost.getEventListeners(node);
   1347     },
   1348 
   1349     debug: function(fn)
   1350     {
   1351         InjectedScriptHost.debugFunction(fn);
   1352     },
   1353 
   1354     undebug: function(fn)
   1355     {
   1356         InjectedScriptHost.undebugFunction(fn);
   1357     },
   1358 
   1359     monitor: function(fn)
   1360     {
   1361         InjectedScriptHost.monitorFunction(fn);
   1362     },
   1363 
   1364     unmonitor: function(fn) {
   1365         InjectedScriptHost.unmonitorFunction(fn);
   1366     },
   1367 
   1368     table: function()
   1369     {
   1370         inspectedWindow.console.table.apply(inspectedWindow.console, arguments);
   1371     },
   1372 
   1373     /**
   1374      * @param {number} num
   1375      */
   1376     _inspectedObject: function(num)
   1377     {
   1378         return InjectedScriptHost.inspectedObject(num);
   1379     },
   1380 
   1381     /**
   1382      * @param {Array.<string>|string=} types
   1383      * @return {Array.<string>}
   1384      */
   1385     _normalizeEventTypes: function(types)
   1386     {
   1387         if (typeof types === "undefined")
   1388             types = [ "mouse", "key", "touch", "control", "load", "unload", "abort", "error", "select", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation" ];
   1389         else if (typeof types === "string")
   1390             types = [ types ];
   1391 
   1392         var result = [];
   1393         for (var i = 0; i < types.length; i++) {
   1394             if (types[i] === "mouse")
   1395                 result.splice(0, 0, "mousedown", "mouseup", "click", "dblclick", "mousemove", "mouseover", "mouseout", "mousewheel");
   1396             else if (types[i] === "key")
   1397                 result.splice(0, 0, "keydown", "keyup", "keypress", "textInput");
   1398             else if (types[i] === "touch")
   1399                 result.splice(0, 0, "touchstart", "touchmove", "touchend", "touchcancel");
   1400             else if (types[i] === "control")
   1401                 result.splice(0, 0, "resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset");
   1402             else
   1403                 result.push(types[i]);
   1404         }
   1405         return result;
   1406     },
   1407 
   1408     /**
   1409      * @param {Event} event
   1410      */
   1411     _logEvent: function(event)
   1412     {
   1413         inspectedWindow.console.log(event.type, event);
   1414     }
   1415 }
   1416 
   1417 injectedScript._commandLineAPIImpl = new CommandLineAPIImpl();
   1418 return injectedScript;
   1419 })
   1420