Changeset 219639 in webkit


Ignore:
Timestamp:
Jul 18, 2017, 4:19:57 PM (8 years ago)
Author:
Joseph Pecoraro
Message:

Web Inspector: Modernize InjectedScriptSource
https://bugs.webkit.org/show_bug.cgi?id=173890

Reviewed by Brian Burg.

Source/JavaScriptCore:

  • inspector/InjectedScript.h:

Reorder functions to be slightly better.

  • inspector/InjectedScriptSource.js:
  • Convert to classes named InjectedScript and RemoteObject
  • Align InjectedScript's API with the wrapper C++ interfaces
  • Move some code to RemoteObject where appropriate (subtype, describe)
  • Move some code to helper functions (isPrimitiveValue, isDefined)
  • Refactor for readability and modern features
  • Remove some unused / unnecessary code

Source/WebCore:

Covered by existing tests.

  • inspector/CommandLineAPIModuleSource.js:

(CommandLineAPIImpl.prototype.copy):
(CommandLineAPIImpl.prototype._inspect):
Use RemoteObject, a new parameter.

LayoutTests:

  • inspector/model/remote-object-expected.txt:
  • inspector/model/stack-trace-expected.txt:

Now that we use classes, implicitly strict mode, the call frame
for evaluate gets tail call eliminated.

Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r219638 r219639  
     12017-07-18  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Modernize InjectedScriptSource
     4        https://bugs.webkit.org/show_bug.cgi?id=173890
     5
     6        Reviewed by Brian Burg.
     7
     8        * inspector/model/remote-object-expected.txt:
     9        * inspector/model/stack-trace-expected.txt:
     10        Now that we use classes, implicitly strict mode, the call frame
     11        for evaluate gets tail call eliminated.
     12
    1132017-07-18  Ryosuke Niwa  <rniwa@webkit.org>
    214
  • trunk/LayoutTests/inspector/model/remote-object-expected.txt

    r219612 r219639  
    16571657        "_name": "stack",
    16581658        "_type": "string",
    1659         "_value": "global code\nevaluateWithScopeExtension@[native code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
     1659        "_value": "global code\nevaluateWithScopeExtension@[native code]\n_evaluateOn\n_evaluateAndWrap"
    16601660      }
    16611661    ],
     
    16961696        "_name": "stack",
    16971697        "_type": "string",
    1698         "_value": "eval@[native code]\nglobal code\nevaluateWithScopeEx…ative code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
     1698        "_value": "eval@[native code]\nglobal code\nevaluateWithScopeExtension@[native code]\n_evaluateOn\n_evaluateAndWrap"
    16991699      }
    17001700    ],
     
    17301730        "_name": "stack",
    17311731        "_type": "string",
    1732         "_value": "splitText@[native code]\nglobal code\nevaluateWithSc…ative code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
     1732        "_value": "splitText@[native code]\nglobal code\nevaluateWithSc…ension@[native code]\n_evaluateOn\n_evaluateAndWrap"
    17331733      },
    17341734      {
  • trunk/LayoutTests/inspector/model/stack-trace-expected.txt

    r200533 r219639  
    99PASS: CallFrame in StackTrace has no thisObject.
    1010PASS: CallFrame in StackTrace has no scopeChain.
    11 StackTrace: 11
     11StackTrace: 10
    1212  1: foo (Anonymous Script 1 (line 1)) - nativeCode (false) programCode (false)
    1313  2: Eval Code (Anonymous Script 1 (line 1)) - nativeCode (false) programCode (true)
     
    2020  9: _evaluateOn - nativeCode (true) programCode (false)
    2121  10: _evaluateAndWrap - nativeCode (true) programCode (false)
    22   11: evaluate - nativeCode (true) programCode (false)
    2322
  • trunk/Source/JavaScriptCore/ChangeLog

    r219636 r219639  
     12017-07-18  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Modernize InjectedScriptSource
     4        https://bugs.webkit.org/show_bug.cgi?id=173890
     5
     6        Reviewed by Brian Burg.
     7
     8        * inspector/InjectedScript.h:
     9        Reorder functions to be slightly better.
     10
     11        * inspector/InjectedScriptSource.js:
     12        - Convert to classes named InjectedScript and RemoteObject
     13        - Align InjectedScript's API with the wrapper C++ interfaces
     14        - Move some code to RemoteObject where appropriate (subtype, describe)
     15        - Move some code to helper functions (isPrimitiveValue, isDefined)
     16        - Refactor for readability and modern features
     17        - Remove some unused / unnecessary code
     18
    1192017-07-18  Mark Lam  <mark.lam@apple.com>
    220
  • trunk/Source/JavaScriptCore/inspector/InjectedScript.h

    r218794 r219639  
    5252
    5353    void evaluate(ErrorString&, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>* result, Protocol::OptOutput<bool>* wasThrown, Inspector::Protocol::OptOutput<int>* savedResultIndex);
     54    void evaluateOnCallFrame(ErrorString&, JSC::JSValue callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>* result, Protocol::OptOutput<bool>* wasThrown, Inspector::Protocol::OptOutput<int>* savedResultIndex);
    5455    void callFunctionOn(ErrorString&, const String& objectId, const String& expression, const String& arguments, bool returnByValue, bool generatePreview, RefPtr<Protocol::Runtime::RemoteObject>* result, Protocol::OptOutput<bool>* wasThrown);
    55     void evaluateOnCallFrame(ErrorString&, JSC::JSValue callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>* result, Protocol::OptOutput<bool>* wasThrown, Inspector::Protocol::OptOutput<int>* savedResultIndex);
    5656    void getFunctionDetails(ErrorString&, const String& functionId, RefPtr<Protocol::Debugger::FunctionDetails>* result);
    5757    void functionDetails(ErrorString&, JSC::JSValue, RefPtr<Protocol::Debugger::FunctionDetails>* result);
  • trunk/Source/JavaScriptCore/inspector/InjectedScriptSource.js

    r218718 r219639  
    6868}
    6969
    70 var InjectedScript = function()
     70function isDefined(value)
    7171{
    72     this._lastBoundObjectId = 1;
    73     this._idToWrappedObject = {};
    74     this._idToObjectGroupName = {};
    75     this._objectGroups = {};
    76     this._modules = {};
    77     this._nextSavedResultIndex = 1;
    78     this._savedResults = [];
     72    return !!value || InjectedScriptHost.isHTMLAllCollection(value);
    7973}
    8074
    81 InjectedScript.primitiveTypes = {
    82     undefined: true,
    83     boolean: true,
    84     number: true,
    85     string: true,
     75function isPrimitiveValue(value)
     76{
     77    switch (typeof value) {
     78    case "boolean":
     79    case "number":
     80    case "string":
     81        return true;
     82    case "undefined":
     83        return !InjectedScriptHost.isHTMLAllCollection(value);
     84    default:
     85        return false;
     86    }
    8687}
    8788
    88 InjectedScript.CollectionMode = {
    89     OwnProperties: 1 << 0,          // own properties.
    90     NativeGetterProperties: 1 << 1, // native getter properties in the prototype chain.
    91     AllProperties: 1 << 2,          // all properties in the prototype chain.
    92 }
    93 
    94 InjectedScript.prototype = {
    95     isPrimitiveValue: function(object)
    96     {
    97         // FIXME(33716): typeof document.all is always 'undefined'.
    98         return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
    99     },
    100 
    101     previewValue: function(value)
    102     {
    103         return InjectedScript.RemoteObject.createObjectPreviewForValue(value, true);
    104     },
    105 
    106     functionDetails: function(func)
    107     {
    108         var details = InjectedScriptHost.functionDetails(func);
    109         if (!details)
    110             return "Cannot resolve function details.";
    111 
    112         return details;
    113     },
    114 
    115     wrapObject: function(object, groupName, canAccessInspectedGlobalObject, generatePreview)
    116     {
    117         if (canAccessInspectedGlobalObject)
    118             return this._wrapObject(object, groupName, false, generatePreview);
    119         return this._fallbackWrapper(object);
    120     },
    121 
    122     setExceptionValue: function(value)
    123     {
    124         this._exceptionValue = value;
    125     },
    126 
    127     clearExceptionValue: function()
    128     {
    129         delete this._exceptionValue;
    130     },
    131 
    132     _fallbackWrapper: function(object)
    133     {
    134         var result = {};
    135         result.type = typeof object;
    136         if (this.isPrimitiveValue(object))
    137             result.value = object;
    138         else
    139             result.description = toString(object);
    140         return result;
    141     },
    142 
    143     wrapTable: function(canAccessInspectedGlobalObject, table, columns)
    144     {
    145         if (!canAccessInspectedGlobalObject)
    146             return this._fallbackWrapper(table);
    147 
    148         // FIXME: Currently columns are ignored. Instead, the frontend filters all
    149         // properties based on the provided column names and in the provided order.
    150         // We could filter here to avoid sending very large preview objects.
    151 
    152         var columnNames = null;
    153         if (typeof columns === "string")
    154             columns = [columns];
    155 
    156         if (InjectedScriptHost.subtype(columns) === "array") {
    157             columnNames = [];
    158             for (var i = 0; i < columns.length; ++i)
    159                 columnNames.push(toString(columns[i]));
    160         }
    161 
    162         return this._wrapObject(table, "console", false, true, columnNames);
    163     },
    164 
    165     inspectObject: function(object)
    166     {
    167         if (this._commandLineAPIImpl)
    168             this._commandLineAPIImpl.inspect(object);
    169     },
    170 
    171     _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames)
    172     {
    173         try {
    174             return new InjectedScript.RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
    175         } catch (e) {
    176             try {
    177                 var description = injectedScript._describe(e);
    178             } catch (ex) {
    179                 var description = "<failed to convert exception to string>";
    180             }
    181             return new InjectedScript.RemoteObject(description);
    182         }
    183     },
    184 
    185     _bind: function(object, objectGroupName)
    186     {
    187         var id = this._lastBoundObjectId++;
    188         this._idToWrappedObject[id] = object;
    189         var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}";
    190         if (objectGroupName) {
    191             var group = this._objectGroups[objectGroupName];
    192             if (!group) {
    193                 group = [];
    194                 this._objectGroups[objectGroupName] = group;
    195             }
    196             group.push(id);
    197             this._idToObjectGroupName[id] = objectGroupName;
    198         }
    199         return objectId;
    200     },
    201 
    202     _parseObjectId: function(objectId)
    203     {
    204         return InjectedScriptHost.evaluate("(" + objectId + ")");
    205     },
    206 
    207     releaseObjectGroup: function(objectGroupName)
    208     {
    209         if (objectGroupName === "console") {
    210             delete this._lastResult;
    211             this._nextSavedResultIndex = 1;
    212             this._savedResults = [];
    213         }
    214 
    215         var group = this._objectGroups[objectGroupName];
    216         if (!group)
    217             return;
    218 
    219         for (var i = 0; i < group.length; i++)
    220             this._releaseObject(group[i]);
    221 
    222         delete this._objectGroups[objectGroupName];
    223     },
    224 
    225     dispatch: function(methodName, args)
    226     {
    227         var argsArray = InjectedScriptHost.evaluate("(" + args + ")");
    228         var result = this[methodName].apply(this, argsArray);
    229         if (typeof result === "undefined") {
    230             if (inspectedGlobalObject.console)
    231                 inspectedGlobalObject.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
    232             result = null;
    233         }
    234         return result;
    235     },
    236 
    237     getPreview: function(objectId)
     89// -------
     90
     91let InjectedScript = class InjectedScript
     92{
     93    constructor()
     94    {
     95        this._lastBoundObjectId = 1;
     96        this._idToWrappedObject = {};
     97        this._idToObjectGroupName = {};
     98        this._objectGroups = {};
     99        this._modules = {};
     100        this._nextSavedResultIndex = 1;
     101        this._savedResults = [];
     102    }
     103
     104    // InjectedScript C++ API
     105
     106    evaluate(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
     107    {
     108        return this._evaluateAndWrap(InjectedScriptHost.evaluateWithScopeExtension, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
     109    }
     110
     111    evaluateOnCallFrame(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
     112    {
     113        let callFrame = this._callFrameForId(topCallFrame, callFrameId);
     114        if (!callFrame)
     115            return "Could not find call frame with given id";
     116        return this._evaluateAndWrap(callFrame.evaluateWithScopeExtension, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
     117    }
     118
     119    callFunctionOn(objectId, expression, args, returnByValue, generatePreview)
    238120    {
    239121        let parsedObjectId = this._parseObjectId(objectId);
    240122        let object = this._objectForId(parsedObjectId);
    241 
    242         return InjectedScript.RemoteObject.createObjectPreviewForValue(object, true);
    243     },
    244 
    245     _getProperties: function(objectId, collectionMode, generatePreview, nativeGettersAsValues)
    246     {
    247         var parsedObjectId = this._parseObjectId(objectId);
    248         var object = this._objectForId(parsedObjectId);
    249         var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
    250 
    251         if (!this._isDefined(object))
    252             return false;
    253 
    254         if (isSymbol(object))
    255             return false;
    256 
    257         var descriptors = this._propertyDescriptors(object, collectionMode, nativeGettersAsValues);
    258 
    259         // Go over properties, wrap object values.
    260         for (var i = 0; i < descriptors.length; ++i) {
    261             var descriptor = descriptors[i];
    262             if ("get" in descriptor)
    263                 descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
    264             if ("set" in descriptor)
    265                 descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
    266             if ("value" in descriptor)
    267                 descriptor.value = this._wrapObject(descriptor.value, objectGroupName, false, generatePreview);
    268             if (!("configurable" in descriptor))
    269                 descriptor.configurable = false;
    270             if (!("enumerable" in descriptor))
    271                 descriptor.enumerable = false;
    272             if ("symbol" in descriptor)
    273                 descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName);
    274         }
    275 
    276         return descriptors;
    277     },
    278 
    279     getProperties: function(objectId, ownProperties, generatePreview)
    280     {
    281         var nativeGettersAsValues = false;
    282         var collectionMode = ownProperties ? InjectedScript.CollectionMode.OwnProperties : InjectedScript.CollectionMode.AllProperties;
    283         return this._getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues);
    284     },
    285 
    286     getDisplayableProperties: function(objectId, generatePreview)
    287     {
    288         var nativeGettersAsValues = true;
    289         var collectionMode = InjectedScript.CollectionMode.OwnProperties | InjectedScript.CollectionMode.NativeGetterProperties;
    290         return this._getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues);
    291     },
    292 
    293     getInternalProperties: function(objectId, generatePreview)
    294     {
    295         var parsedObjectId = this._parseObjectId(objectId);
    296         var object = this._objectForId(parsedObjectId);
    297         var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
    298 
    299         if (!this._isDefined(object))
    300             return false;
    301 
    302         if (isSymbol(object))
    303             return false;
    304 
    305         var descriptors = this._internalPropertyDescriptors(object);
    306         if (!descriptors)
    307             return [];
    308 
    309         // Go over properties, wrap object values.
    310         for (var i = 0; i < descriptors.length; ++i) {
    311             var descriptor = descriptors[i];
    312             if ("value" in descriptor)
    313                 descriptor.value = this._wrapObject(descriptor.value, objectGroupName, false, generatePreview);
    314         }
    315 
    316         return descriptors;
    317     },
    318 
    319     getCollectionEntries: function(objectId, objectGroupName, startIndex, numberToFetch)
    320     {
    321         var parsedObjectId = this._parseObjectId(objectId);
    322         var object = this._objectForId(parsedObjectId);
    323         var objectGroupName = objectGroupName || this._idToObjectGroupName[parsedObjectId.id];
    324 
    325         if (!this._isDefined(object))
    326             return;
    327 
    328         if (typeof object !== "object")
    329             return;
    330 
    331         var entries = this._entries(object, InjectedScriptHost.subtype(object), startIndex, numberToFetch);
    332         return entries.map(function(entry) {
    333             entry.value = injectedScript._wrapObject(entry.value, objectGroupName, false, true);
    334             if ("key" in entry)
    335                 entry.key = injectedScript._wrapObject(entry.key, objectGroupName, false, true);
    336             return entry;
    337         });
    338     },
    339 
    340     saveResult: function(callArgumentJSON)
    341     {
    342         this._savedResultIndex = 0;
    343 
    344         try {
    345             var callArgument = InjectedScriptHost.evaluate("(" + callArgumentJSON + ")");
    346             var value = this._resolveCallArgument(callArgument);
    347             this._saveResult(value);
    348         } catch (e) {}
    349 
    350         return this._savedResultIndex;
    351     },
    352 
    353     getFunctionDetails: function(functionId)
    354     {
    355         var parsedFunctionId = this._parseObjectId(functionId);
    356         var func = this._objectForId(parsedFunctionId);
    357         if (typeof func !== "function")
    358             return "Cannot resolve function by id.";
    359         return injectedScript.functionDetails(func);
    360     },
    361 
    362     releaseObject: function(objectId)
    363     {
    364         var parsedObjectId = this._parseObjectId(objectId);
    365         this._releaseObject(parsedObjectId.id);
    366     },
    367 
    368     _releaseObject: function(id)
    369     {
    370         delete this._idToWrappedObject[id];
    371         delete this._idToObjectGroupName[id];
    372     },
    373 
    374     evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
    375     {
    376         return this._evaluateAndWrap(InjectedScriptHost.evaluateWithScopeExtension, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
    377     },
    378 
    379     callFunctionOn: function(objectId, expression, args, returnByValue, generatePreview)
    380     {
    381         var parsedObjectId = this._parseObjectId(objectId);
    382         var object = this._objectForId(parsedObjectId);
    383         if (!this._isDefined(object))
     123        let objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
     124
     125        if (!isDefined(object))
    384126            return "Could not find object with given id";
    385127
     128        let resolvedArgs = [];
    386129        if (args) {
    387             var resolvedArgs = [];
    388             var callArgs = InjectedScriptHost.evaluate(args);
    389             for (var i = 0; i < callArgs.length; ++i) {
     130            let callArgs = InjectedScriptHost.evaluate(args);
     131            for (let i = 0; i < callArgs.length; ++i) {
    390132                try {
    391133                    resolvedArgs[i] = this._resolveCallArgument(callArgs[i]);
     
    397139
    398140        try {
    399             var objectGroup = this._idToObjectGroupName[parsedObjectId.id];
    400             var func = InjectedScriptHost.evaluate("(" + expression + ")");
     141            let func = InjectedScriptHost.evaluate("(" + expression + ")");
    401142            if (typeof func !== "function")
    402143                return "Given expression does not evaluate to a function";
     
    404145            return {
    405146                wasThrown: false,
    406                 result: this._wrapObject(func.apply(object, resolvedArgs), objectGroup, returnByValue, generatePreview)
     147                result: RemoteObject.create(func.apply(object, resolvedArgs), objectGroupName, returnByValue, generatePreview)
    407148            };
    408149        } catch (e) {
    409             return this._createThrownValue(e, objectGroup);
    410         }
    411     },
    412 
    413     _resolveCallArgument: function(callArgumentJSON)
     150            return this._createThrownValue(e, objectGroupName);
     151        }
     152    }
     153
     154    getFunctionDetails(objectId)
     155    {
     156        let parsedObjectId = this._parseObjectId(objectId);
     157        let object = this._objectForId(parsedObjectId);
     158        if (typeof object !== "function")
     159            return "Cannot resolve function by id.";
     160        return this.functionDetails(object);
     161    }
     162
     163    functionDetails(func)
     164    {
     165        let details = InjectedScriptHost.functionDetails(func);
     166        if (!details)
     167            return "Cannot resolve function details.";
     168        return details;
     169    }
     170
     171    getPreview(objectId)
     172    {
     173        let parsedObjectId = this._parseObjectId(objectId);
     174        let object = this._objectForId(parsedObjectId);
     175        return RemoteObject.createObjectPreviewForValue(object, true);
     176    }
     177
     178    getProperties(objectId, ownProperties, generatePreview)
     179    {
     180        let nativeGettersAsValues = false;
     181        let collectionMode = ownProperties ? InjectedScript.CollectionMode.OwnProperties : InjectedScript.CollectionMode.AllProperties;
     182        return this._getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues);
     183    }
     184
     185    getDisplayableProperties(objectId, generatePreview)
     186    {
     187        let nativeGettersAsValues = true;
     188        let collectionMode = InjectedScript.CollectionMode.OwnProperties | InjectedScript.CollectionMode.NativeGetterProperties;
     189        return this._getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues);
     190    }
     191
     192    getInternalProperties(objectId, generatePreview)
     193    {
     194        let parsedObjectId = this._parseObjectId(objectId);
     195        let object = this._objectForId(parsedObjectId);
     196        let objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
     197
     198        if (!isDefined(object))
     199            return false;
     200
     201        if (isSymbol(object))
     202            return false;
     203
     204        let descriptors = this._internalPropertyDescriptors(object);
     205        if (!descriptors)
     206            return [];
     207
     208        for (let i = 0; i < descriptors.length; ++i) {
     209            let descriptor = descriptors[i];
     210            if ("value" in descriptor)
     211                descriptor.value = RemoteObject.create(descriptor.value, objectGroupName, false, generatePreview);
     212        }
     213
     214        return descriptors;
     215    }
     216
     217    getCollectionEntries(objectId, objectGroupName, startIndex, numberToFetch)
     218    {
     219        let parsedObjectId = this._parseObjectId(objectId);
     220        let object = this._objectForId(parsedObjectId);
     221        objectGroupName = objectGroupName || this._idToObjectGroupName[parsedObjectId.id];
     222
     223        if (!isDefined(object))
     224            return;
     225
     226        if (typeof object !== "object")
     227            return;
     228
     229        let entries = this._entries(object, InjectedScriptHost.subtype(object), startIndex, numberToFetch);
     230        return entries.map(function(entry) {
     231            entry.value = RemoteObject.create(entry.value, objectGroupName, false, true);
     232            if ("key" in entry)
     233                entry.key = RemoteObject.create(entry.key, objectGroupName, false, true);
     234            return entry;
     235        });
     236    }
     237
     238    saveResult(callArgumentJSON)
     239    {
     240        this._savedResultIndex = 0;
     241
     242        try {
     243            let callArgument = InjectedScriptHost.evaluate("(" + callArgumentJSON + ")");
     244            let value = this._resolveCallArgument(callArgument);
     245            this._saveResult(value);
     246        } catch (e) {}
     247
     248        return this._savedResultIndex;
     249    }
     250
     251    wrapCallFrames(callFrame)
     252    {
     253        if (!callFrame)
     254            return false;
     255
     256        let result = [];
     257        let depth = 0;
     258        do {
     259            result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
     260            callFrame = callFrame.caller;
     261        } while (callFrame);
     262        return result;
     263    }
     264
     265    wrapObject(object, groupName, canAccessInspectedGlobalObject, generatePreview)
     266    {
     267        if (!canAccessInspectedGlobalObject)
     268            return this._fallbackWrapper(object);
     269
     270        return RemoteObject.create(object, groupName, false, generatePreview);
     271    }
     272
     273    wrapTable(canAccessInspectedGlobalObject, table, columns)
     274    {
     275        if (!canAccessInspectedGlobalObject)
     276            return this._fallbackWrapper(table);
     277
     278        // FIXME: Currently columns are ignored. Instead, the frontend filters all
     279        // properties based on the provided column names and in the provided order.
     280        // We could filter here to avoid sending very large preview objects.
     281
     282        let columnNames = null;
     283        if (typeof columns === "string")
     284            columns = [columns];
     285
     286        if (InjectedScriptHost.subtype(columns) === "array") {
     287            columnNames = [];
     288            for (let i = 0; i < columns.length; ++i)
     289                columnNames.push(toString(columns[i]));
     290        }
     291
     292        return RemoteObject.create(table, "console", false, true, columnNames);
     293    }
     294
     295    previewValue(value)
     296    {
     297        return RemoteObject.createObjectPreviewForValue(value, true);
     298    }
     299
     300    setExceptionValue(value)
     301    {
     302        this._exceptionValue = value;
     303    }
     304
     305    clearExceptionValue()
     306    {
     307        delete this._exceptionValue;
     308    }
     309
     310    findObjectById(objectId)
     311    {
     312        let parsedObjectId = this._parseObjectId(objectId);
     313        return this._objectForId(parsedObjectId);
     314    }
     315
     316    inspectObject(object)
     317    {
     318        if (this._commandLineAPIImpl)
     319            this._commandLineAPIImpl.inspect(object);
     320    }
     321
     322    releaseObject(objectId)
     323    {
     324        let parsedObjectId = this._parseObjectId(objectId);
     325        this._releaseObject(parsedObjectId.id);
     326    }
     327
     328    releaseObjectGroup(objectGroupName)
     329    {
     330        if (objectGroupName === "console") {
     331            delete this._lastResult;
     332            this._nextSavedResultIndex = 1;
     333            this._savedResults = [];
     334        }
     335
     336        let group = this._objectGroups[objectGroupName];
     337        if (!group)
     338            return;
     339
     340        for (let i = 0; i < group.length; i++)
     341            this._releaseObject(group[i]);
     342
     343        delete this._objectGroups[objectGroupName];
     344    }
     345
     346    // InjectedScriptModule C++ API
     347
     348    module(name)
     349    {
     350        return this._modules[name];
     351    }
     352
     353    injectModule(name, source, host)
     354    {
     355        delete this._modules[name];
     356
     357        let moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
     358        if (typeof moduleFunction !== "function") {
     359            if (inspectedGlobalObject.console)
     360                inspectedGlobalObject.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
     361            return null;
     362        }
     363
     364        let module = moduleFunction.call(inspectedGlobalObject, InjectedScriptHost, inspectedGlobalObject, injectedScriptId, this, RemoteObject, host);
     365        this._modules[name] = module;
     366        return module;
     367    }
     368
     369    // InjectedScriptModule JavaScript API
     370
     371    isPrimitiveValue(value)
     372    {
     373        return isPrimitiveValue(value);
     374    }
     375
     376    // Private
     377
     378    _parseObjectId(objectId)
     379    {
     380        return InjectedScriptHost.evaluate("(" + objectId + ")");
     381    }
     382
     383    _objectForId(objectId)
     384    {
     385        return this._idToWrappedObject[objectId.id];
     386    }
     387
     388    _bind(object, objectGroupName)
     389    {
     390        let id = this._lastBoundObjectId++;
     391        let objectId = `{"injectedScriptId":${injectedScriptId},"id":${id}}`;
     392
     393        this._idToWrappedObject[id] = object;
     394
     395        if (objectGroupName) {
     396            let group = this._objectGroups[objectGroupName];
     397            if (!group) {
     398                group = [];
     399                this._objectGroups[objectGroupName] = group;
     400            }
     401            group.push(id);
     402            this._idToObjectGroupName[id] = objectGroupName;
     403        }
     404
     405        return objectId;
     406    }
     407
     408    _releaseObject(id)
     409    {
     410        delete this._idToWrappedObject[id];
     411        delete this._idToObjectGroupName[id];
     412    }
     413
     414    _fallbackWrapper(object)
     415    {
     416        let result = {};
     417        result.type = typeof object;
     418        if (isPrimitiveValue(object))
     419            result.value = object;
     420        else
     421            result.description = toString(object);
     422        return result;
     423    }
     424
     425    _resolveCallArgument(callArgumentJSON)
    414426    {
    415427        if ("value" in callArgumentJSON)
    416428            return callArgumentJSON.value;
    417429
    418         var objectId = callArgumentJSON.objectId;
     430        let objectId = callArgumentJSON.objectId;
    419431        if (objectId) {
    420             var parsedArgId = this._parseObjectId(objectId);
     432            let parsedArgId = this._parseObjectId(objectId);
    421433            if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
    422434                throw "Arguments should belong to the same JavaScript world as the target object.";
    423435
    424             var resolvedArg = this._objectForId(parsedArgId);
    425             if (!this._isDefined(resolvedArg))
     436            let resolvedArg = this._objectForId(parsedArgId);
     437            if (!isDefined(resolvedArg))
    426438                throw "Could not find object with given id";
    427439
     
    430442
    431443        return undefined;
    432     },
    433 
    434     _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
    435     {
    436         try {
    437             this._savedResultIndex = 0;
    438 
    439             var returnObject = {
    440                 wasThrown: false,
    441                 result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI, saveResult), objectGroup, returnByValue, generatePreview)
    442             };
    443 
    444             if (saveResult && this._savedResultIndex)
    445                 returnObject.savedResultIndex = this._savedResultIndex;
    446 
    447             return returnObject;
    448         } catch (e) {
    449             return this._createThrownValue(e, objectGroup);
    450         }
    451     },
    452 
    453     _createThrownValue: function(value, objectGroup)
    454     {
    455         var remoteObject = this._wrapObject(value, objectGroup);
     444    }
     445
     446    _createThrownValue(value, objectGroup)
     447    {
     448        let remoteObject = RemoteObject.create(value, objectGroup);
    456449        try {
    457450            remoteObject.description = toStringDescription(value);
     
    461454            result: remoteObject
    462455        };
    463     },
    464 
    465     _evaluateOn: function(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI, saveResult)
    466     {
    467         var commandLineAPI = null;
     456    }
     457
     458    _evaluateAndWrap(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
     459    {
     460        try {
     461            this._savedResultIndex = 0;
     462
     463            let returnObject = {
     464                wasThrown: false,
     465                result: RemoteObject.create(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI, saveResult), objectGroup, returnByValue, generatePreview)
     466            };
     467
     468            if (saveResult && this._savedResultIndex)
     469                returnObject.savedResultIndex = this._savedResultIndex;
     470
     471            return returnObject;
     472        } catch (e) {
     473            return this._createThrownValue(e, objectGroup);
     474        }
     475    }
     476
     477    _evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI, saveResult)
     478    {
     479        let commandLineAPI = null;
    468480        if (injectCommandLineAPI) {
    469481            if (this.CommandLineAPI)
     
    473485        }
    474486
    475         var result = evalFunction.call(object, expression, commandLineAPI);
     487        let result = evalFunction.call(object, expression, commandLineAPI);
    476488        if (saveResult)
    477489            this._saveResult(result);
    478490        return result;
    479     },
    480 
    481     wrapCallFrames: function(callFrame)
    482     {
    483         if (!callFrame)
    484             return false;
    485 
    486         var result = [];
    487         var depth = 0;
    488         do {
    489             result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
    490             callFrame = callFrame.caller;
    491         } while (callFrame);
    492         return result;
    493     },
    494 
    495     evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
    496     {
    497         var callFrame = this._callFrameForId(topCallFrame, callFrameId);
    498         if (!callFrame)
    499             return "Could not find call frame with given id";
    500         return this._evaluateAndWrap(callFrame.evaluateWithScopeExtension, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
    501     },
    502 
    503     _callFrameForId: function(topCallFrame, callFrameId)
    504     {
    505         var parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
    506         var ordinal = parsedCallFrameId["ordinal"];
    507         var callFrame = topCallFrame;
     491    }
     492
     493    _callFrameForId(topCallFrame, callFrameId)
     494    {
     495        let parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
     496        let ordinal = parsedCallFrameId["ordinal"];
     497        let callFrame = topCallFrame;
    508498        while (--ordinal >= 0 && callFrame)
    509499            callFrame = callFrame.caller;
    510500        return callFrame;
    511     },
    512 
    513     _objectForId: function(objectId)
    514     {
    515         return this._idToWrappedObject[objectId.id];
    516     },
    517 
    518     findObjectById: function(objectId)
    519     {
    520         var parsedObjectId = this._parseObjectId(objectId);
    521         return this._objectForId(parsedObjectId);
    522     },
    523 
    524     module: function(name)
    525     {
    526         return this._modules[name];
    527     },
    528 
    529     injectModule: function(name, source, host)
    530     {
    531         delete this._modules[name];
    532 
    533         var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
    534         if (typeof moduleFunction !== "function") {
    535             if (inspectedGlobalObject.console)
    536                 inspectedGlobalObject.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
    537             return null;
    538         }
    539 
    540         var module = moduleFunction.call(inspectedGlobalObject, InjectedScriptHost, inspectedGlobalObject, injectedScriptId, this, host);
    541         this._modules[name] = module;
    542         return module;
    543     },
    544 
    545     _internalPropertyDescriptors: function(object, completeDescriptor)
    546     {
    547         var internalProperties = InjectedScriptHost.getInternalProperties(object);
     501    }
     502
     503    _getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues)
     504    {
     505        let parsedObjectId = this._parseObjectId(objectId);
     506        let object = this._objectForId(parsedObjectId);
     507        let objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
     508
     509        if (!isDefined(object))
     510            return false;
     511
     512        if (isSymbol(object))
     513            return false;
     514
     515        let descriptors = this._propertyDescriptors(object, collectionMode, nativeGettersAsValues);
     516
     517        for (let i = 0; i < descriptors.length; ++i) {
     518            let descriptor = descriptors[i];
     519            if ("get" in descriptor)
     520                descriptor.get = RemoteObject.create(descriptor.get, objectGroupName);
     521            if ("set" in descriptor)
     522                descriptor.set = RemoteObject.create(descriptor.set, objectGroupName);
     523            if ("value" in descriptor)
     524                descriptor.value = RemoteObject.create(descriptor.value, objectGroupName, false, generatePreview);
     525            if (!("configurable" in descriptor))
     526                descriptor.configurable = false;
     527            if (!("enumerable" in descriptor))
     528                descriptor.enumerable = false;
     529            if ("symbol" in descriptor)
     530                descriptor.symbol = RemoteObject.create(descriptor.symbol, objectGroupName);
     531        }
     532
     533        return descriptors;
     534    }
     535
     536    _internalPropertyDescriptors(object, completeDescriptor)
     537    {
     538        let internalProperties = InjectedScriptHost.getInternalProperties(object);
    548539        if (!internalProperties)
    549540            return null;
    550541
    551         var descriptors = [];
    552         for (var i = 0; i < internalProperties.length; i++) {
    553             var property = internalProperties[i];
    554             var descriptor = {name: property.name, value: property.value};
     542        let descriptors = [];
     543        for (let i = 0; i < internalProperties.length; i++) {
     544            let property = internalProperties[i];
     545            let descriptor = {name: property.name, value: property.value};
    555546            if (completeDescriptor) {
    556547                descriptor.writable = false;
     
    562553        }
    563554        return descriptors;
    564     },
    565 
    566     _propertyDescriptors: function(object, collectionMode, nativeGettersAsValues)
     555    }
     556
     557    _propertyDescriptors(object, collectionMode, nativeGettersAsValues)
    567558    {
    568559        if (InjectedScriptHost.subtype(object) === "proxy")
    569560            return [];
    570561
    571         var descriptors = [];
    572         var nameProcessed = new Set;
     562        let descriptors = [];
     563        let nameProcessed = new Set;
    573564
    574565        function createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, possibleNativeBindingGetter)
    575566        {
    576567            try {
    577                 var descriptor = {name, value: object[name], writable: descriptor.writable || false, configurable: descriptor.configurable || false, enumerable: descriptor.enumerable || false};
     568                let fakeDescriptor = {name, value: object[name], writable: descriptor.writable || false, configurable: descriptor.configurable || false, enumerable: descriptor.enumerable || false};
    578569                if (possibleNativeBindingGetter)
    579                     descriptor.nativeGetter = true;
     570                    fakeDescriptor.nativeGetter = true;
    580571                if (isOwnProperty)
    581                     descriptor.isOwn = true;
     572                    fakeDescriptor.isOwn = true;
    582573                if (symbol)
    583                     descriptor.symbol = symbol;
     574                    fakeDescriptor.symbol = symbol;
    584575                // Silence any possible unhandledrejection exceptions created from accessing a native accessor with a wrong this object.
    585                 if (descriptor.value instanceof Promise)
    586                     descriptor.value.catch(function(){});
    587                 return descriptor;
     576                if (fakeDescriptor.value instanceof Promise)
     577                    fakeDescriptor.value.catch(function(){});
     578                return fakeDescriptor;
    588579            } catch (e) {
    589                 var errorDescriptor = {name, value: e, wasThrown: true};
     580                let errorDescriptor = {name, value: e, wasThrown: true};
    590581                if (isOwnProperty)
    591582                    errorDescriptor.isOwn = true;
     
    621612        function processProperties(o, properties, isOwnProperty)
    622613        {
    623             for (var i = 0; i < properties.length; ++i) {
    624                 var property = properties[i];
     614            for (let i = 0; i < properties.length; ++i) {
     615                let property = properties[i];
    625616                if (nameProcessed.has(property) || property === "__proto__")
    626617                    continue;
     
    628619                nameProcessed.add(property);
    629620
    630                 var name = toString(property);
    631                 var symbol = isSymbol(property) ? property : null;
    632 
    633                 var descriptor = Object.getOwnPropertyDescriptor(o, property);
     621                let name = toString(property);
     622                let symbol = isSymbol(property) ? property : null;
     623
     624                let descriptor = Object.getOwnPropertyDescriptor(o, property);
    634625                if (!descriptor) {
    635626                    // FIXME: Bad descriptor. Can we get here?
    636627                    // Fall back to very restrictive settings.
    637                     var fakeDescriptor = createFakeValueDescriptor(name, symbol, {writable: false, configurable: false, enumerable: false}, isOwnProperty);
     628                    let fakeDescriptor = createFakeValueDescriptor(name, symbol, {writable: false, configurable: false, enumerable: false}, isOwnProperty);
    638629                    processDescriptor(fakeDescriptor, isOwnProperty);
    639630                    continue;
     
    643634                    if (String(descriptor.get).endsWith("[native code]\n}") || (!descriptor.get && descriptor.hasOwnProperty("get") && !descriptor.set && descriptor.hasOwnProperty("set"))) {
    644635                        // Developers may create such a descriptor, so we should be resilient:
    645                         // var x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p")
    646                         var fakeDescriptor = createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, true);
     636                        // let x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p")
     637                        let fakeDescriptor = createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, true);
    647638                        processDescriptor(fakeDescriptor, isOwnProperty, true);
    648639                        continue;
     
    661652        function arrayIndexPropertyNames(o, length)
    662653        {
    663             var array = [];
    664             for (var i = 0; i < length; ++i) {
     654            let array = [];
     655            for (let i = 0; i < length; ++i) {
    665656                if (i in o)
    666657                    array.push("" + i);
     
    671662        // FIXME: <https://webkit.org/b/143589> Web Inspector: Better handling for large collections in Object Trees
    672663        // For array types with a large length we attempt to skip getOwnPropertyNames and instead just sublist of indexes.
    673         var isArrayLike = false;
     664        let isArrayLike = false;
    674665        try {
    675             isArrayLike = injectedScript._subtype(object) === "array" && isFinite(object.length) && object.length > 0;
     666            isArrayLike = RemoteObject.subtype(object) === "array" && isFinite(object.length) && object.length > 0;
    676667        } catch(e) {}
    677668
    678         for (var o = object; this._isDefined(o); o = Object.getPrototypeOf(o)) {
    679             var isOwnProperty = o === object;
     669        for (let o = object; isDefined(o); o = Object.getPrototypeOf(o)) {
     670            let isOwnProperty = o === object;
    680671
    681672            if (isArrayLike && isOwnProperty)
     
    698689
    699690        return descriptors;
    700     },
    701 
    702     _isDefined: function(object)
    703     {
    704         return !!object || this._isHTMLAllCollection(object);
    705     },
    706 
    707     _isHTMLAllCollection: function(object)
    708     {
    709         // document.all is reported as undefined, but we still want to process it.
    710         return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCollection(object);
    711     },
    712 
    713     _subtype: function(obj)
    714     {
    715         if (obj === null)
    716             return "null";
    717 
    718         if (this.isPrimitiveValue(obj) || isSymbol(obj))
    719             return null;
    720 
    721         if (this._isHTMLAllCollection(obj))
    722             return "array";
    723 
    724         var preciseType = InjectedScriptHost.subtype(obj);
    725         if (preciseType)
    726             return preciseType;
    727 
    728         // FireBug's array detection.
    729         try {
    730             if (typeof obj.splice === "function" && isFinite(obj.length))
    731                 return "array";
    732         } catch (e) {}
    733 
    734         return null;
    735     },
    736 
    737     _classPreview: function(classConstructorValue)
    738     {
    739         return "class " + classConstructorValue.name;
    740     },
    741 
    742     _nodePreview: function(node)
    743     {
    744         var isXMLDocument = node.ownerDocument && !!node.ownerDocument.xmlVersion;
    745         var nodeName = isXMLDocument ? node.nodeName : node.nodeName.toLowerCase();
    746 
    747         switch (node.nodeType) {
    748         case 1: // Node.ELEMENT_NODE
    749             if (node.id)
    750                 return "<" + nodeName + " id=\"" + node.id + "\">";
    751             if (node.classList.length)
    752                 return "<" + nodeName + " class=\"" + node.classList.toString().replace(/\s+/, " ") + "\">";
    753             if (nodeName === "input" && node.type)
    754                 return "<" + nodeName + " type=\"" + node.type + "\">";
    755             return "<" + nodeName + ">";
    756 
    757         case 3: // Node.TEXT_NODE
    758             return nodeName + " \"" + node.nodeValue + "\"";
    759 
    760         case 8: // Node.COMMENT_NODE
    761             return "<!--" + node.nodeValue + "-->";
    762 
    763         case 10: // Node.DOCUMENT_TYPE_NODE
    764             return "<!DOCTYPE " + nodeName + ">";
    765 
    766         default:
    767             return nodeName;
    768         }
    769     },
    770 
    771     _describe: function(obj)
    772     {
    773         if (this.isPrimitiveValue(obj))
    774             return null;
    775 
    776         if (isSymbol(obj))
    777             return toString(obj);
    778 
    779         var subtype = this._subtype(obj);
    780 
    781         if (subtype === "regexp")
    782             return toString(obj);
    783 
    784         if (subtype === "date")
    785             return toString(obj);
    786 
    787         if (subtype === "error")
    788             return toString(obj);
    789 
    790         if (subtype === "proxy")
    791             return "Proxy";
    792 
    793         if (subtype === "node")
    794             return this._nodePreview(obj);
    795 
    796         var className = InjectedScriptHost.internalConstructorName(obj);
    797         if (subtype === "array")
    798             return className;
    799 
    800         if (subtype === "iterator" && Symbol.toStringTag in obj)
    801             return obj[Symbol.toStringTag];
    802 
    803         // NodeList in JSC is a function, check for array prior to this.
    804         if (typeof obj === "function")
    805             return obj.toString();
    806 
    807         // If Object, try for a better name from the constructor.
    808         if (className === "Object") {
    809             var constructorName = obj.constructor && obj.constructor.name;
    810             if (constructorName)
    811                 return constructorName;
    812         }
    813 
    814         return className;
    815     },
    816 
    817     _getSetEntries: function(object, skip, numberToFetch)
    818     {
    819         var entries = [];
    820 
    821         for (var value of object) {
     691    }
     692
     693    _getSetEntries(object, skip, numberToFetch)
     694    {
     695        let entries = [];
     696
     697        // FIXME: This is observable if the page overrides Set.prototype[Symbol.iterator].
     698        for (let value of object) {
    822699            if (skip > 0) {
    823700                skip--;
     
    832709
    833710        return entries;
    834     },
    835 
    836     _getMapEntries: function(object, skip, numberToFetch)
    837     {
    838         var entries = [];
    839 
    840         for (var [key, value] of object) {
     711    }
     712
     713    _getMapEntries(object, skip, numberToFetch)
     714    {
     715        let entries = [];
     716
     717        // FIXME: This is observable if the page overrides Map.prototype[Symbol.iterator].
     718        for (let [key, value] of object) {
    841719            if (skip > 0) {
    842720                skip--;
     
    851729
    852730        return entries;
    853     },
    854 
    855     _getWeakMapEntries: function(object, numberToFetch)
     731    }
     732
     733    _getWeakMapEntries(object, numberToFetch)
    856734    {
    857735        return InjectedScriptHost.weakMapEntries(object, numberToFetch);
    858     },
    859 
    860     _getWeakSetEntries: function(object, numberToFetch)
     736    }
     737
     738    _getWeakSetEntries(object, numberToFetch)
    861739    {
    862740        return InjectedScriptHost.weakSetEntries(object, numberToFetch);
    863     },
    864 
    865     _getIteratorEntries: function(object, numberToFetch)
     741    }
     742
     743    _getIteratorEntries(object, numberToFetch)
    866744    {
    867745        return InjectedScriptHost.iteratorEntries(object, numberToFetch);
    868     },
    869 
    870     _entries: function(object, subtype, startIndex, numberToFetch)
     746    }
     747
     748    _entries(object, subtype, startIndex, numberToFetch)
    871749    {
    872750        if (subtype === "set")
     
    882760
    883761        throw "unexpected type";
    884     },
    885 
    886     _saveResult: function(result)
     762    }
     763
     764    _saveResult(result)
    887765    {
    888766        this._lastResult = result;
     
    891769            return;
    892770
    893         var existingIndex = this._savedResults.indexOf(result);
     771        let existingIndex = this._savedResults.indexOf(result);
    894772        if (existingIndex !== -1) {
    895773            this._savedResultIndex = existingIndex;
     
    903781        if (this._nextSavedResultIndex >= 100)
    904782            this._nextSavedResultIndex = 1;
    905     },
    906 
    907     _savedResult: function(index)
     783    }
     784
     785    _savedResult(index)
    908786    {
    909787        return this._savedResults[index];
     
    911789}
    912790
     791InjectedScript.CollectionMode = {
     792    OwnProperties: 1 << 0,          // own properties.
     793    NativeGetterProperties: 1 << 1, // native getter properties in the prototype chain.
     794    AllProperties: 1 << 2,          // all properties in the prototype chain.
     795};
     796
    913797var injectedScript = new InjectedScript;
    914798
    915 
    916 InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, generatePreview, columnNames)
     799// -------
     800
     801let RemoteObject = class RemoteObject
    917802{
    918     this.type = typeof object;
    919 
    920     if (this.type === "undefined" && injectedScript._isHTMLAllCollection(object))
    921         this.type = "object";
    922 
    923     if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
    924         // We don't send undefined values over JSON.
    925         if (this.type !== "undefined")
    926             this.value = object;
    927 
    928         // Null object is object with 'null' subtype.
    929         if (object === null)
    930             this.subtype = "null";
    931 
    932         // Provide user-friendly number values.
    933         if (this.type === "number")
    934             this.description = toStringDescription(object);
    935         return;
    936     }
    937 
    938     this.objectId = injectedScript._bind(object, objectGroupName);
    939 
    940     var subtype = injectedScript._subtype(object);
    941     if (subtype)
    942         this.subtype = subtype;
    943 
    944     this.className = InjectedScriptHost.internalConstructorName(object);
    945     this.description = injectedScript._describe(object);
    946 
    947     if (subtype === "array")
    948         this.size = typeof object.length === "number" ? object.length : 0;
    949     else if (subtype === "set" || subtype === "map")
    950         this.size = object.size;
    951     else if (subtype === "weakmap")
    952         this.size = InjectedScriptHost.weakMapSize(object);
    953     else if (subtype === "weakset")
    954         this.size = InjectedScriptHost.weakSetSize(object);
    955     else if (subtype === "class") {
    956         this.classPrototype = injectedScript._wrapObject(object.prototype, objectGroupName);
    957         this.className = object.name;
    958     }
    959 
    960     if (generatePreview && this.type === "object") {
    961         if (subtype === "proxy") {
    962             this.preview = this._generatePreview(InjectedScriptHost.proxyTargetValue(object));
    963             this.preview.lossless = false;
    964         } else
    965             this.preview = this._generatePreview(object, undefined, columnNames);
    966     }
    967 };
    968 
    969 InjectedScript.RemoteObject.createObjectPreviewForValue = function(value, generatePreview, columnNames)
    970 {
    971     var remoteObject = new InjectedScript.RemoteObject(value, undefined, false, generatePreview, columnNames);
    972     if (remoteObject.objectId)
    973         injectedScript.releaseObject(remoteObject.objectId);
    974     if (remoteObject.classPrototype && remoteObject.classPrototype.objectId)
    975         injectedScript.releaseObject(remoteObject.classPrototype.objectId);
    976     return remoteObject.preview || remoteObject._emptyPreview();
    977 };
    978 
    979 InjectedScript.RemoteObject.prototype = {
    980     _initialPreview: function()
    981     {
    982         var preview = {
     803    constructor(object, objectGroupName, forceValueType, generatePreview, columnNames)
     804    {
     805        this.type = typeof object;
     806
     807        if (this.type === "undefined" && InjectedScriptHost.isHTMLAllCollection(object))
     808            this.type = "object";
     809
     810        if (isPrimitiveValue(object) || object === null || forceValueType) {
     811            // We don't send undefined values over JSON.
     812            if (this.type !== "undefined")
     813                this.value = object;
     814
     815            // Null object is object with 'null' subtype.
     816            if (object === null)
     817                this.subtype = "null";
     818
     819            // Provide user-friendly number values.
     820            if (this.type === "number")
     821                this.description = toStringDescription(object);
     822            return;
     823        }
     824
     825        this.objectId = injectedScript._bind(object, objectGroupName);
     826
     827        let subtype = RemoteObject.subtype(object);
     828        if (subtype)
     829            this.subtype = subtype;
     830
     831        this.className = InjectedScriptHost.internalConstructorName(object);
     832        this.description = RemoteObject.describe(object);
     833
     834        if (subtype === "array")
     835            this.size = typeof object.length === "number" ? object.length : 0;
     836        else if (subtype === "set" || subtype === "map")
     837            this.size = object.size;
     838        else if (subtype === "weakmap")
     839            this.size = InjectedScriptHost.weakMapSize(object);
     840        else if (subtype === "weakset")
     841            this.size = InjectedScriptHost.weakSetSize(object);
     842        else if (subtype === "class") {
     843            this.classPrototype = RemoteObject.create(object.prototype, objectGroupName);
     844            this.className = object.name;
     845        }
     846
     847        if (generatePreview && this.type === "object") {
     848            if (subtype === "proxy") {
     849                this.preview = this._generatePreview(InjectedScriptHost.proxyTargetValue(object));
     850                this.preview.lossless = false;
     851            } else
     852                this.preview = this._generatePreview(object, undefined, columnNames);
     853        }
     854    }
     855
     856    // Static
     857
     858    static create(object, objectGroupName, forceValueType, generatePreview, columnNames)
     859    {
     860        try {
     861            return new RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
     862        } catch (e) {
     863            let description;
     864            try {
     865                description = RemoteObject.describe(e);
     866            } catch (ex) {
     867                alert(ex.message);
     868                description = "<failed to convert exception to string>";
     869            }
     870            return new RemoteObject(description);
     871        }
     872    }
     873
     874    static createObjectPreviewForValue(value, generatePreview, columnNames)
     875    {
     876        let remoteObject = new RemoteObject(value, undefined, false, generatePreview, columnNames);
     877        if (remoteObject.objectId)
     878            injectedScript.releaseObject(remoteObject.objectId);
     879        if (remoteObject.classPrototype && remoteObject.classPrototype.objectId)
     880            injectedScript.releaseObject(remoteObject.classPrototype.objectId);
     881        return remoteObject.preview || remoteObject._emptyPreview();
     882    }
     883
     884    static subtype(value)
     885    {
     886        if (value === null)
     887            return "null";
     888
     889        if (isPrimitiveValue(value) || isSymbol(value))
     890            return null;
     891
     892        if (InjectedScriptHost.isHTMLAllCollection(value))
     893            return "array";
     894
     895        let preciseType = InjectedScriptHost.subtype(value);
     896        if (preciseType)
     897            return preciseType;
     898
     899        // FireBug's array detection.
     900        try {
     901            if (typeof value.splice === "function" && isFinite(value.length))
     902                return "array";
     903        } catch (e) {}
     904
     905        return null;
     906    }
     907
     908    static describe(value)
     909    {
     910        if (isPrimitiveValue(value))
     911            return null;
     912
     913        if (isSymbol(value))
     914            return toString(value);
     915
     916        let subtype = RemoteObject.subtype(value);
     917
     918        if (subtype === "regexp")
     919            return toString(value);
     920
     921        if (subtype === "date")
     922            return toString(value);
     923
     924        if (subtype === "error")
     925            return toString(value);
     926
     927        if (subtype === "proxy")
     928            return "Proxy";
     929
     930        if (subtype === "node")
     931            return RemoteObject.nodePreview(value);
     932
     933        let className = InjectedScriptHost.internalConstructorName(value);
     934        if (subtype === "array")
     935            return className;
     936
     937        if (subtype === "iterator" && Symbol.toStringTag in value)
     938            return value[Symbol.toStringTag];
     939
     940        // NodeList in JSC is a function, check for array prior to this.
     941        if (typeof value === "function")
     942            return value.toString();
     943
     944        // If Object, try for a better name from the constructor.
     945        if (className === "Object") {
     946            let constructorName = value.constructor && value.constructor.name;
     947            if (constructorName)
     948                return constructorName;
     949        }
     950
     951        return className;
     952    }
     953
     954    static nodePreview(node)
     955    {
     956        let isXMLDocument = node.ownerDocument && !!node.ownerDocument.xmlVersion;
     957        let nodeName = isXMLDocument ? node.nodeName : node.nodeName.toLowerCase();
     958
     959        switch (node.nodeType) {
     960        case 1: // Node.ELEMENT_NODE
     961            if (node.id)
     962                return "<" + nodeName + " id=\"" + node.id + "\">";
     963            if (node.classList.length)
     964                return "<" + nodeName + " class=\"" + node.classList.toString().replace(/\s+/, " ") + "\">";
     965            if (nodeName === "input" && node.type)
     966                return "<" + nodeName + " type=\"" + node.type + "\">";
     967            return "<" + nodeName + ">";
     968
     969        case 3: // Node.TEXT_NODE
     970            return nodeName + " \"" + node.nodeValue + "\"";
     971
     972        case 8: // Node.COMMENT_NODE
     973            return "<!--" + node.nodeValue + "-->";
     974
     975        case 10: // Node.DOCUMENT_TYPE_NODE
     976            return "<!DOCTYPE " + nodeName + ">";
     977
     978        default:
     979            return nodeName;
     980        }
     981    }
     982
     983    // Private
     984
     985    _initialPreview()
     986    {
     987        let preview = {
    983988            type: this.type,
    984989            description: this.description || toString(this.value),
     
    9981003
    9991004        return preview;
    1000     },
    1001 
    1002     _emptyPreview: function()
    1003     {
    1004         var preview = this._initialPreview();
     1005    }
     1006
     1007    _emptyPreview()
     1008    {
     1009        let preview = this._initialPreview();
    10051010
    10061011        if (this.subtype === "map" || this.subtype === "set" || this.subtype === "weakmap" || this.subtype === "weakset" || this.subtype === "iterator") {
     
    10131018
    10141019        return preview;
    1015     },
    1016 
    1017     _generatePreview: function(object, firstLevelKeys, secondLevelKeys)
    1018     {
    1019         var preview = this._initialPreview();
    1020         var isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
    1021         var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
    1022 
    1023         var propertiesThreshold = {
     1020    }
     1021
     1022    _generatePreview(object, firstLevelKeys, secondLevelKeys)
     1023    {
     1024        let preview = this._initialPreview();
     1025        let isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
     1026        let firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
     1027
     1028        let propertiesThreshold = {
    10241029            properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount),
    10251030            indexes: isTableRowsRequest ? 1000 : Math.max(10, firstLevelKeysCount)
     
    10341039
    10351040            // Internal Properties.
    1036             var internalPropertyDescriptors = injectedScript._internalPropertyDescriptors(object, true);
     1041            let internalPropertyDescriptors = injectedScript._internalPropertyDescriptors(object, true);
    10371042            if (internalPropertyDescriptors) {
    10381043                this._appendPropertyPreviews(object, preview, internalPropertyDescriptors, true, propertiesThreshold, firstLevelKeys, secondLevelKeys);
     
    10451050
    10461051            // Properties.
    1047             var nativeGettersAsValues = true;
    1048             var descriptors = injectedScript._propertyDescriptors(object, InjectedScript.CollectionMode.AllProperties, nativeGettersAsValues);
     1052            let nativeGettersAsValues = true;
     1053            let descriptors = injectedScript._propertyDescriptors(object, InjectedScript.CollectionMode.AllProperties, nativeGettersAsValues);
    10491054            this._appendPropertyPreviews(object, preview, descriptors, false, propertiesThreshold, firstLevelKeys, secondLevelKeys);
    10501055            if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
     
    10551060
    10561061        return preview;
    1057     },
    1058 
    1059     _appendPropertyPreviews: function(object, preview, descriptors, internal, propertiesThreshold, firstLevelKeys, secondLevelKeys)
     1062    }
     1063
     1064    _appendPropertyPreviews(object, preview, descriptors, internal, propertiesThreshold, firstLevelKeys, secondLevelKeys)
    10601065    {
    10611066        for (let i = 0; i < descriptors.length; ++i) {
     
    10731078
    10741079            // Do not show "__proto__" in preview.
    1075             var name = descriptor.name;
     1080            let name = descriptor.name;
    10761081            if (name === "__proto__") {
    10771082                // Non basic __proto__ objects may have interesting, non-enumerable, methods to show.
     
    11071112
    11081113            // Null value.
    1109             var value = descriptor.value;
     1114            let value = descriptor.value;
    11101115            if (value === null) {
    11111116                this._appendPropertyPreview(preview, internal, {name, type: "object", subtype: "null", value: "null"}, propertiesThreshold);
     
    11141119
    11151120            // Ignore non-enumerable functions.
    1116             var type = typeof value;
     1121            let type = typeof value;
    11171122            if (!descriptor.enumerable && type === "function")
    11181123                continue;
    11191124
    11201125            // Fix type of document.all.
    1121             if (type === "undefined" && injectedScript._isHTMLAllCollection(value))
     1126            if (InjectedScriptHost.isHTMLAllCollection(value))
    11221127                type = "object";
    11231128
    11241129            // Primitive.
    11251130            const maxLength = 100;
    1126             if (InjectedScript.primitiveTypes[type]) {
     1131            if (isPrimitiveValue(value)) {
    11271132                if (type === "string" && value.length > maxLength) {
    11281133                    value = this._abbreviateString(value, maxLength, true);
     
    11351140            // Symbol.
    11361141            if (isSymbol(value)) {
    1137                 var symbolString = toString(value);
     1142                let symbolString = toString(value);
    11381143                if (symbolString.length > maxLength) {
    11391144                    symbolString = this._abbreviateString(symbolString, maxLength, true);
     
    11451150
    11461151            // Object.
    1147             var property = {name, type};
    1148             var subtype = injectedScript._subtype(value);
     1152            let property = {name, type};
     1153            let subtype = RemoteObject.subtype(value);
    11491154            if (subtype)
    11501155                property.subtype = subtype;
     
    11531158            if ((secondLevelKeys === null || secondLevelKeys) || this._isPreviewableObject(value, object)) {
    11541159                // FIXME: If we want secondLevelKeys filter to continue we would need some refactoring.
    1155                 var subPreview = InjectedScript.RemoteObject.createObjectPreviewForValue(value, value !== object, secondLevelKeys);
     1160                let subPreview = RemoteObject.createObjectPreviewForValue(value, value !== object, secondLevelKeys);
    11561161                property.valuePreview = subPreview;
    11571162                if (!subPreview.lossless)
     
    11601165                    preview.overflow = true;
    11611166            } else {
    1162                 var description = "";
     1167                let description = "";
    11631168                if (type !== "function" || subtype === "class") {
    1164                     var fullDescription;
     1169                    let fullDescription;
    11651170                    if (subtype === "class")
    11661171                        fullDescription = "class " + value.name;
    11671172                    else if (subtype === "node")
    1168                         fullDescription = injectedScript._nodePreview(value);
     1173                        fullDescription = RemoteObject.nodePreview(value);
    11691174                    else
    1170                         fullDescription = injectedScript._describe(value);
     1175                        fullDescription = RemoteObject.describe(value);
    11711176                    description = this._abbreviateString(fullDescription, maxLength, subtype === "regexp");
    11721177                }
     
    11771182            this._appendPropertyPreview(preview, internal, property, propertiesThreshold);
    11781183        }
    1179     },
    1180 
    1181     _appendPropertyPreview: function(preview, internal, property, propertiesThreshold)
     1184    }
     1185
     1186    _appendPropertyPreview(preview, internal, property, propertiesThreshold)
    11821187    {
    11831188        if (toString(property.name >>> 0) === property.name)
     
    11961201
    11971202        preview.properties.push(property);
    1198     },
    1199 
    1200     _appendEntryPreviews: function(object, preview)
     1203    }
     1204
     1205    _appendEntryPreviews(object, preview)
    12011206    {
    12021207        // Fetch 6, but only return 5, so we can tell if we overflowed.
    1203         var entries = injectedScript._entries(object, this.subtype, 0, 6);
     1208        let entries = injectedScript._entries(object, this.subtype, 0, 6);
    12041209        if (!entries)
    12051210            return;
     
    12171222
    12181223        preview.entries = entries.map(function(entry) {
    1219             entry.value = InjectedScript.RemoteObject.createObjectPreviewForValue(entry.value, entry.value !== object);
     1224            entry.value = RemoteObject.createObjectPreviewForValue(entry.value, entry.value !== object);
    12201225            updateMainPreview(entry.value);
    12211226            if ("key" in entry) {
    1222                 entry.key = InjectedScript.RemoteObject.createObjectPreviewForValue(entry.key, entry.key !== object);
     1227                entry.key = RemoteObject.createObjectPreviewForValue(entry.key, entry.key !== object);
    12231228                updateMainPreview(entry.key);
    12241229            }
    12251230            return entry;
    1226         }, this);
    1227     },
    1228 
    1229     _isPreviewableObject: function(value, object)
     1231        });
     1232    }
     1233
     1234    _isPreviewableObject(value, object)
    12301235    {
    12311236        let set = new Set;
     
    12331238
    12341239        return this._isPreviewableObjectInternal(value, set, 1);
    1235     },
    1236 
    1237     _isPreviewableObjectInternal: function(object, knownObjects, depth)
     1240    }
     1241
     1242    _isPreviewableObjectInternal(object, knownObjects, depth)
    12381243    {
    12391244        // Deep object.
     
    12421247
    12431248        // Primitive.
    1244         if (injectedScript.isPrimitiveValue(object) || isSymbol(object))
     1249        if (isPrimitiveValue(object) || isSymbol(object))
    12451250            return true;
    12461251
     
    12571262
    12581263        // Arrays are simple if they have 5 or less simple objects.
    1259         var subtype = injectedScript._subtype(object);
     1264        let subtype = RemoteObject.subtype(object);
    12601265        if (subtype === "array") {
    1261             var length = object.length;
     1266            let length = object.length;
    12621267            if (length > 5)
    12631268                return false;
    1264             for (var i = 0; i < length; ++i) {
     1269            for (let i = 0; i < length; ++i) {
    12651270                if (!this._isPreviewableObjectInternal(object[i], knownObjects, depth))
    12661271                    return false;
     
    12841289
    12851290        return true;
    1286     },
    1287 
    1288     _abbreviateString: function(string, maxLength, middle)
     1291    }
     1292
     1293    _abbreviateString(string, maxLength, middle)
    12891294    {
    12901295        if (string.length <= maxLength)
     
    12921297
    12931298        if (middle) {
    1294             var leftHalf = maxLength >> 1;
    1295             var rightHalf = maxLength - leftHalf - 1;
     1299            let leftHalf = maxLength >> 1;
     1300            let rightHalf = maxLength - leftHalf - 1;
    12961301            return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
    12971302        }
     
    13001305    }
    13011306}
     1307
     1308// -------
    13021309
    13031310InjectedScript.CallFrameProxy = function(ordinal, callFrame)
    13041311{
    1305     this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + injectedScriptId + "}";
     1312    this.callFrameId = `{"ordinal":${ordinal},"injectedScriptId":${injectedScriptId}}`;
    13061313    this.functionName = callFrame.functionName;
    13071314    this.location = {scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column};
    13081315    this.scopeChain = this._wrapScopeChain(callFrame);
    1309     this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace");
     1316    this.this = RemoteObject.create(callFrame.thisObject, "backtrace");
    13101317    this.isTailDeleted = callFrame.isTailDeleted;
    13111318}
    13121319
    13131320InjectedScript.CallFrameProxy.prototype = {
    1314     _wrapScopeChain: function(callFrame)
    1315     {
    1316         var scopeChain = callFrame.scopeChain;
    1317         var scopeDescriptions = callFrame.scopeDescriptions();
    1318 
    1319         var scopeChainProxy = [];
    1320         for (var i = 0; i < scopeChain.length; i++)
     1321    _wrapScopeChain(callFrame)
     1322    {
     1323        let scopeChain = callFrame.scopeChain;
     1324        let scopeDescriptions = callFrame.scopeDescriptions();
     1325
     1326        let scopeChainProxy = [];
     1327        for (let i = 0; i < scopeChain.length; i++)
    13211328            scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(scopeChain[i], scopeDescriptions[i], "backtrace");
    13221329        return scopeChainProxy;
     
    13371344{
    13381345    let scope = {
    1339         object: injectedScript._wrapObject(object, groupId),
     1346        object: RemoteObject.create(object, groupId),
    13401347        type: InjectedScript.CallFrameProxy._scopeTypeNames[type],
    13411348    };
     
    13531360}
    13541361
     1362// -------
    13551363
    13561364function bind(func, thisObject, ...outerArgs)
  • trunk/Source/WebCore/ChangeLog

    r219638 r219639  
     12017-07-18  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Modernize InjectedScriptSource
     4        https://bugs.webkit.org/show_bug.cgi?id=173890
     5
     6        Reviewed by Brian Burg.
     7
     8        Covered by existing tests.
     9
     10        * inspector/CommandLineAPIModuleSource.js:
     11        (CommandLineAPIImpl.prototype.copy):
     12        (CommandLineAPIImpl.prototype._inspect):
     13        Use RemoteObject, a new parameter.
     14
    1152017-07-18  Ryosuke Niwa  <rniwa@webkit.org>
    216
  • trunk/Source/WebCore/inspector/CommandLineAPIModuleSource.js

    r201168 r219639  
    2929//# sourceURL=__InjectedScript_CommandLineAPIModuleSource.js
    3030
    31 /**
    32  * @param {InjectedScriptHost} InjectedScriptHost
    33  * @param {Window} inspectedWindow
    34  * @param {number} injectedScriptId
    35  * @param {InjectedScript} injectedScript
    36  * @param {CommandLineAPIHost} CommandLineAPIHost
    37  */
    38 (function (InjectedScriptHost, inspectedWindow, injectedScriptId, injectedScript, CommandLineAPIHost) {
     31(function (InjectedScriptHost, inspectedWindow, injectedScriptId, injectedScript, RemoteObject, CommandLineAPIHost) {
    3932
    4033// FIXME: <https://webkit.org/b/152294> Web Inspector: Parse InjectedScriptSource as a built-in to get guaranteed non-user-overriden built-ins
     
    232225    {
    233226        var string;
    234         var subtype = injectedScript._subtype(object);
     227        var subtype = RemoteObject.subtype(object);
    235228        if (subtype === "node")
    236229            string = object.outerHTML;
     
    316309            return;
    317310
    318         var objectId = injectedScript._wrapObject(object, "");
     311        var objectId = RemoteObject.create(object, "");
    319312        var hints = {};
    320313
    321         switch (injectedScript._describe(object)) {
     314        switch (RemoteObject.describe(object)) {
    322315        case "Database":
    323316            var databaseId = CommandLineAPIHost.databaseId(object)
Note: See TracChangeset for help on using the changeset viewer.