Changeset 250087 in webkit


Ignore:
Timestamp:
Sep 18, 2019 10:58:27 PM (5 years ago)
Author:
Devin Rousso
Message:

Web Inspector: Better handling for large arrays and collections in Object Trees
https://bugs.webkit.org/show_bug.cgi?id=143589
<rdar://problem/16135388>

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

Adds two buttons before the "Prototype" item in expanded object/collection previews:

  • Show %d More
  • Show All (%d More)

The default fetchCount increment is 100. The first button will only be shown if there
are more than 100 items remaining (haven't been shown).

  • inspector/InjectedScriptSource.js:

(InjectedScript.prototype.getProperties):
(InjectedScript.prototype.getDisplayableProperties):
(InjectedScript.prototype.getCollectionEntries):
(InjectedScript.prototype._getProperties):
(InjectedScript.prototype._internalPropertyDescriptors):
(InjectedScript.prototype._propertyDescriptors):
(InjectedScript.prototype._propertyDescriptors.createFakeValueDescriptor):
(InjectedScript.prototype._propertyDescriptors.processProperties):
(InjectedScript.prototype._getSetEntries):
(InjectedScript.prototype._getMapEntries):
(InjectedScript.prototype._getWeakMapEntries):
(InjectedScript.prototype._getWeakSetEntries):
(InjectedScript.prototype._getIteratorEntries):
(InjectedScript.prototype._entries):
(RemoteObject.prototype._generatePreview):
(InjectedScript.prototype._propertyDescriptors.arrayIndexPropertyNames): Deleted.
Don't include boolean property descriptor values if they are `false.

  • inspector/JSInjectedScriptHost.cpp:

(Inspector::JSInjectedScriptHost::weakMapEntries):
(Inspector::JSInjectedScriptHost::weakSetEntries):

  • inspector/InjectedScript.h:
  • inspector/InjectedScript.cpp:

(Inspector::InjectedScript::getProperties):
(Inspector::InjectedScript::getDisplayableProperties):
(Inspector::InjectedScript::getCollectionEntries):

  • inspector/agents/InspectorRuntimeAgent.h:
  • inspector/agents/InspectorRuntimeAgent.cpp:

(Inspector::asInt): Added.
(Inspector::InspectorRuntimeAgent::getProperties):
(Inspector::InspectorRuntimeAgent::getDisplayableProperties):
(Inspector::InspectorRuntimeAgent::getCollectionEntries):

  • inspector/protocol/Runtime.json:

Add fetchStart/fetchCount to getProperties/getDisplayableProperties/getCollectionEntries.
Mark boolean properties as optional so they can be omitted if false.

Source/WebInspectorUI:

Adds two buttons before the "Prototype" item in expanded object/collection previews:

  • Show %d More
  • Show All (%d More)

The default fetchCount increment is 100. The first button will only be shown if there
are more than 100 items remaining (haven't been shown).

  • UserInterface/Protocol/RemoteObject.js:

(WI.RemoteObject.prototype.getPropertyDescriptors):
(WI.RemoteObject.prototype.getDisplayablePropertyDescriptors):
(WI.RemoteObject.prototype.getCollectionEntries):
(WI.RemoteObject.prototype.getOwnPropertyDescriptor.wrappedCallback):
(WI.RemoteObject.prototype._getProperties): Added.
(WI.RemoteObject.prototype._getDisplayableProperties): Added.

  • UserInterface/Views/ObjectTreeView.js:

(WI.ObjectTreeView):
(WI.ObjectTreeView.showMoreFetchCount): Added.
(WI.ObjectTreeView.addShowMoreIfNeeded): Added.
(WI.ObjectTreeView.prototype.update):
(WI.ObjectTreeView.prototype._updateChildren):
(WI.ObjectTreeView.prototype._updateEntries):
(WI.ObjectTreeView.prototype._updateProperties):

  • UserInterface/Views/ObjectTreeView.css:

(.tree-outline.object li > button[disabled] + .indeterminate-progress-spinner): Added.
Avoid duplicating the button creation logic in WI.ObjectTreePropertyTreeElement by using a
static function. This expects the existence of and requires access to "private" values.

  • UserInterface/Views/ObjectTreePropertyTreeElement.js:

(WI.ObjectTreePropertyTreeElement):
(WI.ObjectTreePropertyTreeElement.prototype.onpopulate):
(WI.ObjectTreePropertyTreeElement.prototype._updateChildren):
(WI.ObjectTreePropertyTreeElement.prototype._updateEntries):
(WI.ObjectTreePropertyTreeElement.prototype._updateProperties):
(WI.ObjectTreePropertyTreeElement.prototype._updateChildrenInternal): Deleted.

  • UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js:

(WI.JavaScriptRuntimeCompletionProvider.prototype.completionControllerCompletionsNeeded.evaluated):
(WI.JavaScriptRuntimeCompletionProvider.prototype.completionControllerCompletionsNeeded.receivedArrayPropertyNames): Deleted.

  • UserInterface/Controllers/RuntimeManager.js:

(WI.RuntimeManager.prototype.getPropertiesForRemoteObject): Deleted.
Remove unused function.

  • Localizations/en.lproj/localizedStrings.js:

LayoutTests:

  • inspector/runtime/getCollectionEntries.html: Added.
  • inspector/runtime/getCollectionEntries-expected.txt: Added.
  • inspector/runtime/getDisplayableProperties.html: Added.
  • inspector/runtime/getDisplayableProperties-expected.txt: Added.
  • inspector/runtime/getProperties.html:
  • inspector/runtime/getProperties-expected.txt:
  • inspector/runtime/resources/property-descriptor-utilities.js: Added.

(makeArray):
(makeObject):
(makeMap):
(makeSet):
(makeWeakMap):
(makeWeakSet):
(TestPage.registerInitializer.ProtocolTest.PropertyDescriptorUtilities.logForEach):
(TestPage.registerInitializer.ProtocolTest.PropertyDescriptorUtilities.stringifyRemoteObject):

  • inspector/model/remote-object-weak-collection.html:
Location:
trunk
Files:
5 added
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r250055 r250087  
     12019-09-18  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: Better handling for large arrays and collections in Object Trees
     4        https://bugs.webkit.org/show_bug.cgi?id=143589
     5        <rdar://problem/16135388>
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        * inspector/runtime/getCollectionEntries.html: Added.
     10        * inspector/runtime/getCollectionEntries-expected.txt: Added.
     11        * inspector/runtime/getDisplayableProperties.html: Added.
     12        * inspector/runtime/getDisplayableProperties-expected.txt: Added.
     13        * inspector/runtime/getProperties.html:
     14        * inspector/runtime/getProperties-expected.txt:
     15        * inspector/runtime/resources/property-descriptor-utilities.js: Added.
     16        (makeArray):
     17        (makeObject):
     18        (makeMap):
     19        (makeSet):
     20        (makeWeakMap):
     21        (makeWeakSet):
     22        (TestPage.registerInitializer.ProtocolTest.PropertyDescriptorUtilities.logForEach):
     23        (TestPage.registerInitializer.ProtocolTest.PropertyDescriptorUtilities.stringifyRemoteObject):
     24
     25        * inspector/model/remote-object-weak-collection.html:
     26
    1272019-09-18  Ryan Haddad  <ryanhaddad@apple.com>
    228
  • trunk/LayoutTests/inspector/model/remote-object-weak-collection.html

    r220119 r250087  
    5858            WI.runtimeManager.evaluateInInspectedWindow("GCController.collect()", {objectGroup: "test", doNotPauseOnExceptionsAndMuteConsole: true}, function() {
    5959                InspectorTest.assert(remoteObject instanceof WI.RemoteObject);
    60                 remoteObject.getCollectionEntries(0, 100, function(entries) {
     60                remoteObject.getCollectionEntries(function(entries) {
    6161                    InspectorTest.log("ENTRIES:");
    6262                    entries.sort((a, b) => a.value.value - b.value.value);
  • trunk/LayoutTests/inspector/runtime/getProperties-expected.txt

    r226489 r250087  
     1CONSOLE MESSAGE: Unhandled Promise Rejection: 123
     2Tests for the Runtime.getProperties command.
     3
    14
    25== Running test suite: Runtime.getProperties
    3 -- Running test case: CheckPropertiesOfWrapperObject
    4 Evaluating expression: (function(){var r = Object(5); r.foo = 'cat';return r;})()
    5 Properties:
    6   __proto__ object Number
    7   foo string cat
    8 
    9 -- Running test case: CheckPropertiesOfArray
    10 Evaluating expression: ['red', 'green', 'blue']
    11 Properties:
    12   __proto__ object Array
    13   0 string red
    14   1 string green
    15   2 string blue
    16 
    17 -- Running test case: CheckPropertiesOfBoundConstructor
    18 Evaluating expression: Number.bind({}, 5)
    19 Properties:
    20   __proto__ function function () {
    21     [native code]
    22 }
    23   length number 0
    24   name string bound Number
    25 Internal properties:
    26   boundArgs object Array
    27   boundThis object Object
    28   targetFunction function function Number() {
    29     [native code]
    30 }
    31 
    32 -- Running test case: CheckPropertiesOfBoundFunctionNoArguments
    33 Evaluating expression: (function(){}).bind(null)
    34 Properties:
    35   __proto__ function function () {
    36     [native code]
    37 }
    38   length number 0
    39   name string bound
    40 Internal properties:
    41   boundThis object undefined
    42   targetFunction function function (){}
    43 
     6-- Running test case: Runtime.getProperties.Object
     7Evaluating expression...
     8Getting own properties...
     9Properties:
     10    "foo"        =>  "cat" (string)  [writable | enumerable | configurable | isOwn]
     11    "__proto__"  =>  "Number" (object)  [writable | configurable | isOwn]
     12
     13-- Running test case: Runtime.getProperties.Array
     14Evaluating expression...
     15Getting own properties...
     16Properties:
     17    "0"          =>  "red" (string)  [writable | enumerable | configurable | isOwn]
     18    "1"          =>  "green" (string)  [writable | enumerable | configurable | isOwn]
     19    "2"          =>  "blue" (string)  [writable | enumerable | configurable | isOwn]
     20    "__proto__"  =>  "Array" (object array)  [writable | configurable | isOwn]
     21
     22-- Running test case: Runtime.getProperties.Constructor
     23Evaluating expression...
     24Getting own properties...
     25Properties:
     26    "length"     =>  0 (number)  [configurable | isOwn]
     27    "prototype"  =>  "Test" (object)  [isOwn]
     28    "name"       =>  "Test" (string)  [configurable | isOwn]
     29    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     30
     31-- Running test case: Runtime.getProperties.BoundConstructor
     32Evaluating expression...
     33Getting own properties...
     34Properties:
     35    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     36    "length"     =>  0 (number)  [configurable | isOwn]
     37    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     38Internal Properties:
     39    "targetFunction"  =>  "class Test { }" (function class)  []
     40    "boundThis"       =>  null (object null)  []
     41
     42-- Running test case: Runtime.getProperties.BoundConstructorArguments
     43Evaluating expression...
     44Getting own properties...
     45Properties:
     46    "name"       =>  "bound Test" (string)  [configurable | isOwn]
     47    "length"     =>  0 (number)  [configurable | isOwn]
     48    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     49Internal Properties:
     50    "targetFunction"  =>  "class Test { }" (function class)  []
     51    "boundThis"       =>  null (object null)  []
     52    "boundArgs"       =>  "Array" (object array)  []
     53
     54-- Running test case: Runtime.getProperties.Function
     55Evaluating expression...
     56Getting own properties...
     57Properties:
     58    "arguments"  =>  null (object null)  [isOwn]
     59    "caller"     =>  null (object null)  [isOwn]
     60    "length"     =>  3 (number)  [configurable | isOwn]
     61    "name"       =>  "" (string)  [configurable | isOwn]
     62    "prototype"  =>  "Object" (object)  [writable | isOwn]
     63    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     64
     65-- Running test case: Runtime.getProperties.FunctionNoParameters
     66Evaluating expression...
     67Getting own properties...
     68Properties:
     69    "arguments"  =>  null (object null)  [isOwn]
     70    "caller"     =>  null (object null)  [isOwn]
     71    "length"     =>  0 (number)  [configurable | isOwn]
     72    "name"       =>  "" (string)  [configurable | isOwn]
     73    "prototype"  =>  "Object" (object)  [writable | isOwn]
     74    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     75
     76-- Running test case: Runtime.getProperties.BoundFunction
     77Evaluating expression...
     78Getting own properties...
     79Properties:
     80    "name"       =>  "bound " (string)  [configurable | isOwn]
     81    "length"     =>  3 (number)  [configurable | isOwn]
     82    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     83Internal Properties:
     84    "targetFunction"  =>  "function (a, b, c){}" (function)  []
     85    "boundThis"       =>  null (object null)  []
     86
     87-- Running test case: Runtime.getProperties.BoundFunctionWithArguments
     88Evaluating expression...
     89Getting own properties...
     90Properties:
     91    "name"       =>  "bound " (string)  [configurable | isOwn]
     92    "length"     =>  0 (number)  [configurable | isOwn]
     93    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     94Internal Properties:
     95    "targetFunction"  =>  "function (a, b, c){}" (function)  []
     96    "boundThis"       =>  null (object null)  []
     97    "boundArgs"       =>  "Array" (object array)  []
     98
     99-- Running test case: Runtime.getProperties.BoundFunctionNoParameters
     100Evaluating expression...
     101Getting own properties...
     102Properties:
     103    "name"       =>  "bound " (string)  [configurable | isOwn]
     104    "length"     =>  0 (number)  [configurable | isOwn]
     105    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     106Internal Properties:
     107    "targetFunction"  =>  "function (){}" (function)  []
     108    "boundThis"       =>  null (object null)  []
     109
     110-- Running test case: Runtime.getProperties.BoundFunctionNoParametersWithArguments
     111Evaluating expression...
     112Getting own properties...
     113Properties:
     114    "name"       =>  "bound " (string)  [configurable | isOwn]
     115    "length"     =>  0 (number)  [configurable | isOwn]
     116    "__proto__"  =>  "function () {\n    [native code]\n}" (function)  [writable | configurable | isOwn]
     117Internal Properties:
     118    "targetFunction"  =>  "function (){}" (function)  []
     119    "boundThis"       =>  null (object null)  []
     120    "boundArgs"       =>  "Array" (object array)  []
     121
     122-- Running test case: Runtime.getProperties.Promise.Resolved
     123Evaluating expression...
     124Getting own properties...
     125Properties:
     126    "__proto__"  =>  "Promise" (object)  [writable | configurable | isOwn]
     127Internal Properties:
     128    "status"  =>  "resolved" (string)  []
     129    "result"  =>  123 (number)  []
     130
     131-- Running test case: Runtime.getProperties.Promise.Rejected
     132Evaluating expression...
     133Getting own properties...
     134Properties:
     135    "__proto__"  =>  "Promise" (object)  [writable | configurable | isOwn]
     136Internal Properties:
     137    "status"  =>  "rejected" (string)  []
     138    "result"  =>  123 (number)  []
     139
     140-- Running test case: Runtime.getProperties.fetchStart.Object
     141Evaluating expression...
     142Getting own properties with fetchStart 5...
     143Properties:
     144    "U"  =>  5 (number)  [writable | enumerable | configurable | isOwn]
     145    "G"  =>  6 (number)  [writable | enumerable | configurable | isOwn]
     146    "S"  =>  7 (number)  [writable | enumerable | configurable | isOwn]
     147    "I"  =>  8 (number)  [writable | enumerable | configurable | isOwn]
     148    "Q"  =>  9 (number)  [writable | enumerable | configurable | isOwn]
     149
     150-- Running test case: Runtime.getProperties.fetchCount.Object
     151Evaluating expression...
     152Getting own properties with fetchCount 5...
     153ASSERT: Should only get 5 properties.
     154Properties:
     155    "A"          =>  0 (number)  [writable | enumerable | configurable | isOwn]
     156    "Y"          =>  1 (number)  [writable | enumerable | configurable | isOwn]
     157    "C"          =>  2 (number)  [writable | enumerable | configurable | isOwn]
     158    "W"          =>  3 (number)  [writable | enumerable | configurable | isOwn]
     159    "E"          =>  4 (number)  [writable | enumerable | configurable | isOwn]
     160    "__proto__"  =>  "Object" (object)  [writable | configurable | isOwn]
     161
     162-- Running test case: Runtime.getProperties.fetchStartCount.Object
     163Evaluating expression...
     164Getting own properties with fetchStart 3 with fetchCount 4...
     165Properties:
     166    "W"  =>  3 (number)  [writable | enumerable | configurable | isOwn]
     167    "E"  =>  4 (number)  [writable | enumerable | configurable | isOwn]
     168    "U"  =>  5 (number)  [writable | enumerable | configurable | isOwn]
     169    "G"  =>  6 (number)  [writable | enumerable | configurable | isOwn]
     170
     171-- Running test case: Runtime.getProperties.fetchStart.Array
     172Evaluating expression...
     173Getting own properties with fetchStart 5...
     174Properties:
     175    "5"  =>  "U" (string)  [writable | enumerable | configurable | isOwn]
     176    "6"  =>  "G" (string)  [writable | enumerable | configurable | isOwn]
     177    "7"  =>  "S" (string)  [writable | enumerable | configurable | isOwn]
     178    "8"  =>  "I" (string)  [writable | enumerable | configurable | isOwn]
     179    "9"  =>  "Q" (string)  [writable | enumerable | configurable | isOwn]
     180
     181-- Running test case: Runtime.getProperties.fetchCount.Array
     182Evaluating expression...
     183Getting own properties with fetchCount 5...
     184ASSERT: Should only get 5 properties.
     185Properties:
     186    "0"          =>  "A" (string)  [writable | enumerable | configurable | isOwn]
     187    "1"          =>  "Y" (string)  [writable | enumerable | configurable | isOwn]
     188    "2"          =>  "C" (string)  [writable | enumerable | configurable | isOwn]
     189    "3"          =>  "W" (string)  [writable | enumerable | configurable | isOwn]
     190    "4"          =>  "E" (string)  [writable | enumerable | configurable | isOwn]
     191    "__proto__"  =>  "Array" (object array)  [writable | configurable | isOwn]
     192
     193-- Running test case: Runtime.getProperties.fetchStartCount.Array
     194Evaluating expression...
     195Getting own properties with fetchStart 3 with fetchCount 4...
     196Properties:
     197    "3"  =>  "W" (string)  [writable | enumerable | configurable | isOwn]
     198    "4"  =>  "E" (string)  [writable | enumerable | configurable | isOwn]
     199    "5"  =>  "U" (string)  [writable | enumerable | configurable | isOwn]
     200    "6"  =>  "G" (string)  [writable | enumerable | configurable | isOwn]
     201
  • trunk/LayoutTests/inspector/runtime/getProperties.html

    r210062 r250087  
    22<head>
    33<script src="../../http/tests/inspector/resources/protocol-test.js"></script>
     4<script src="resources/property-descriptor-utilities.js"></script>
    45<script>
    56function test()
    67{
    7     var suite = ProtocolTest.createAsyncSuite("Runtime.getProperties");
     8    let suite = ProtocolTest.createAsyncSuite("Runtime.getProperties");
    89
    9     addGetPropertiesTestCase({
    10         name: "CheckPropertiesOfWrapperObject",
    11         description: "Check properties of `Object(5)`.",
    12         expression: "(function(){var r = Object(5); r.foo = 'cat';return r;})()",
    13     });
    14 
    15     addGetPropertiesTestCase({
    16         name: "CheckPropertiesOfArray",
    17         description: "Check properties of `['red', 'green', 'blue']`.",
    18         expression: "['red', 'green', 'blue']",
    19     });
    20 
    21     addGetPropertiesTestCase({
    22         name: "CheckPropertiesOfBoundConstructor",
    23         description: "Check properties of a bound function (has a bunch of internal properties).",
    24         expression: "Number.bind({}, 5)",
    25     });
    26 
    27     addGetPropertiesTestCase({
    28         name: "CheckPropertiesOfBoundFunctionNoArguments",
    29         description: "Check properties of a bound function with no bound arguments.",
    30         expression: "(function(){}).bind(null)",
    31     });
    32 
    33     suite.runTestCasesAndFinish();
    34 
    35     function addGetPropertiesTestCase(args) {
    36         var {name, description, expression} = args;
    37 
     10    function addTestCase({name, expression, ownProperties, fetchStart, fetchCount}) {
    3811        suite.addTestCase({
    3912            name,
    40             description,
    41             test(resolve, reject) {
    42                 ProtocolTest.log("Evaluating expression: " + expression);
    43                 InspectorProtocol.awaitCommand({
     13            async test() {
     14                ProtocolTest.log(`Evaluating expression...`);
     15                let evaluateResponse = await InspectorProtocol.awaitCommand({
    4416                    method: "Runtime.evaluate",
    45                     params: {expression}
    46                 })
    47                 .then(function(reply) {
    48                     var objectId = reply.result.objectId;
    49                     if (objectId === undefined)
    50                         throw new Error("objectId is expected");
     17                    params: {expression},
     18                });
     19                ProtocolTest.assert(!evaluateResponse.wasThrown);
    5120
    52                     return InspectorProtocol.awaitCommand({
    53                         method: "Runtime.getProperties",
    54                         params: {objectId, ownProperties: true}
    55                     });
    56                 })
    57                 .then(function(reply) {
    58                     dumpGetPropertiesResult(reply);
    59                     resolve();
    60                 })
    61                 .catch(reject);
    62             }
     21                let getPropertiesPreLog = "Getting";
     22                if (ownProperties)
     23                    getPropertiesPreLog += " own";
     24                getPropertiesPreLog += " properties";
     25                if (fetchStart)
     26                    getPropertiesPreLog += ` with fetchStart ${fetchStart}`;
     27                if (fetchCount)
     28                    getPropertiesPreLog += ` with fetchCount ${fetchCount}`;
     29                ProtocolTest.log(getPropertiesPreLog + "...");
     30
     31                let getPropertiesResponse = await InspectorProtocol.awaitCommand({
     32                    method: "Runtime.getProperties",
     33                    params: {
     34                        objectId: evaluateResponse.result.objectId,
     35                        ownProperties,
     36                        fetchStart,
     37                        fetchCount,
     38                    },
     39                });
     40
     41                let properties = getPropertiesResponse.properties;
     42                if (properties) {
     43                    ProtocolTest.assert(!fetchCount || properties.length <= fetchCount, `Should only get ${fetchCount} properties.`);
     44
     45                    ProtocolTest.log("Properties:");
     46                    properties.forEach(ProtocolTest.PropertyDescriptorUtilities.logForEach);
     47                }
     48
     49                let internalProperties = getPropertiesResponse.internalProperties;
     50                if (internalProperties) {
     51                    ProtocolTest.log("Internal Properties:");
     52                    internalProperties.forEach(ProtocolTest.PropertyDescriptorUtilities.logForEach);
     53                }
     54            },
    6355        });
    6456    }
    6557
    66     // A helper function that dumps object properties and internal properties in sorted order.
    67     function dumpGetPropertiesResult(protocolResult) {
    68         var properties = protocolResult.result;
    69         if (properties) {
    70             ProtocolTest.log("Properties:");
    71             properties.sort(NamedThingComparator);
    72             properties.map(dumpSingleProperty);
    73         }
     58    addTestCase({
     59        name: "Runtime.getProperties.Object",
     60        expression: `(function() { let r = Object(5); r.foo = "cat"; return r; })()`,
     61        ownProperties: true,
     62    });
    7463
    75         var internalProperties = protocolResult.internalProperties;
    76         if (internalProperties) {
    77             ProtocolTest.log("Internal properties:");
    78             internalProperties.sort(NamedThingComparator);
    79             internalProperties.map(dumpSingleProperty);
    80         }
     64    addTestCase({
     65        name: "Runtime.getProperties.Array",
     66        expression: `['red', 'green', 'blue']`,
     67        ownProperties: true,
     68    });
    8169
    82         function dumpSingleProperty(property) {
    83             var {name, value, get, set} = property;
    84             if (value)
    85                 ProtocolTest.log(`  ${name} ${value.type} ${value.value || value.description}`);
    86             else if (get || set)
    87                 ProtocolTest.log(`  ${name} ${get ? "getter" : "-"} ${set ? "setter" : ""}`);
    88             else
    89                 ProtocolTest.log(`  ${name}`);
    90         }
     70    addTestCase({
     71        name: "Runtime.getProperties.Constructor",
     72        expression: `(class Test { })`,
     73        ownProperties: true,
     74    });
    9175
    92         function NamedThingComparator(o1, o2) {
    93             return o1.name.localeCompare(o2.name);
    94         }
     76    addTestCase({
     77        name: "Runtime.getProperties.BoundConstructor",
     78        expression: `(class Test { }).bind(null)`,
     79        ownProperties: true,
     80    });
     81
     82    addTestCase({
     83        name: "Runtime.getProperties.BoundConstructorArguments",
     84        expression: `(class Test { }).bind(null, 1, 2, 3)`,
     85        ownProperties: true,
     86    });
     87
     88    addTestCase({
     89        name: "Runtime.getProperties.Function",
     90        expression: `(function(a, b, c){})`,
     91        ownProperties: true,
     92    });
     93
     94    addTestCase({
     95        name: "Runtime.getProperties.FunctionNoParameters",
     96        expression: `(function(){})`,
     97        ownProperties: true,
     98    });
     99
     100    addTestCase({
     101        name: "Runtime.getProperties.BoundFunction",
     102        expression: `(function(a, b, c){}).bind(null)`,
     103        ownProperties: true,
     104    });
     105
     106    addTestCase({
     107        name: "Runtime.getProperties.BoundFunctionWithArguments",
     108        expression: `(function(a, b, c){}).bind(null, 1, 2, 3)`,
     109        ownProperties: true,
     110    });
     111
     112    addTestCase({
     113        name: "Runtime.getProperties.BoundFunctionNoParameters",
     114        expression: `(function(){}).bind(null)`,
     115        ownProperties: true,
     116    });
     117
     118    addTestCase({
     119        name: "Runtime.getProperties.BoundFunctionNoParametersWithArguments",
     120        expression: `(function(){}).bind(null, 1, 2, 3)`,
     121        ownProperties: true,
     122    });
     123
     124    addTestCase({
     125        name: "Runtime.getProperties.Promise.Resolved",
     126        expression: `Promise.resolve(123)`,
     127        ownProperties: true,
     128    });
     129
     130    addTestCase({
     131        name: "Runtime.getProperties.Promise.Rejected",
     132        expression: `Promise.reject(123)`,
     133        ownProperties: true,
     134    });
     135
     136    for (let type of ["Object", "Array"]) {
     137        addTestCase({
     138            name: `Runtime.getProperties.fetchStart.${type}`,
     139            expression: `make${type}(10)`,
     140            ownProperties: true,
     141            fetchStart: 5,
     142        });
     143
     144        addTestCase({
     145            name: `Runtime.getProperties.fetchCount.${type}`,
     146            expression: `make${type}(10)`,
     147            ownProperties: true,
     148            fetchCount: 5,
     149        });
     150
     151        addTestCase({
     152            name: `Runtime.getProperties.fetchStartCount.${type}`,
     153            expression: `make${type}(10)`,
     154            ownProperties: true,
     155            fetchStart: 3,
     156            fetchCount: 4,
     157        });
    95158    }
     159
     160    suite.runTestCasesAndFinish();
    96161}
    97162</script>
    98163</head>
    99164<body onLoad="runTest()">
     165<p>Tests for the Runtime.getProperties command.</p>
    100166</body>
    101167</html>
  • trunk/Source/JavaScriptCore/ChangeLog

    r250086 r250087  
     12019-09-18  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: Better handling for large arrays and collections in Object Trees
     4        https://bugs.webkit.org/show_bug.cgi?id=143589
     5        <rdar://problem/16135388>
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        Adds two buttons before the "Prototype" item in expanded object/collection previews:
     10         - Show %d More
     11         - Show All (%d More)
     12
     13        The default `fetchCount` increment is `100`. The first button will only be shown if there
     14        are more than `100` items remaining (haven't been shown).
     15
     16        * inspector/InjectedScriptSource.js:
     17        (InjectedScript.prototype.getProperties):
     18        (InjectedScript.prototype.getDisplayableProperties):
     19        (InjectedScript.prototype.getCollectionEntries):
     20        (InjectedScript.prototype._getProperties):
     21        (InjectedScript.prototype._internalPropertyDescriptors):
     22        (InjectedScript.prototype._propertyDescriptors):
     23        (InjectedScript.prototype._propertyDescriptors.createFakeValueDescriptor):
     24        (InjectedScript.prototype._propertyDescriptors.processProperties):
     25        (InjectedScript.prototype._getSetEntries):
     26        (InjectedScript.prototype._getMapEntries):
     27        (InjectedScript.prototype._getWeakMapEntries):
     28        (InjectedScript.prototype._getWeakSetEntries):
     29        (InjectedScript.prototype._getIteratorEntries):
     30        (InjectedScript.prototype._entries):
     31        (RemoteObject.prototype._generatePreview):
     32        (InjectedScript.prototype._propertyDescriptors.arrayIndexPropertyNames): Deleted.
     33        Don't include boolean property descriptor values if they are `false.
     34
     35        * inspector/JSInjectedScriptHost.cpp:
     36        (Inspector::JSInjectedScriptHost::weakMapEntries):
     37        (Inspector::JSInjectedScriptHost::weakSetEntries):
     38
     39        * inspector/InjectedScript.h:
     40        * inspector/InjectedScript.cpp:
     41        (Inspector::InjectedScript::getProperties):
     42        (Inspector::InjectedScript::getDisplayableProperties):
     43        (Inspector::InjectedScript::getCollectionEntries):
     44
     45        * inspector/agents/InspectorRuntimeAgent.h:
     46        * inspector/agents/InspectorRuntimeAgent.cpp:
     47        (Inspector::asInt): Added.
     48        (Inspector::InspectorRuntimeAgent::getProperties):
     49        (Inspector::InspectorRuntimeAgent::getDisplayableProperties):
     50        (Inspector::InspectorRuntimeAgent::getCollectionEntries):
     51
     52        * inspector/protocol/Runtime.json:
     53        Add `fetchStart`/`fetchCount` to `getProperties`/`getDisplayableProperties`/`getCollectionEntries`.
     54        Mark boolean properties as optional so they can be omitted if `false`.
     55
    1562019-09-18  Joonghun Park  <pjh0718@gmail.com>
    257
  • trunk/Source/JavaScriptCore/inspector/InjectedScript.cpp

    r243161 r250087  
    165165}
    166166
    167 void InjectedScript::getProperties(ErrorString& errorString, const String& objectId, bool ownProperties, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties)
    168 {
     167void InjectedScript::getProperties(ErrorString& errorString, const String& objectId, bool ownProperties, int fetchStart, int fetchCount, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties)
     168{
     169    ASSERT(fetchStart >= 0);
     170    ASSERT(fetchCount >= 0);
     171
    169172    Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getProperties"_s, inspectorEnvironment()->functionCallHandler());
    170173    function.appendArgument(objectId);
    171174    function.appendArgument(ownProperties);
     175    function.appendArgument(fetchStart);
     176    function.appendArgument(fetchCount);
    172177    function.appendArgument(generatePreview);
    173178
     
    181186}
    182187
    183 void InjectedScript::getDisplayableProperties(ErrorString& errorString, const String& objectId, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties)
    184 {
     188void InjectedScript::getDisplayableProperties(ErrorString& errorString, const String& objectId, int fetchStart, int fetchCount, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties)
     189{
     190    ASSERT(fetchStart >= 0);
     191    ASSERT(fetchCount >= 0);
     192
    185193    Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getDisplayableProperties"_s, inspectorEnvironment()->functionCallHandler());
    186194    function.appendArgument(objectId);
     195    function.appendArgument(fetchStart);
     196    function.appendArgument(fetchCount);
    187197    function.appendArgument(generatePreview);
    188198
     
    212222}
    213223
    214 void InjectedScript::getCollectionEntries(ErrorString& errorString, const String& objectId, const String& objectGroup, int startIndex, int numberToFetch, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries)
    215 {
     224void InjectedScript::getCollectionEntries(ErrorString& errorString, const String& objectId, const String& objectGroup, int fetchStart, int fetchCount, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries)
     225{
     226    ASSERT(fetchStart >= 0);
     227    ASSERT(fetchCount >= 0);
     228
    216229    Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getCollectionEntries"_s, inspectorEnvironment()->functionCallHandler());
    217230    function.appendArgument(objectId);
    218231    function.appendArgument(objectGroup);
    219     function.appendArgument(startIndex);
    220     function.appendArgument(numberToFetch);
     232    function.appendArgument(fetchStart);
     233    function.appendArgument(fetchCount);
    221234
    222235    RefPtr<JSON::Value> result = makeCall(function);
  • trunk/Source/JavaScriptCore/inspector/InjectedScript.h

    r243161 r250087  
    6969    void functionDetails(ErrorString&, JSC::JSValue, RefPtr<Protocol::Debugger::FunctionDetails>& result);
    7070    void getPreview(ErrorString&, const String& objectId, RefPtr<Protocol::Runtime::ObjectPreview>& result);
    71     void getProperties(ErrorString&, const String& objectId, bool ownProperties, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result);
    72     void getDisplayableProperties(ErrorString&, const String& objectId, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result);
     71    void getProperties(ErrorString&, const String& objectId, bool ownProperties, int fetchStart, int fetchCount, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result);
     72    void getDisplayableProperties(ErrorString&, const String& objectId, int fetchStart, int fetchCount, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result);
    7373    void getInternalProperties(ErrorString&, const String& objectId, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& result);
    74     void getCollectionEntries(ErrorString&, const String& objectId, const String& objectGroup, int startIndex, int numberToFetch, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries);
     74    void getCollectionEntries(ErrorString&, const String& objectId, const String& objectGroup, int fetchStart, int fetchCount, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries);
    7575    void saveResult(ErrorString&, const String& callArgumentJSON, Optional<int>& savedResultIndex);
    7676
  • trunk/Source/JavaScriptCore/inspector/InjectedScriptSource.js

    r249445 r250087  
    231231    }
    232232
    233     getProperties(objectId, ownProperties, generatePreview)
    234     {
    235         let nativeGettersAsValues = false;
     233    getProperties(objectId, ownProperties, fetchStart, fetchCount, generatePreview)
     234    {
    236235        let collectionMode = ownProperties ? InjectedScript.CollectionMode.OwnProperties : InjectedScript.CollectionMode.AllProperties;
    237         return this._getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues);
    238     }
    239 
    240     getDisplayableProperties(objectId, generatePreview)
    241     {
    242         let nativeGettersAsValues = true;
     236        return this._getProperties(objectId, collectionMode, {fetchStart, fetchCount, generatePreview});
     237    }
     238
     239    getDisplayableProperties(objectId, fetchStart, fetchCount, generatePreview)
     240    {
    243241        let collectionMode = InjectedScript.CollectionMode.OwnProperties | InjectedScript.CollectionMode.NativeGetterProperties;
    244         return this._getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues);
     242        return this._getProperties(objectId, collectionMode, {fetchStart, fetchCount, generatePreview, nativeGettersAsValues: true});
    245243    }
    246244
     
    270268    }
    271269
    272     getCollectionEntries(objectId, objectGroupName, startIndex, numberToFetch)
     270    getCollectionEntries(objectId, objectGroupName, fetchStart, fetchCount)
    273271    {
    274272        let parsedObjectId = this._parseObjectId(objectId);
     
    282280            return;
    283281
    284         let entries = this._entries(object, InjectedScriptHost.subtype(object), startIndex, numberToFetch);
     282        let entries = this._entries(object, InjectedScriptHost.subtype(object), fetchStart, fetchCount);
    285283        return entries.map(function(entry) {
    286284            entry.value = RemoteObject.create(entry.value, objectGroupName, false, true);
     
    582580    }
    583581
    584     _getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues)
     582    _getProperties(objectId, collectionMode, {fetchStart, fetchCount, generatePreview, nativeGettersAsValues})
    585583    {
    586584        let parsedObjectId = this._parseObjectId(objectId);
     
    594592            return false;
    595593
    596         let descriptors = this._propertyDescriptors(object, collectionMode, nativeGettersAsValues);
     594        let descriptors = this._propertyDescriptors(object, collectionMode, {fetchStart, fetchCount, nativeGettersAsValues});
    597595
    598596        for (let i = 0; i < descriptors.length; ++i) {
     
    604602            if ("value" in descriptor)
    605603                descriptor.value = RemoteObject.create(descriptor.value, objectGroupName, false, generatePreview);
    606             if (!("configurable" in descriptor))
    607                 descriptor.configurable = false;
    608             if (!("enumerable" in descriptor))
    609                 descriptor.enumerable = false;
    610604            if ("symbol" in descriptor)
    611605                descriptor.symbol = RemoteObject.create(descriptor.symbol, objectGroupName);
     
    625619            let property = internalProperties[i];
    626620            let descriptor = {name: property.name, value: property.value};
    627             if (completeDescriptor) {
    628                 descriptor.writable = false;
    629                 descriptor.configurable = false;
    630                 descriptor.enumerable = false;
     621            if (completeDescriptor)
    631622                descriptor.isOwn = true;
    632             }
    633623            descriptors.push(descriptor);
    634624        }
     
    636626    }
    637627
    638     _propertyDescriptors(object, collectionMode, nativeGettersAsValues)
     628    _propertyDescriptors(object, collectionMode, {fetchStart, fetchCount, nativeGettersAsValues})
    639629    {
    640630        if (InjectedScriptHost.subtype(object) === "proxy")
     
    643633        let descriptors = [];
    644634        let nameProcessed = new Set;
     635
     636        let start = fetchStart || 0;
     637        if (start < 0)
     638            start = 0;
     639
     640        let count = fetchCount || 0;
     641        if (count < 0)
     642            count = 0;
     643        else if (count > 0 && start > 0)
     644            count += start;
    645645
    646646        function createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, possibleNativeBindingGetter)
    647647        {
    648648            try {
    649                 let fakeDescriptor = {name, value: object[name], writable: descriptor.writable || false, configurable: descriptor.configurable || false, enumerable: descriptor.enumerable || false};
     649                let fakeDescriptor = {name, value: object[name]};
     650                if (descriptor) {
     651                    if (descriptor.writable)
     652                        fakeDescriptor.writable = true;
     653                    if (descriptor.configurable)
     654                        fakeDescriptor.configurable = true;
     655                    if (descriptor.enumerable)
     656                        fakeDescriptor.enumerable = true;
     657                }
    650658                if (possibleNativeBindingGetter)
    651659                    fakeDescriptor.nativeGetter = true;
     
    694702        {
    695703            for (let i = 0; i < properties.length; ++i) {
     704                if (count && descriptors.length === count)
     705                    break;
     706
    696707                let property = properties[i];
    697708                if (nameProcessed.has(property) || property === "__proto__")
     
    707718                    // FIXME: Bad descriptor. Can we get here?
    708719                    // Fall back to very restrictive settings.
    709                     let fakeDescriptor = createFakeValueDescriptor(name, symbol, {writable: false, configurable: false, enumerable: false}, isOwnProperty);
     720                    let fakeDescriptor = createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty);
    710721                    processDescriptor(fakeDescriptor, isOwnProperty);
    711722                    continue;
     
    741752        }
    742753
    743         // FIXME: <https://webkit.org/b/143589> Web Inspector: Better handling for large collections in Object Trees
    744         // For array types with a large length we attempt to skip getOwnPropertyNames and instead just sublist of indexes.
    745754        let isArrayLike = false;
    746755        try {
     
    751760            let isOwnProperty = o === object;
    752761
     762            // FIXME: <https://webkit.org/b/201861> Web Inspector: show autocomplete entries for non-index properties on arrays
    753763            if (isArrayLike && isOwnProperty)
    754                 processProperties(o, arrayIndexPropertyNames(o, Math.min(object.length, 100)), isOwnProperty);
    755             else {
     764                processProperties(o, arrayIndexPropertyNames(o, object.length), isOwnProperty);
     765            else
    756766                processProperties(o, Object.getOwnPropertyNames(o), isOwnProperty);
    757                 if (Object.getOwnPropertySymbols)
    758                     processProperties(o, Object.getOwnPropertySymbols(o), isOwnProperty);
     767
     768            if (count && descriptors.length === count)
     769                break;
     770
     771            if (Object.getOwnPropertySymbols) {
     772                processProperties(o, Object.getOwnPropertySymbols(o), isOwnProperty);
     773
     774                if (count && descriptors.length === count)
     775                    break;
    759776            }
    760777
     
    763780        }
    764781
    765         // Always include __proto__ at the end.
    766         try {
    767             if (object.__proto__)
    768                 descriptors.push({name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true});
    769         } catch { }
    770 
    771         return descriptors;
    772     }
    773 
    774     _getSetEntries(object, skip, numberToFetch)
     782        // Always include __proto__ at the end, but only for the first fetch.
     783        if (!start) {
     784            try {
     785                if (object.__proto__)
     786                    descriptors.push({name: "__proto__", value: object.__proto__, writable: true, configurable: true, isOwn: true});
     787            } catch { }
     788            return descriptors;
     789        }
     790
     791        return descriptors.slice(start);
     792    }
     793
     794    _getSetEntries(object, fetchStart, fetchCount)
    775795    {
    776796        let entries = [];
     
    778798        // FIXME: This is observable if the page overrides Set.prototype[Symbol.iterator].
    779799        for (let value of object) {
    780             if (skip > 0) {
    781                 skip--;
     800            if (fetchStart > 0) {
     801                fetchStart--;
    782802                continue;
    783803            }
     
    785805            entries.push({value});
    786806
    787             if (numberToFetch && entries.length === numberToFetch)
     807            if (fetchCount && entries.length === fetchCount)
    788808                break;
    789809        }
     
    792812    }
    793813
    794     _getMapEntries(object, skip, numberToFetch)
     814    _getMapEntries(object, fetchStart, fetchCount)
    795815    {
    796816        let entries = [];
     
    798818        // FIXME: This is observable if the page overrides Map.prototype[Symbol.iterator].
    799819        for (let [key, value] of object) {
    800             if (skip > 0) {
    801                 skip--;
     820            if (fetchStart > 0) {
     821                fetchStart--;
    802822                continue;
    803823            }
     
    805825            entries.push({key, value});
    806826
    807             if (numberToFetch && entries.length === numberToFetch)
     827            if (fetchCount && entries.length === fetchCount)
    808828                break;
    809829        }
     
    812832    }
    813833
    814     _getWeakMapEntries(object, numberToFetch)
    815     {
    816         return InjectedScriptHost.weakMapEntries(object, numberToFetch);
    817     }
    818 
    819     _getWeakSetEntries(object, numberToFetch)
    820     {
    821         return InjectedScriptHost.weakSetEntries(object, numberToFetch);
    822     }
    823 
    824     _getIteratorEntries(object, numberToFetch)
    825     {
    826         return InjectedScriptHost.iteratorEntries(object, numberToFetch);
    827     }
    828 
    829     _entries(object, subtype, startIndex, numberToFetch)
     834    _getWeakMapEntries(object, fetchCount)
     835    {
     836        return InjectedScriptHost.weakMapEntries(object, fetchCount);
     837    }
     838
     839    _getWeakSetEntries(object, fetchCount)
     840    {
     841        return InjectedScriptHost.weakSetEntries(object, fetchCount);
     842    }
     843
     844    _getIteratorEntries(object, fetchCount)
     845    {
     846        return InjectedScriptHost.iteratorEntries(object, fetchCount);
     847    }
     848
     849    _entries(object, subtype, fetchStart, fetchCount)
    830850    {
    831851        if (subtype === "set")
    832             return this._getSetEntries(object, startIndex, numberToFetch);
     852            return this._getSetEntries(object, fetchStart, fetchCount);
    833853        if (subtype === "map")
    834             return this._getMapEntries(object, startIndex, numberToFetch);
     854            return this._getMapEntries(object, fetchStart, fetchCount);
    835855        if (subtype === "weakmap")
    836             return this._getWeakMapEntries(object, numberToFetch);
     856            return this._getWeakMapEntries(object, fetchCount);
    837857        if (subtype === "weakset")
    838             return this._getWeakSetEntries(object, numberToFetch);
     858            return this._getWeakSetEntries(object, fetchCount);
    839859        if (subtype === "iterator")
    840             return this._getIteratorEntries(object, numberToFetch);
     860            return this._getIteratorEntries(object, fetchCount);
    841861
    842862        throw "unexpected type";
     
    11311151
    11321152            // Properties.
    1133             let nativeGettersAsValues = true;
    1134             let descriptors = injectedScript._propertyDescriptors(object, InjectedScript.CollectionMode.AllProperties, nativeGettersAsValues);
     1153            let descriptors = injectedScript._propertyDescriptors(object, InjectedScript.CollectionMode.AllProperties, {nativeGettersAsValues: true});
    11351154            this._appendPropertyPreviews(object, preview, descriptors, false, propertiesThreshold, firstLevelKeys, secondLevelKeys);
    11361155            if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
  • trunk/Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp

    r249175 r250087  
    486486    VM& vm = exec->vm();
    487487    auto scope = DECLARE_THROW_SCOPE(vm);
    488     JSValue value = exec->uncheckedArgument(0);
    489     JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(vm, value);
     488    auto* weakMap = jsDynamicCast<JSWeakMap*>(vm, exec->uncheckedArgument(0));
    490489    if (!weakMap)
    491490        return jsUndefined();
    492491
    493     unsigned numberToFetch = 100;
    494 
    495     JSValue numberToFetchArg = exec->argument(1);
    496     double fetchDouble = numberToFetchArg.toInteger(exec);
    497     if (fetchDouble >= 0)
    498         numberToFetch = static_cast<unsigned>(fetchDouble);
     492    MarkedArgumentBuffer buffer;
     493    auto fetchCount = exec->argument(1).toInteger(exec);
     494    weakMap->takeSnapshot(buffer, fetchCount >= 0 ? static_cast<unsigned>(fetchCount) : 0);
     495    ASSERT(!buffer.hasOverflowed());
    499496
    500497    JSArray* array = constructEmptyArray(exec, nullptr);
    501498    RETURN_IF_EXCEPTION(scope, JSValue());
    502 
    503     MarkedArgumentBuffer buffer;
    504     weakMap->takeSnapshot(buffer, numberToFetch);
    505499
    506500    for (unsigned index = 0; index < buffer.size(); index += 2) {
     
    536530    VM& vm = exec->vm();
    537531    auto scope = DECLARE_THROW_SCOPE(vm);
    538     JSValue value = exec->uncheckedArgument(0);
    539     JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(vm, value);
     532    auto* weakSet = jsDynamicCast<JSWeakSet*>(vm, exec->uncheckedArgument(0));
    540533    if (!weakSet)
    541534        return jsUndefined();
    542535
    543     unsigned numberToFetch = 100;
    544 
    545     JSValue numberToFetchArg = exec->argument(1);
    546     double fetchDouble = numberToFetchArg.toInteger(exec);
    547     if (fetchDouble >= 0)
    548         numberToFetch = static_cast<unsigned>(fetchDouble);
     536    MarkedArgumentBuffer buffer;
     537    auto fetchCount = exec->argument(1).toInteger(exec);
     538    weakSet->takeSnapshot(buffer, fetchCount >= 0 ? static_cast<unsigned>(fetchCount) : 0);
     539    ASSERT(!buffer.hasOverflowed());
    549540
    550541    JSArray* array = constructEmptyArray(exec, nullptr);
    551542    RETURN_IF_EXCEPTION(scope, JSValue());
    552 
    553     MarkedArgumentBuffer buffer;
    554     weakSet->takeSnapshot(buffer, numberToFetch);
    555543
    556544    for (unsigned index = 0; index < buffer.size(); ++index) {
  • trunk/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp

    r250005 r250087  
    5757}
    5858
     59static int asInt(const int* i)
     60{
     61    if (i)
     62        return *i;
     63    return 0;
     64}
     65
    5966InspectorRuntimeAgent::InspectorRuntimeAgent(AgentContext& context)
    6067    : InspectorAgentBase("Runtime"_s)
     
    192199}
    193200
    194 void InspectorRuntimeAgent::getProperties(ErrorString& errorString, const String& objectId, const bool* ownProperties, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties)
     201void InspectorRuntimeAgent::getProperties(ErrorString& errorString, const String& objectId, const bool* ownProperties, const int* fetchStart, const int* fetchCount, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties)
    195202{
    196203    InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
    197204    if (injectedScript.hasNoValue()) {
    198205        errorString = "Missing injected script for given objectId"_s;
     206        return;
     207    }
     208
     209    int start = asInt(fetchStart);
     210    if (start < 0) {
     211        errorString = "fetchStart cannot be negative"_s;
     212        return;
     213    }
     214
     215    int count = asInt(fetchCount);
     216    if (count < 0) {
     217        errorString = "fetchCount cannot be negative"_s;
    199218        return;
    200219    }
     
    203222    muteConsole();
    204223
    205     injectedScript.getProperties(errorString, objectId, asBool(ownProperties), asBool(generatePreview), result);
    206     injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), internalProperties);
     224    injectedScript.getProperties(errorString, objectId, asBool(ownProperties), start, count, asBool(generatePreview), properties);
     225
     226    // Only include internal properties for the first fetch.
     227    if (!start)
     228        injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), internalProperties);
    207229
    208230    unmuteConsole();
     
    210232}
    211233
    212 void InspectorRuntimeAgent::getDisplayableProperties(ErrorString& errorString, const String& objectId, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties)
     234void InspectorRuntimeAgent::getDisplayableProperties(ErrorString& errorString, const String& objectId, const int* fetchStart, const int* fetchCount, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties)
    213235{
    214236    InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
    215237    if (injectedScript.hasNoValue()) {
    216238        errorString = "Missing injected script for given objectId"_s;
     239        return;
     240    }
     241
     242    int start = asInt(fetchStart);
     243    if (start < 0) {
     244        errorString = "fetchStart cannot be negative"_s;
     245        return;
     246    }
     247
     248    int count = asInt(fetchCount);
     249    if (count < 0) {
     250        errorString = "fetchCount cannot be negative"_s;
    217251        return;
    218252    }
     
    221255    muteConsole();
    222256
    223     injectedScript.getDisplayableProperties(errorString, objectId, asBool(generatePreview), result);
    224     injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), internalProperties);
     257    injectedScript.getDisplayableProperties(errorString, objectId, start, count, asBool(generatePreview), properties);
     258
     259    // Only include internal properties for the first fetch.
     260    if (!start)
     261        injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), internalProperties);
    225262
    226263    unmuteConsole();
     
    228265}
    229266
    230 void InspectorRuntimeAgent::getCollectionEntries(ErrorString& errorString, const String& objectId, const String* objectGroup, const int* startIndex, const int* numberToFetch, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries)
     267void InspectorRuntimeAgent::getCollectionEntries(ErrorString& errorString, const String& objectId, const String* objectGroup, const int* fetchStart, const int* fetchCount, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries)
    231268{
    232269    InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
     
    236273    }
    237274
    238     int start = startIndex && *startIndex >= 0 ? *startIndex : 0;
    239     int fetch = numberToFetch && *numberToFetch >= 0 ? *numberToFetch : 0;
    240 
    241     injectedScript.getCollectionEntries(errorString, objectId, objectGroup ? *objectGroup : String(), start, fetch, entries);
     275    int start = asInt(fetchStart);
     276    if (start < 0) {
     277        errorString = "fetchStart cannot be negative"_s;
     278        return;
     279    }
     280
     281    int count = asInt(fetchCount);
     282    if (count < 0) {
     283        errorString = "fetchCount cannot be negative"_s;
     284        return;
     285    }
     286
     287    injectedScript.getCollectionEntries(errorString, objectId, objectGroup ? *objectGroup : String(), start, count, entries);
    242288}
    243289
  • trunk/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h

    r249132 r250087  
    6767    void releaseObject(ErrorString&, const ErrorString& objectId) final;
    6868    void getPreview(ErrorString&, const String& objectId, RefPtr<Protocol::Runtime::ObjectPreview>&) final;
    69     void getProperties(ErrorString&, const String& objectId, const bool* ownProperties, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties) final;
    70     void getDisplayableProperties(ErrorString&, const String& objectId, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties) final;
    71     void getCollectionEntries(ErrorString&, const String& objectId, const String* objectGroup, const int* startIndex, const int* numberToFetch, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries) final;
     69    void getProperties(ErrorString&, const String& objectId, const bool* ownProperties, const int* fetchStart, const int* fetchCount, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties) final;
     70    void getDisplayableProperties(ErrorString&, const String& objectId, const int* fetchStart, const int* fetchCount, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties) final;
     71    void getCollectionEntries(ErrorString&, const String& objectId, const String* objectGroup, const int* fetchStart, const int* fetchCount, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries) final;
    7272    void saveResult(ErrorString&, const JSON::Object& callArgument, const int* executionContextId, Optional<int>& savedResultIndex) final;
    7373    void setSavedResultAlias(ErrorString&, const String* alias) final;
  • trunk/Source/JavaScriptCore/inspector/protocol/Runtime.json

    r248898 r250087  
    7777                { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." },
    7878                { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." },
    79                 { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." },
    80                 { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." },
    8179                { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." },
     80                { "name": "configurable", "type": "boolean", "optional": true, "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." },
     81                { "name": "enumerable", "type": "boolean", "optional": true, "description": "True if this property shows up during enumeration of the properties on the corresponding object." },
    8282                { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." },
    8383                { "name": "symbol", "optional": true, "$ref": "Runtime.RemoteObject", "description": "Property symbol object, if the property is a symbol." },
     
    276276                { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." },
    277277                { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the object itself, not to its prototype chain." },
     278                { "name": "fetchStart", "optional": true, "type": "integer", "description": "If provided skip to this value before collecting values. Otherwise, start at the beginning. Has no effect when the `objectId` is for a `iterator`/`WeakMap`/`WeakSet` object." },
     279                { "name": "fetchCount", "optional": true, "type": "integer", "description": "If provided only return `fetchCount` values. Otherwise, return values all the way to the end." },
    278280                { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for property values." }
    279281            ],
    280282            "returns": [
    281                 { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." },
    282                 { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor"}, "description": "Internal object properties." }
     283                { "name": "properties", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." },
     284                { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor"}, "description": "Internal object properties. Only included if `fetchStart` is 0." }
    283285            ]
    284286        },
     
    288290            "parameters": [
    289291                { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." },
     292                { "name": "fetchStart", "optional": true, "type": "integer", "description": "If provided skip to this value before collecting values. Otherwise, start at the beginning. Has no effect when the `objectId` is for a `iterator`/`WeakMap`/`WeakSet` object." },
     293                { "name": "fetchCount", "optional": true, "type": "integer", "description": "If provided only return `fetchCount` values. Otherwise, return values all the way to the end." },
    290294                { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for property values." }
    291295            ],
    292296            "returns": [
    293297                { "name": "properties", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." },
    294                 { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor"}, "description": "Internal object properties." }
     298                { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor"}, "description": "Internal object properties. Only included if `fetchStart` is 0." }
    295299            ]
    296300        },
     
    301305                { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the collection to get entries for." },
    302306                { "name": "objectGroup", "optional": true, "type": "string", "description": "Symbolic group name that can be used to release multiple. If not provided, it will be the same objectGroup as the RemoteObject determined from <code>objectId</code>. This is useful for WeakMap to release the collection entries." },
    303                 { "name": "startIndex", "optional": true, "type": "integer", "description": "If provided skip to this index before collecting values. Otherwise, 0." },
    304                 { "name": "numberToFetch", "optional": true, "type": "integer", "description": "If provided only return <code>numberToFetch</code> values. Otherwise, return values all the way to the end." }
     307                { "name": "fetchStart", "optional": true, "type": "integer", "description": "If provided skip to this value before collecting values. Otherwise, start at the beginning. Has no effect when the `objectId<` is for a `iterator<`/`WeakMap<`/`WeakSet<` object." },
     308                { "name": "fetchCount", "optional": true, "type": "integer", "description": "If provided only return `fetchCount` values. Otherwise, return values all the way to the end." }
    305309            ],
    306310            "returns": [
  • trunk/Source/WebInspectorUI/ChangeLog

    r250076 r250087  
     12019-09-18  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: Better handling for large arrays and collections in Object Trees
     4        https://bugs.webkit.org/show_bug.cgi?id=143589
     5        <rdar://problem/16135388>
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        Adds two buttons before the "Prototype" item in expanded object/collection previews:
     10         - Show %d More
     11         - Show All (%d More)
     12
     13        The default `fetchCount` increment is `100`. The first button will only be shown if there
     14        are more than `100` items remaining (haven't been shown).
     15
     16        * UserInterface/Protocol/RemoteObject.js:
     17        (WI.RemoteObject.prototype.getPropertyDescriptors):
     18        (WI.RemoteObject.prototype.getDisplayablePropertyDescriptors):
     19        (WI.RemoteObject.prototype.getCollectionEntries):
     20        (WI.RemoteObject.prototype.getOwnPropertyDescriptor.wrappedCallback):
     21        (WI.RemoteObject.prototype._getProperties): Added.
     22        (WI.RemoteObject.prototype._getDisplayableProperties): Added.
     23
     24        * UserInterface/Views/ObjectTreeView.js:
     25        (WI.ObjectTreeView):
     26        (WI.ObjectTreeView.showMoreFetchCount): Added.
     27        (WI.ObjectTreeView.addShowMoreIfNeeded): Added.
     28        (WI.ObjectTreeView.prototype.update):
     29        (WI.ObjectTreeView.prototype._updateChildren):
     30        (WI.ObjectTreeView.prototype._updateEntries):
     31        (WI.ObjectTreeView.prototype._updateProperties):
     32        * UserInterface/Views/ObjectTreeView.css:
     33        (.tree-outline.object li > button[disabled] + .indeterminate-progress-spinner): Added.
     34        Avoid duplicating the button creation logic in `WI.ObjectTreePropertyTreeElement` by using a
     35        `static` function. This expects the existence of and requires access to "private" values.
     36
     37        * UserInterface/Views/ObjectTreePropertyTreeElement.js:
     38        (WI.ObjectTreePropertyTreeElement):
     39        (WI.ObjectTreePropertyTreeElement.prototype.onpopulate):
     40        (WI.ObjectTreePropertyTreeElement.prototype._updateChildren):
     41        (WI.ObjectTreePropertyTreeElement.prototype._updateEntries):
     42        (WI.ObjectTreePropertyTreeElement.prototype._updateProperties):
     43        (WI.ObjectTreePropertyTreeElement.prototype._updateChildrenInternal): Deleted.
     44
     45        * UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js:
     46        (WI.JavaScriptRuntimeCompletionProvider.prototype.completionControllerCompletionsNeeded.evaluated):
     47        (WI.JavaScriptRuntimeCompletionProvider.prototype.completionControllerCompletionsNeeded.receivedArrayPropertyNames): Deleted.
     48
     49        * UserInterface/Controllers/RuntimeManager.js:
     50        (WI.RuntimeManager.prototype.getPropertiesForRemoteObject): Deleted.
     51        Remove unused function.
     52        * Localizations/en.lproj/localizedStrings.js:
     53
    1542019-09-18  Joseph Pecoraro  <pecoraro@apple.com>
    255
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r249786 r250087  
    10061006localizedStrings["Show %d More"] = "Show %d More";
    10071007localizedStrings["Show All"] = "Show All";
     1008localizedStrings["Show All (%d More)"] = "Show All (%d More)";
    10081009localizedStrings["Show All Nodes (%d More)"] = "Show All Nodes (%d More)";
    10091010localizedStrings["Show Console"] = "Show Console";
     
    10131014localizedStrings["Show Icons"] = "Show Icons";
    10141015localizedStrings["Show Jump to Effective Property Button"] = "Show Jump to Effective Property Button";
     1016localizedStrings["Show More"] = "Show More";
    10151017localizedStrings["Show Path"] = "Show Path";
    10161018localizedStrings["Show Remaining (%d)"] = "Show Remaining (%d)";
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js

    r249846 r250087  
    252252        function receivedArrayPropertyNames(propertyNames)
    253253        {
    254             // FIXME: <https://webkit.org/b/143589> Web Inspector: Better handling for large collections in Object Trees
    255             // If there was an array like object, we generate autocompletion up to 1000 indexes, but this should
    256             // handle a list with arbitrary length.
    257254            if (propertyNames && typeof propertyNames.length === "number") {
     255                // FIXME <https://webkit.org/b/201909> Web Inspector: autocompletion of array indexes can't handle large arrays in a performant way
    258256                var max = Math.min(propertyNames.length, 1000);
    259257                for (var i = 0; i < max; ++i)
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js

    r248287 r250087  
    186186        else
    187187            target.RuntimeAgent.saveResult(remoteObject.asCallArgument(), executionContextId, mycallback);
    188     }
    189 
    190     getPropertiesForRemoteObject(objectId, callback)
    191     {
    192         this._activeExecutionContext.target.RuntimeAgent.getProperties(objectId, function(error, result) {
    193             if (error) {
    194                 callback(error);
    195                 return;
    196             }
    197 
    198             let properties = new Map;
    199             for (let property of result)
    200                 properties.set(property.name, property);
    201 
    202             callback(null, properties);
    203         });
    204188    }
    205189
  • trunk/Source/WebInspectorUI/UserInterface/Protocol/RemoteObject.js

    r249786 r250087  
    295295        }
    296296
    297         this._target.RuntimeAgent.getProperties(this._objectId, options.ownProperties, options.generatePreview, this._getPropertyDescriptorsResolver.bind(this, callback));
    298     }
    299 
    300     getDisplayablePropertyDescriptors(callback)
     297        this._getProperties(this._getPropertyDescriptorsResolver.bind(this, callback), options);
     298    }
     299
     300    getDisplayablePropertyDescriptors(callback, options = {})
    301301    {
    302302        if (!this._objectId || this._isSymbol() || this._isFakeObject()) {
     
    308308        // Here we do our best to reimplement it by getting all properties and reducing them down.
    309309        if (!this._target.RuntimeAgent.getDisplayableProperties) {
    310             this._target.RuntimeAgent.getProperties(this._objectId, function(error, allProperties) {
     310            this._getProperties(options, (error, allProperties) => {
    311311                var ownOrGetterPropertiesList = [];
    312312                if (allProperties) {
     
    325325                    }
    326326                }
    327 
    328327                this._getPropertyDescriptorsResolver(callback, error, ownOrGetterPropertiesList);
    329             }.bind(this));
    330             return;
    331         }
    332 
    333         this._target.RuntimeAgent.getDisplayableProperties(this._objectId, true, this._getPropertyDescriptorsResolver.bind(this, callback));
     328            });
     329            return;
     330        }
     331
     332        this._getDisplayableProperties(this._getPropertyDescriptorsResolver.bind(this, callback), options);
    334333    }
    335334
     
    405404    }
    406405
    407     getCollectionEntries(start, numberToFetch, callback)
    408     {
    409         start = typeof start === "number" ? start : 0;
    410         numberToFetch = typeof numberToFetch === "number" ? numberToFetch : 100;
    411 
    412         console.assert(start >= 0);
    413         console.assert(numberToFetch >= 0);
     406    getCollectionEntries(callback, {fetchStart, fetchCount} = {})
     407    {
    414408        console.assert(this.isCollectionType());
     409        console.assert(typeof fetchStart === "undefined" || (typeof fetchStart === "number" && fetchStart >= 0), fetchStart);
     410        console.assert(typeof fetchCount === "undefined" || (typeof fetchCount === "number" && fetchCount > 0), fetchCount);
    415411
    416412        // WeakMaps and WeakSets are not ordered. We should never send a non-zero start.
    417         console.assert((this._subtype === "weakmap" && start === 0) || this._subtype !== "weakmap");
    418         console.assert((this._subtype === "weakset" && start === 0) || this._subtype !== "weakset");
     413        console.assert(!this.isWeakCollection() || typeof fetchStart === "undefined" || fetchStart === 0, fetchStart);
    419414
    420415        let objectGroup = this.isWeakCollection() ? this._weakCollectionObjectGroup() : "";
    421416
    422         this._target.RuntimeAgent.getCollectionEntries(this._objectId, objectGroup, start, numberToFetch, (error, entries) => {
    423             entries = entries.map((x) => WI.CollectionEntry.fromPayload(x, this._target));
    424             callback(entries);
     417        // COMPATIBILITY (iOS 13): `startIndex` and `numberToFetch` were renamed to `fetchStart` and `fetchCount` (but kept in the same position).
     418        this._target.RuntimeAgent.getCollectionEntries(this._objectId, objectGroup, fetchStart, fetchCount, (error, entries) => {
     419            callback(entries.map((x) => WI.CollectionEntry.fromPayload(x, this._target)));
    425420        });
    426421    }
     
    546541            }
    547542
    548             var fakeDescriptor = {name: propertyName, value: result, writable: true, configurable: true, enumerable: false};
     543            var fakeDescriptor = {name: propertyName, value: result, writable: true, configurable: true};
    549544            var fakePropertyDescriptor = new WI.PropertyDescriptor(fakeDescriptor, null, true, false, false, false);
    550545            callback(fakePropertyDescriptor);
     
    630625    }
    631626
     627    _getProperties(callback, {ownProperties, fetchStart, fetchCount, generatePreview} = {})
     628    {
     629        // COMPATIBILITY (iOS 13): `result` was renamed to `properties` (but kept in the same position).
     630        this._target.RuntimeAgent.getProperties.invoke({
     631            objectId: this._objectId,
     632            ownProperties,
     633            fetchStart,
     634            fetchCount,
     635            generatePreview,
     636        }, callback, this._target.RuntimeAgent);
     637    }
     638
     639    _getDisplayableProperties(callback, {fetchStart, fetchCount, generatePreview} = {})
     640    {
     641        console.assert(this._target.RuntimeAgent.getDisplayableProperties);
     642
     643        this._target.RuntimeAgent.getDisplayableProperties.invoke({
     644            objectId: this._objectId,
     645            fetchStart,
     646            fetchCount,
     647            generatePreview,
     648        }, callback, this._target.RuntimeAgent);
     649    }
     650
    632651    _getPropertyDescriptorsResolver(callback, error, properties, internalProperties)
    633652    {
  • trunk/Source/WebInspectorUI/UserInterface/Views/ObjectTreePropertyTreeElement.js

    r242076 r250087  
    3333        this._prototypeName = prototypeName;
    3434
     35        this._fetchStart = 0;
     36        this._fetchEnd = WI.ObjectTreeView.showMoreFetchCount;
     37        this._fetchEndIndex = 0;
     38
    3539        this.mainTitle = this._titleFragment();
    3640        this.addClassName("object-tree-property");
     
    5660    onpopulate()
    5761    {
     62        if (this.children.length && !this.shouldRefreshChildren)
     63            return;
     64
     65        this._fetchStart = 0;
     66        this._fetchEndIndex = 0;
    5867        this._updateChildren();
    5968    }
     
    319328    _updateChildren()
    320329    {
    321         if (this.children.length && !this.shouldRefreshChildren)
    322             return;
    323 
    324         const options = {
     330        let resolvedValue = this.resolvedValue();
     331
     332        let wrap = (handler, mode) => (list) => {
     333            if (this._fetchEndIndex === 0)
     334                this.removeChildren();
     335
     336            if (!list) {
     337                let errorMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("Could not fetch properties. Object may no longer exist."));
     338                this.appendChild(new WI.TreeElement(errorMessageElement));
     339                return;
     340            }
     341
     342            handler.call(this, list, this.resolvedValuePropertyPath(), mode);
     343
     344            this.dispatchEventToListeners(WI.ObjectTreeView.Event.Updated);
     345        };
     346
     347        let options = {
    325348            ownProperties: true,
    326349            generatePreview: true,
    327350        };
    328351
    329         var resolvedValue = this.resolvedValue();
     352        if (isFinite(this._fetchEnd) && this._fetchEnd < resolvedValue.size) {
     353            options.fetchStart = this._fetchStart;
     354            options.fetchCount = this._fetchEnd - this._fetchStart;
     355        }
     356
    330357        if (resolvedValue.isCollectionType() && this._mode === WI.ObjectTreeView.Mode.Properties)
    331             resolvedValue.getCollectionEntries(0, 100, this._updateChildrenInternal.bind(this, this._updateEntries, this._mode));
     358            resolvedValue.getCollectionEntries(wrap(this._updateEntries, this._mode), options);
    332359        else if (this._mode === WI.ObjectTreeView.Mode.ClassAPI || this._mode === WI.ObjectTreeView.Mode.PureAPI)
    333             resolvedValue.getPropertyDescriptors(this._updateChildrenInternal.bind(this, this._updateProperties, WI.ObjectTreeView.Mode.ClassAPI), options);
     360            resolvedValue.getPropertyDescriptors(wrap(this._updateProperties, WI.ObjectTreeView.Mode.ClassAPI), options);
    334361        else if (this.property.name === "__proto__")
    335             resolvedValue.getPropertyDescriptors(this._updateChildrenInternal.bind(this, this._updateProperties, WI.ObjectTreeView.Mode.PrototypeAPI), options);
     362            resolvedValue.getPropertyDescriptors(wrap(this._updateProperties, WI.ObjectTreeView.Mode.PrototypeAPI), options);
    336363        else
    337             resolvedValue.getDisplayablePropertyDescriptors(this._updateChildrenInternal.bind(this, this._updateProperties, this._mode));
    338     }
    339 
    340     _updateChildrenInternal(handler, mode, list)
    341     {
    342         this.removeChildren();
    343 
    344         if (!list) {
    345             var errorMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("Could not fetch properties. Object may no longer exist."));
    346             this.appendChild(new WI.TreeElement(errorMessageElement, null, false));
    347             return;
    348         }
    349 
    350         handler.call(this, list, this.resolvedValuePropertyPath(), mode);
     364            resolvedValue.getDisplayablePropertyDescriptors(wrap(this._updateProperties, this._mode), options);
    351365    }
    352366
    353367    _updateEntries(entries, propertyPath, mode)
    354368    {
    355         for (var entry of entries) {
     369        let resolvedValue = this.resolvedValue();
     370
     371        entries.forEach((entry, i) => {
    356372            if (entry.key) {
    357                 this.appendChild(new WI.ObjectTreeMapKeyTreeElement(entry.key, propertyPath));
    358                 this.appendChild(new WI.ObjectTreeMapValueTreeElement(entry.value, propertyPath, entry.key));
     373                this.insertChild(new WI.ObjectTreeMapKeyTreeElement(entry.key, propertyPath), this._fetchEndIndex++);
     374                this.insertChild(new WI.ObjectTreeMapValueTreeElement(entry.value, propertyPath, entry.key), this._fetchEndIndex++);
    359375            } else
    360                 this.appendChild(new WI.ObjectTreeSetIndexTreeElement(entry.value, propertyPath));
    361         }
     376                this.insertChild(new WI.ObjectTreeSetIndexTreeElement(entry.value, propertyPath), this._fetchEndIndex++);
     377        });
    362378
    363379        if (!this.children.length) {
    364             var emptyMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("No Entries"));
    365             this.appendChild(new WI.TreeElement(emptyMessageElement, null, false));
    366         }
    367 
    368         // Show the prototype so users can see the API.
    369         var resolvedValue = this.resolvedValue();
    370         resolvedValue.getOwnPropertyDescriptor("__proto__", (propertyDescriptor) => {
    371             if (propertyDescriptor)
    372                 this.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode));
    373         });
     380            let emptyMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("No Entries"));
     381            this.appendChild(new WI.TreeElement(emptyMessageElement));
     382        } else {
     383            console.assert(mode === WI.ObjectTreeView.Mode.Properties);
     384            WI.ObjectTreeView.addShowMoreIfNeeded({
     385                resolvedValue,
     386                representation: this,
     387                parentTreeElement: this,
     388                handleShowMoreClicked: () => {
     389                    this._updateChildren();
     390                },
     391                handleShowAllClicked: () => {
     392                    this.shouldRefreshChildren = true;
     393                },
     394            });
     395        }
     396
     397        // Show the prototype so users can see the API, but only fetch it the first time.
     398        if (this._fetchStart === 0) {
     399            resolvedValue.getOwnPropertyDescriptor("__proto__", (propertyDescriptor) => {
     400                if (propertyDescriptor)
     401                    this.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode));
     402            });
     403        }
    374404    }
    375405
     
    397427                continue;
    398428
    399             // COMPATIBILITY (iOS 8): Sometimes __proto__ is not a value, but a get/set property.
    400             // In those cases it is actually not useful to show.
    401             if (propertyDescriptor.name === "__proto__" && !propertyDescriptor.hasValue())
     429            if (propertyDescriptor.name === "__proto__") {
     430                // COMPATIBILITY (iOS 8): Sometimes __proto__ is not a value, but a get/set property.
     431                // In those cases it is actually not useful to show.
     432                if (!propertyDescriptor.hasValue())
     433                    continue;
     434
     435                hadProto = true;
     436                this.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode, prototypeName));
    402437                continue;
     438            }
    403439
    404440            if (isArray && isPropertyMode) {
    405441                if (propertyDescriptor.isIndexProperty())
    406                     this.appendChild(new WI.ObjectTreeArrayIndexTreeElement(propertyDescriptor, propertyPath));
    407                 else if (propertyDescriptor.name === "__proto__")
    408                     this.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode, prototypeName));
     442                    this.insertChild(new WI.ObjectTreeArrayIndexTreeElement(propertyDescriptor, propertyPath), this._fetchEndIndex++);
    409443            } else
    410                 this.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode, prototypeName));
    411 
    412             if (propertyDescriptor.name === "__proto__")
    413                 hadProto = true;
     444                this.insertChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, mode, prototypeName), this._fetchEndIndex++);
    414445        }
    415446
    416447        if (!this.children.length || (hadProto && this.children.length === 1)) {
    417             var emptyMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("No Properties"));
    418             this.insertChild(new WI.TreeElement(emptyMessageElement, null, false), 0);
     448            let emptyMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("No Properties"));
     449            this.insertChild(new WI.TreeElement(emptyMessageElement), 0);
     450        } else if (isArray && isPropertyMode) {
     451            WI.ObjectTreeView.addShowMoreIfNeeded({
     452                resolvedValue,
     453                representation: this,
     454                parentTreeElement: this,
     455                handleShowMoreClicked: () => {
     456                    this._updateChildren();
     457                },
     458                handleShowAllClicked: () => {
     459                    this.shouldRefreshChildren = true;
     460                },
     461            });
    419462        }
    420463    }
  • trunk/Source/WebInspectorUI/UserInterface/Views/ObjectTreeView.css

    r248766 r250087  
    134134}
    135135
     136.tree-outline.object li > button[disabled] + .indeterminate-progress-spinner {
     137    display: inline-block;
     138    -webkit-margin-start: 2px;
     139    vertical-align: -4px;
     140}
     141
    136142.object-tree-property :matches(.formatted-string, .formatted-regexp) {
    137143    white-space: nowrap;
  • trunk/Source/WebInspectorUI/UserInterface/Views/ObjectTreeView.js

    r249301 r250087  
    4747        this._inConsole = true;
    4848
     49        this._fetchStart = 0;
     50        this._fetchEnd = ObjectTreeView.showMoreFetchCount;
     51        this._fetchEndIndex = 0;
     52
    4953        // Always force expanding for classes.
    5054        if (this._object.isClass())
     
    98102        emptyMessageElement.textContent = message;
    99103        return emptyMessageElement;
     104    }
     105
     106    static get showMoreFetchCount() { return 100; }
     107
     108    static addShowMoreIfNeeded({resolvedValue, representation, parentTreeElement, handleShowMoreClicked, handleShowAllClicked})
     109    {
     110        console.assert(resolvedValue instanceof WI.RemoteObject, resolvedValue);
     111        console.assert(resolvedValue.isArray() || resolvedValue.isCollectionType());
     112        console.assert(typeof resolvedValue.size === "number" && resolvedValue.size > 0, resolvedValue);
     113        console.assert("_fetchStart" in representation, representation);
     114        console.assert("_fetchEnd" in representation, representation);
     115        console.assert("_fetchEndIndex" in representation, representation);
     116        console.assert(typeof handleShowMoreClicked === "function", handleShowMoreClicked);
     117        console.assert(typeof handleShowAllClicked === "function", handleShowAllClicked);
     118
     119        // COMPATIBILITY (iOS 13): Runtime.getProperties/Runtime.getDisplayableProperties didn't support `fetchStart`/`fetchCount` yet.
     120        if (!InspectorBackend.domains.Runtime.getProperties.supports("fetchStart"))
     121            return;
     122
     123        let remaining = resolvedValue.size - representation._fetchEnd;
     124        if (remaining <= 0)
     125            return;
     126
     127        let treeElement = new WI.TreeElement(document.createElement("span"));
     128        treeElement.selectable = false;
     129        parentTreeElement.insertChild(treeElement, representation._fetchEndIndex);
     130
     131        // FIXME: <https://webkit.org/b/201965> Web Inspector: allow examining items in the middle of an array/collection without having to show all items before
     132
     133        let buttons = [];
     134        let createButton = (textContent, handleClick) => {
     135            let button = treeElement.title.appendChild(document.createElement("button"));
     136            button.textContent = textContent;
     137            button.addEventListener("click", (event) => {
     138                event.stop();
     139
     140                representation.singleFireEventListener(ObjectTreeView.Event.Updated, () => {
     141                    // The `treeElement` may have already been removed by some other means (e.g. `removeChildren`).
     142                    if (treeElement.parent === parentTreeElement)
     143                        parentTreeElement.removeChild(treeElement);
     144                });
     145
     146                for (let other of buttons)
     147                    other.disabled = true;
     148
     149                let spinner = new WI.IndeterminateProgressSpinner;
     150                button.insertAdjacentElement("afterend", spinner.element);
     151
     152                handleClick();
     153            });
     154            buttons.push(button);
     155        };
     156
     157        if (!resolvedValue.isWeakCollection() && remaining > ObjectTreeView.showMoreFetchCount) {
     158            createButton(WI.UIString("Show %d More").format(ObjectTreeView.showMoreFetchCount), () => {
     159                representation._fetchStart = representation._fetchEnd;
     160                representation._fetchEnd = representation._fetchStart + ObjectTreeView.showMoreFetchCount;
     161                handleShowMoreClicked();
     162            });
     163        }
     164
     165        createButton(WI.UIString("Show All (%d More)").format(remaining), () => {
     166            representation._fetchStart = 0;
     167            representation._fetchEnd = Infinity;
     168            representation._fetchEndIndex = 0;
     169            handleShowAllClicked();
     170        });
    100171    }
    101172
     
    255326    update()
    256327    {
    257         const options = {
     328        this._fetchStart = 0;
     329        this._fetchEndIndex = 0;
     330        this._updateChildren();
     331    }
     332
     333    // Private
     334
     335    _updateChildren()
     336    {
     337        let options = {
    258338            ownProperties: true,
    259339            generatePreview: true,
    260340        };
    261341
    262         if (this._object.isCollectionType() && this._mode === WI.ObjectTreeView.Mode.Properties)
    263             this._object.getCollectionEntries(0, 100, this._updateChildren.bind(this, this._updateEntries));
     342        if (isFinite(this._fetchEnd) && this._fetchEnd < this._object.size) {
     343            options.fetchStart = this._fetchStart;
     344            options.fetchCount = this._fetchEnd - this._fetchStart;
     345        }
     346
     347        let wrap = (handler) => (list) => {
     348            if (this._fetchEndIndex === 0)
     349                this._outline.removeChildren();
     350
     351            if (!list) {
     352                let errorMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("Could not fetch properties. Object may no longer exist."));
     353                this._outline.appendChild(new WI.TreeElement(errorMessageElement));
     354                return;
     355            }
     356
     357            handler.call(this, list, this._propertyPath);
     358
     359            this.dispatchEventToListeners(ObjectTreeView.Event.Updated);
     360        };
     361
     362        if (this._object.isCollectionType() && this._mode === ObjectTreeView.Mode.Properties)
     363            this._object.getCollectionEntries(wrap(this._updateEntries), options);
    264364        else if (this._object.isClass())
    265             this._object.classPrototype.getDisplayablePropertyDescriptors(this._updateChildren.bind(this, this._updateProperties));
    266         else if (this._mode === WI.ObjectTreeView.Mode.PureAPI)
    267             this._object.getPropertyDescriptors(this._updateChildren.bind(this, this._updateProperties), options);
     365            this._object.classPrototype.getDisplayablePropertyDescriptors(wrap(this._updateProperties), options);
     366        else if (this._mode === ObjectTreeView.Mode.PureAPI)
     367            this._object.getPropertyDescriptors(wrap(this._updateProperties), options);
    268368        else
    269             this._object.getDisplayablePropertyDescriptors(this._updateChildren.bind(this, this._updateProperties));
    270     }
    271 
    272     // Private
    273 
    274     _updateChildren(handler, list)
    275     {
    276         this._outline.removeChildren();
    277 
    278         if (!list) {
    279             var errorMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("Could not fetch properties. Object may no longer exist."));
    280             this._outline.appendChild(new WI.TreeElement(errorMessageElement, null, false));
    281             return;
    282         }
    283 
    284         handler.call(this, list, this._propertyPath);
    285 
    286         this.dispatchEventToListeners(WI.ObjectTreeView.Event.Updated);
     369            this._object.getDisplayablePropertyDescriptors(wrap(this._updateProperties), options);
    287370    }
    288371
    289372    _updateEntries(entries, propertyPath)
    290373    {
    291         for (var entry of entries) {
     374        entries.forEach((entry, i) => {
    292375            if (entry.key) {
    293                 this._outline.appendChild(new WI.ObjectTreeMapKeyTreeElement(entry.key, propertyPath));
    294                 this._outline.appendChild(new WI.ObjectTreeMapValueTreeElement(entry.value, propertyPath, entry.key));
     376                this._outline.insertChild(new WI.ObjectTreeMapKeyTreeElement(entry.key, propertyPath), this._fetchEndIndex++);
     377                this._outline.insertChild(new WI.ObjectTreeMapValueTreeElement(entry.value, propertyPath, entry.key), this._fetchEndIndex++);
    295378            } else
    296                 this._outline.appendChild(new WI.ObjectTreeSetIndexTreeElement(entry.value, propertyPath));
    297         }
     379                this._outline.insertChild(new WI.ObjectTreeSetIndexTreeElement(entry.value, propertyPath), this._fetchEndIndex++);
     380        });
    298381
    299382        if (!this._outline.children.length) {
    300             var emptyMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("No Entries"));
    301             this._outline.appendChild(new WI.TreeElement(emptyMessageElement, null, false));
    302         }
    303 
    304         // Show the prototype so users can see the API.
    305         this._object.getOwnPropertyDescriptor("__proto__", (propertyDescriptor) => {
    306             if (propertyDescriptor)
    307                 this._outline.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, this._mode));
    308         });
     383            let emptyMessageElement = ObjectTreeView.createEmptyMessageElement(WI.UIString("No Entries"));
     384            this._outline.appendChild(new WI.TreeElement(emptyMessageElement));
     385        } else {
     386            console.assert(this._mode === WI.ObjectTreeView.Mode.Properties);
     387            ObjectTreeView.addShowMoreIfNeeded({
     388                resolvedValue: this._object,
     389                representation: this,
     390                parentTreeElement: this._outline,
     391                handleShowMoreClicked: () => {
     392                    this._updateChildren();
     393                },
     394                handleShowAllClicked: () => {
     395                    this.update();
     396                },
     397            });
     398        }
     399
     400        // Show the prototype so users can see the API, but only fetch it the first time.
     401        if (this._fetchStart === 0) {
     402            this._object.getOwnPropertyDescriptor("__proto__", (propertyDescriptor) => {
     403                if (propertyDescriptor)
     404                    this._outline.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, this._mode));
     405            });
     406        }
    309407    }
    310408
     
    329427                    continue;
    330428                hadProto = true;
     429                this._outline.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, this._mode, this._prototypeNameOverride));
     430                continue;
    331431            }
    332432
    333433            if (isArray && isPropertyMode) {
    334434                if (propertyDescriptor.isIndexProperty())
    335                     this._outline.appendChild(new WI.ObjectTreeArrayIndexTreeElement(propertyDescriptor, propertyPath));
    336                 else if (propertyDescriptor.name === "__proto__")
    337                     this._outline.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, this._mode, this._prototypeNameOverride));
     435                    this._outline.insertChild(new WI.ObjectTreeArrayIndexTreeElement(propertyDescriptor, propertyPath), this._fetchEndIndex++);
    338436            } else
    339                 this._outline.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, this._mode, this._prototypeNameOverride));
     437                this._outline.insertChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, this._mode, this._prototypeNameOverride), this._fetchEndIndex++);
    340438        }
    341439
    342440        if (!this._outline.children.length || (hadProto && this._outline.children.length === 1)) {
    343             var emptyMessageElement = WI.ObjectTreeView.createEmptyMessageElement(WI.UIString("No Properties"));
    344             this._outline.insertChild(new WI.TreeElement(emptyMessageElement, null, false), 0);
     441            let emptyMessageElement = ObjectTreeView.createEmptyMessageElement(WI.UIString("No Properties"));
     442            this._outline.insertChild(new WI.TreeElement(emptyMessageElement), 0);
     443        } else if (isArray && isPropertyMode) {
     444            ObjectTreeView.addShowMoreIfNeeded({
     445                resolvedValue: this._object,
     446                representation: this,
     447                parentTreeElement: this._outline,
     448                handleShowMoreClicked: () => {
     449                    this._updateChildren();
     450                },
     451                handleShowAllClicked: () => {
     452                    this.update();
     453                },
     454            });
    345455        }
    346456    }
Note: See TracChangeset for help on using the changeset viewer.