Changeset 250087 in webkit
- Timestamp:
- Sep 18, 2019 10:58:27 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 5 added
- 20 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r250055 r250087 1 2019-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 1 27 2019-09-18 Ryan Haddad <ryanhaddad@apple.com> 2 28 -
trunk/LayoutTests/inspector/model/remote-object-weak-collection.html
r220119 r250087 58 58 WI.runtimeManager.evaluateInInspectedWindow("GCController.collect()", {objectGroup: "test", doNotPauseOnExceptionsAndMuteConsole: true}, function() { 59 59 InspectorTest.assert(remoteObject instanceof WI.RemoteObject); 60 remoteObject.getCollectionEntries( 0, 100,function(entries) {60 remoteObject.getCollectionEntries(function(entries) { 61 61 InspectorTest.log("ENTRIES:"); 62 62 entries.sort((a, b) => a.value.value - b.value.value); -
trunk/LayoutTests/inspector/runtime/getProperties-expected.txt
r226489 r250087 1 CONSOLE MESSAGE: Unhandled Promise Rejection: 123 2 Tests for the Runtime.getProperties command. 3 1 4 2 5 == 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 7 Evaluating expression... 8 Getting own properties... 9 Properties: 10 "foo" => "cat" (string) [writable | enumerable | configurable | isOwn] 11 "__proto__" => "Number" (object) [writable | configurable | isOwn] 12 13 -- Running test case: Runtime.getProperties.Array 14 Evaluating expression... 15 Getting own properties... 16 Properties: 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 23 Evaluating expression... 24 Getting own properties... 25 Properties: 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 32 Evaluating expression... 33 Getting own properties... 34 Properties: 35 "name" => "bound Test" (string) [configurable | isOwn] 36 "length" => 0 (number) [configurable | isOwn] 37 "__proto__" => "function () {\n [native code]\n}" (function) [writable | configurable | isOwn] 38 Internal Properties: 39 "targetFunction" => "class Test { }" (function class) [] 40 "boundThis" => null (object null) [] 41 42 -- Running test case: Runtime.getProperties.BoundConstructorArguments 43 Evaluating expression... 44 Getting own properties... 45 Properties: 46 "name" => "bound Test" (string) [configurable | isOwn] 47 "length" => 0 (number) [configurable | isOwn] 48 "__proto__" => "function () {\n [native code]\n}" (function) [writable | configurable | isOwn] 49 Internal 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 55 Evaluating expression... 56 Getting own properties... 57 Properties: 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 66 Evaluating expression... 67 Getting own properties... 68 Properties: 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 77 Evaluating expression... 78 Getting own properties... 79 Properties: 80 "name" => "bound " (string) [configurable | isOwn] 81 "length" => 3 (number) [configurable | isOwn] 82 "__proto__" => "function () {\n [native code]\n}" (function) [writable | configurable | isOwn] 83 Internal Properties: 84 "targetFunction" => "function (a, b, c){}" (function) [] 85 "boundThis" => null (object null) [] 86 87 -- Running test case: Runtime.getProperties.BoundFunctionWithArguments 88 Evaluating expression... 89 Getting own properties... 90 Properties: 91 "name" => "bound " (string) [configurable | isOwn] 92 "length" => 0 (number) [configurable | isOwn] 93 "__proto__" => "function () {\n [native code]\n}" (function) [writable | configurable | isOwn] 94 Internal 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 100 Evaluating expression... 101 Getting own properties... 102 Properties: 103 "name" => "bound " (string) [configurable | isOwn] 104 "length" => 0 (number) [configurable | isOwn] 105 "__proto__" => "function () {\n [native code]\n}" (function) [writable | configurable | isOwn] 106 Internal Properties: 107 "targetFunction" => "function (){}" (function) [] 108 "boundThis" => null (object null) [] 109 110 -- Running test case: Runtime.getProperties.BoundFunctionNoParametersWithArguments 111 Evaluating expression... 112 Getting own properties... 113 Properties: 114 "name" => "bound " (string) [configurable | isOwn] 115 "length" => 0 (number) [configurable | isOwn] 116 "__proto__" => "function () {\n [native code]\n}" (function) [writable | configurable | isOwn] 117 Internal Properties: 118 "targetFunction" => "function (){}" (function) [] 119 "boundThis" => null (object null) [] 120 "boundArgs" => "Array" (object array) [] 121 122 -- Running test case: Runtime.getProperties.Promise.Resolved 123 Evaluating expression... 124 Getting own properties... 125 Properties: 126 "__proto__" => "Promise" (object) [writable | configurable | isOwn] 127 Internal Properties: 128 "status" => "resolved" (string) [] 129 "result" => 123 (number) [] 130 131 -- Running test case: Runtime.getProperties.Promise.Rejected 132 Evaluating expression... 133 Getting own properties... 134 Properties: 135 "__proto__" => "Promise" (object) [writable | configurable | isOwn] 136 Internal Properties: 137 "status" => "rejected" (string) [] 138 "result" => 123 (number) [] 139 140 -- Running test case: Runtime.getProperties.fetchStart.Object 141 Evaluating expression... 142 Getting own properties with fetchStart 5... 143 Properties: 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 151 Evaluating expression... 152 Getting own properties with fetchCount 5... 153 ASSERT: Should only get 5 properties. 154 Properties: 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 163 Evaluating expression... 164 Getting own properties with fetchStart 3 with fetchCount 4... 165 Properties: 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 172 Evaluating expression... 173 Getting own properties with fetchStart 5... 174 Properties: 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 182 Evaluating expression... 183 Getting own properties with fetchCount 5... 184 ASSERT: Should only get 5 properties. 185 Properties: 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 194 Evaluating expression... 195 Getting own properties with fetchStart 3 with fetchCount 4... 196 Properties: 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 2 2 <head> 3 3 <script src="../../http/tests/inspector/resources/protocol-test.js"></script> 4 <script src="resources/property-descriptor-utilities.js"></script> 4 5 <script> 5 6 function test() 6 7 { 7 varsuite = ProtocolTest.createAsyncSuite("Runtime.getProperties");8 let suite = ProtocolTest.createAsyncSuite("Runtime.getProperties"); 8 9 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}) { 38 11 suite.addTestCase({ 39 12 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({ 44 16 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); 51 20 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 }, 63 55 }); 64 56 } 65 57 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 }); 74 63 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 }); 81 69 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 }); 91 75 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 }); 95 158 } 159 160 suite.runTestCasesAndFinish(); 96 161 } 97 162 </script> 98 163 </head> 99 164 <body onLoad="runTest()"> 165 <p>Tests for the Runtime.getProperties command.</p> 100 166 </body> 101 167 </html> -
trunk/Source/JavaScriptCore/ChangeLog
r250086 r250087 1 2019-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 1 56 2019-09-18 Joonghun Park <pjh0718@gmail.com> 2 57 -
trunk/Source/JavaScriptCore/inspector/InjectedScript.cpp
r243161 r250087 165 165 } 166 166 167 void InjectedScript::getProperties(ErrorString& errorString, const String& objectId, bool ownProperties, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties) 168 { 167 void 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 169 172 Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getProperties"_s, inspectorEnvironment()->functionCallHandler()); 170 173 function.appendArgument(objectId); 171 174 function.appendArgument(ownProperties); 175 function.appendArgument(fetchStart); 176 function.appendArgument(fetchCount); 172 177 function.appendArgument(generatePreview); 173 178 … … 181 186 } 182 187 183 void InjectedScript::getDisplayableProperties(ErrorString& errorString, const String& objectId, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties) 184 { 188 void 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 185 193 Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getDisplayableProperties"_s, inspectorEnvironment()->functionCallHandler()); 186 194 function.appendArgument(objectId); 195 function.appendArgument(fetchStart); 196 function.appendArgument(fetchCount); 187 197 function.appendArgument(generatePreview); 188 198 … … 212 222 } 213 223 214 void InjectedScript::getCollectionEntries(ErrorString& errorString, const String& objectId, const String& objectGroup, int startIndex, int numberToFetch, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries) 215 { 224 void 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 216 229 Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getCollectionEntries"_s, inspectorEnvironment()->functionCallHandler()); 217 230 function.appendArgument(objectId); 218 231 function.appendArgument(objectGroup); 219 function.appendArgument( startIndex);220 function.appendArgument( numberToFetch);232 function.appendArgument(fetchStart); 233 function.appendArgument(fetchCount); 221 234 222 235 RefPtr<JSON::Value> result = makeCall(function); -
trunk/Source/JavaScriptCore/inspector/InjectedScript.h
r243161 r250087 69 69 void functionDetails(ErrorString&, JSC::JSValue, RefPtr<Protocol::Debugger::FunctionDetails>& result); 70 70 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); 73 73 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); 75 75 void saveResult(ErrorString&, const String& callArgumentJSON, Optional<int>& savedResultIndex); 76 76 -
trunk/Source/JavaScriptCore/inspector/InjectedScriptSource.js
r249445 r250087 231 231 } 232 232 233 getProperties(objectId, ownProperties, generatePreview) 234 { 235 let nativeGettersAsValues = false; 233 getProperties(objectId, ownProperties, fetchStart, fetchCount, generatePreview) 234 { 236 235 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 { 243 241 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}); 245 243 } 246 244 … … 270 268 } 271 269 272 getCollectionEntries(objectId, objectGroupName, startIndex, numberToFetch)270 getCollectionEntries(objectId, objectGroupName, fetchStart, fetchCount) 273 271 { 274 272 let parsedObjectId = this._parseObjectId(objectId); … … 282 280 return; 283 281 284 let entries = this._entries(object, InjectedScriptHost.subtype(object), startIndex, numberToFetch);282 let entries = this._entries(object, InjectedScriptHost.subtype(object), fetchStart, fetchCount); 285 283 return entries.map(function(entry) { 286 284 entry.value = RemoteObject.create(entry.value, objectGroupName, false, true); … … 582 580 } 583 581 584 _getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues)582 _getProperties(objectId, collectionMode, {fetchStart, fetchCount, generatePreview, nativeGettersAsValues}) 585 583 { 586 584 let parsedObjectId = this._parseObjectId(objectId); … … 594 592 return false; 595 593 596 let descriptors = this._propertyDescriptors(object, collectionMode, nativeGettersAsValues);594 let descriptors = this._propertyDescriptors(object, collectionMode, {fetchStart, fetchCount, nativeGettersAsValues}); 597 595 598 596 for (let i = 0; i < descriptors.length; ++i) { … … 604 602 if ("value" in descriptor) 605 603 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;610 604 if ("symbol" in descriptor) 611 605 descriptor.symbol = RemoteObject.create(descriptor.symbol, objectGroupName); … … 625 619 let property = internalProperties[i]; 626 620 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) 631 622 descriptor.isOwn = true; 632 }633 623 descriptors.push(descriptor); 634 624 } … … 636 626 } 637 627 638 _propertyDescriptors(object, collectionMode, nativeGettersAsValues)628 _propertyDescriptors(object, collectionMode, {fetchStart, fetchCount, nativeGettersAsValues}) 639 629 { 640 630 if (InjectedScriptHost.subtype(object) === "proxy") … … 643 633 let descriptors = []; 644 634 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; 645 645 646 646 function createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, possibleNativeBindingGetter) 647 647 { 648 648 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 } 650 658 if (possibleNativeBindingGetter) 651 659 fakeDescriptor.nativeGetter = true; … … 694 702 { 695 703 for (let i = 0; i < properties.length; ++i) { 704 if (count && descriptors.length === count) 705 break; 706 696 707 let property = properties[i]; 697 708 if (nameProcessed.has(property) || property === "__proto__") … … 707 718 // FIXME: Bad descriptor. Can we get here? 708 719 // 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); 710 721 processDescriptor(fakeDescriptor, isOwnProperty); 711 722 continue; … … 741 752 } 742 753 743 // FIXME: <https://webkit.org/b/143589> Web Inspector: Better handling for large collections in Object Trees744 // For array types with a large length we attempt to skip getOwnPropertyNames and instead just sublist of indexes.745 754 let isArrayLike = false; 746 755 try { … … 751 760 let isOwnProperty = o === object; 752 761 762 // FIXME: <https://webkit.org/b/201861> Web Inspector: show autocomplete entries for non-index properties on arrays 753 763 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 756 766 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; 759 776 } 760 777 … … 763 780 } 764 781 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) 775 795 { 776 796 let entries = []; … … 778 798 // FIXME: This is observable if the page overrides Set.prototype[Symbol.iterator]. 779 799 for (let value of object) { 780 if ( skip> 0) {781 skip--;800 if (fetchStart > 0) { 801 fetchStart--; 782 802 continue; 783 803 } … … 785 805 entries.push({value}); 786 806 787 if ( numberToFetch && entries.length === numberToFetch)807 if (fetchCount && entries.length === fetchCount) 788 808 break; 789 809 } … … 792 812 } 793 813 794 _getMapEntries(object, skip, numberToFetch)814 _getMapEntries(object, fetchStart, fetchCount) 795 815 { 796 816 let entries = []; … … 798 818 // FIXME: This is observable if the page overrides Map.prototype[Symbol.iterator]. 799 819 for (let [key, value] of object) { 800 if ( skip> 0) {801 skip--;820 if (fetchStart > 0) { 821 fetchStart--; 802 822 continue; 803 823 } … … 805 825 entries.push({key, value}); 806 826 807 if ( numberToFetch && entries.length === numberToFetch)827 if (fetchCount && entries.length === fetchCount) 808 828 break; 809 829 } … … 812 832 } 813 833 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) 830 850 { 831 851 if (subtype === "set") 832 return this._getSetEntries(object, startIndex, numberToFetch);852 return this._getSetEntries(object, fetchStart, fetchCount); 833 853 if (subtype === "map") 834 return this._getMapEntries(object, startIndex, numberToFetch);854 return this._getMapEntries(object, fetchStart, fetchCount); 835 855 if (subtype === "weakmap") 836 return this._getWeakMapEntries(object, numberToFetch);856 return this._getWeakMapEntries(object, fetchCount); 837 857 if (subtype === "weakset") 838 return this._getWeakSetEntries(object, numberToFetch);858 return this._getWeakSetEntries(object, fetchCount); 839 859 if (subtype === "iterator") 840 return this._getIteratorEntries(object, numberToFetch);860 return this._getIteratorEntries(object, fetchCount); 841 861 842 862 throw "unexpected type"; … … 1131 1151 1132 1152 // 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}); 1135 1154 this._appendPropertyPreviews(object, preview, descriptors, false, propertiesThreshold, firstLevelKeys, secondLevelKeys); 1136 1155 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) -
trunk/Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
r249175 r250087 486 486 VM& vm = exec->vm(); 487 487 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)); 490 489 if (!weakMap) 491 490 return jsUndefined(); 492 491 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()); 499 496 500 497 JSArray* array = constructEmptyArray(exec, nullptr); 501 498 RETURN_IF_EXCEPTION(scope, JSValue()); 502 503 MarkedArgumentBuffer buffer;504 weakMap->takeSnapshot(buffer, numberToFetch);505 499 506 500 for (unsigned index = 0; index < buffer.size(); index += 2) { … … 536 530 VM& vm = exec->vm(); 537 531 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)); 540 533 if (!weakSet) 541 534 return jsUndefined(); 542 535 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()); 549 540 550 541 JSArray* array = constructEmptyArray(exec, nullptr); 551 542 RETURN_IF_EXCEPTION(scope, JSValue()); 552 553 MarkedArgumentBuffer buffer;554 weakSet->takeSnapshot(buffer, numberToFetch);555 543 556 544 for (unsigned index = 0; index < buffer.size(); ++index) { -
trunk/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp
r250005 r250087 57 57 } 58 58 59 static int asInt(const int* i) 60 { 61 if (i) 62 return *i; 63 return 0; 64 } 65 59 66 InspectorRuntimeAgent::InspectorRuntimeAgent(AgentContext& context) 60 67 : InspectorAgentBase("Runtime"_s) … … 192 199 } 193 200 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)201 void 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) 195 202 { 196 203 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); 197 204 if (injectedScript.hasNoValue()) { 198 205 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; 199 218 return; 200 219 } … … 203 222 muteConsole(); 204 223 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); 207 229 208 230 unmuteConsole(); … … 210 232 } 211 233 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)234 void 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) 213 235 { 214 236 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); 215 237 if (injectedScript.hasNoValue()) { 216 238 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; 217 251 return; 218 252 } … … 221 255 muteConsole(); 222 256 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); 225 262 226 263 unmuteConsole(); … … 228 265 } 229 266 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)267 void InspectorRuntimeAgent::getCollectionEntries(ErrorString& errorString, const String& objectId, const String* objectGroup, const int* fetchStart, const int* fetchCount, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries) 231 268 { 232 269 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); … … 236 273 } 237 274 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); 242 288 } 243 289 -
trunk/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h
r249132 r250087 67 67 void releaseObject(ErrorString&, const ErrorString& objectId) final; 68 68 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; 72 72 void saveResult(ErrorString&, const JSON::Object& callArgument, const int* executionContextId, Optional<int>& savedResultIndex) final; 73 73 void setSavedResultAlias(ErrorString&, const String* alias) final; -
trunk/Source/JavaScriptCore/inspector/protocol/Runtime.json
r248898 r250087 77 77 { "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)." }, 78 78 { "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." },81 79 { "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." }, 82 82 { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." }, 83 83 { "name": "symbol", "optional": true, "$ref": "Runtime.RemoteObject", "description": "Property symbol object, if the property is a symbol." }, … … 276 276 { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, 277 277 { "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." }, 278 280 { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for property values." } 279 281 ], 280 282 "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." } 283 285 ] 284 286 }, … … 288 290 "parameters": [ 289 291 { "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." }, 290 294 { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for property values." } 291 295 ], 292 296 "returns": [ 293 297 { "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." } 295 299 ] 296 300 }, … … 301 305 { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the collection to get entries for." }, 302 306 { "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." } 305 309 ], 306 310 "returns": [ -
trunk/Source/WebInspectorUI/ChangeLog
r250076 r250087 1 2019-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 1 54 2019-09-18 Joseph Pecoraro <pecoraro@apple.com> 2 55 -
trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
r249786 r250087 1006 1006 localizedStrings["Show %d More"] = "Show %d More"; 1007 1007 localizedStrings["Show All"] = "Show All"; 1008 localizedStrings["Show All (%d More)"] = "Show All (%d More)"; 1008 1009 localizedStrings["Show All Nodes (%d More)"] = "Show All Nodes (%d More)"; 1009 1010 localizedStrings["Show Console"] = "Show Console"; … … 1013 1014 localizedStrings["Show Icons"] = "Show Icons"; 1014 1015 localizedStrings["Show Jump to Effective Property Button"] = "Show Jump to Effective Property Button"; 1016 localizedStrings["Show More"] = "Show More"; 1015 1017 localizedStrings["Show Path"] = "Show Path"; 1016 1018 localizedStrings["Show Remaining (%d)"] = "Show Remaining (%d)"; -
trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js
r249846 r250087 252 252 function receivedArrayPropertyNames(propertyNames) 253 253 { 254 // FIXME: <https://webkit.org/b/143589> Web Inspector: Better handling for large collections in Object Trees255 // If there was an array like object, we generate autocompletion up to 1000 indexes, but this should256 // handle a list with arbitrary length.257 254 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 258 256 var max = Math.min(propertyNames.length, 1000); 259 257 for (var i = 0; i < max; ++i) -
trunk/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js
r248287 r250087 186 186 else 187 187 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 });204 188 } 205 189 -
trunk/Source/WebInspectorUI/UserInterface/Protocol/RemoteObject.js
r249786 r250087 295 295 } 296 296 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 = {}) 301 301 { 302 302 if (!this._objectId || this._isSymbol() || this._isFakeObject()) { … … 308 308 // Here we do our best to reimplement it by getting all properties and reducing them down. 309 309 if (!this._target.RuntimeAgent.getDisplayableProperties) { 310 this._ target.RuntimeAgent.getProperties(this._objectId, function(error, allProperties){310 this._getProperties(options, (error, allProperties) => { 311 311 var ownOrGetterPropertiesList = []; 312 312 if (allProperties) { … … 325 325 } 326 326 } 327 328 327 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); 334 333 } 335 334 … … 405 404 } 406 405 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 { 414 408 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); 415 411 416 412 // 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); 419 414 420 415 let objectGroup = this.isWeakCollection() ? this._weakCollectionObjectGroup() : ""; 421 416 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))); 425 420 }); 426 421 } … … 546 541 } 547 542 548 var fakeDescriptor = {name: propertyName, value: result, writable: true, configurable: true , enumerable: false};543 var fakeDescriptor = {name: propertyName, value: result, writable: true, configurable: true}; 549 544 var fakePropertyDescriptor = new WI.PropertyDescriptor(fakeDescriptor, null, true, false, false, false); 550 545 callback(fakePropertyDescriptor); … … 630 625 } 631 626 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 632 651 _getPropertyDescriptorsResolver(callback, error, properties, internalProperties) 633 652 { -
trunk/Source/WebInspectorUI/UserInterface/Views/ObjectTreePropertyTreeElement.js
r242076 r250087 33 33 this._prototypeName = prototypeName; 34 34 35 this._fetchStart = 0; 36 this._fetchEnd = WI.ObjectTreeView.showMoreFetchCount; 37 this._fetchEndIndex = 0; 38 35 39 this.mainTitle = this._titleFragment(); 36 40 this.addClassName("object-tree-property"); … … 56 60 onpopulate() 57 61 { 62 if (this.children.length && !this.shouldRefreshChildren) 63 return; 64 65 this._fetchStart = 0; 66 this._fetchEndIndex = 0; 58 67 this._updateChildren(); 59 68 } … … 319 328 _updateChildren() 320 329 { 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 = { 325 348 ownProperties: true, 326 349 generatePreview: true, 327 350 }; 328 351 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 330 357 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); 332 359 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); 334 361 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); 336 363 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); 351 365 } 352 366 353 367 _updateEntries(entries, propertyPath, mode) 354 368 { 355 for (var entry of entries) { 369 let resolvedValue = this.resolvedValue(); 370 371 entries.forEach((entry, i) => { 356 372 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++); 359 375 } else 360 this. appendChild(new WI.ObjectTreeSetIndexTreeElement(entry.value, propertyPath));361 } 376 this.insertChild(new WI.ObjectTreeSetIndexTreeElement(entry.value, propertyPath), this._fetchEndIndex++); 377 }); 362 378 363 379 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 } 374 404 } 375 405 … … 397 427 continue; 398 428 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)); 402 437 continue; 438 } 403 439 404 440 if (isArray && isPropertyMode) { 405 441 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++); 409 443 } 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++); 414 445 } 415 446 416 447 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 }); 419 462 } 420 463 } -
trunk/Source/WebInspectorUI/UserInterface/Views/ObjectTreeView.css
r248766 r250087 134 134 } 135 135 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 136 142 .object-tree-property :matches(.formatted-string, .formatted-regexp) { 137 143 white-space: nowrap; -
trunk/Source/WebInspectorUI/UserInterface/Views/ObjectTreeView.js
r249301 r250087 47 47 this._inConsole = true; 48 48 49 this._fetchStart = 0; 50 this._fetchEnd = ObjectTreeView.showMoreFetchCount; 51 this._fetchEndIndex = 0; 52 49 53 // Always force expanding for classes. 50 54 if (this._object.isClass()) … … 98 102 emptyMessageElement.textContent = message; 99 103 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 }); 100 171 } 101 172 … … 255 326 update() 256 327 { 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 = { 258 338 ownProperties: true, 259 339 generatePreview: true, 260 340 }; 261 341 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); 264 364 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); 268 368 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); 287 370 } 288 371 289 372 _updateEntries(entries, propertyPath) 290 373 { 291 for (var entry of entries){374 entries.forEach((entry, i) => { 292 375 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++); 295 378 } 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 }); 298 381 299 382 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 } 309 407 } 310 408 … … 329 427 continue; 330 428 hadProto = true; 429 this._outline.appendChild(new WI.ObjectTreePropertyTreeElement(propertyDescriptor, propertyPath, this._mode, this._prototypeNameOverride)); 430 continue; 331 431 } 332 432 333 433 if (isArray && isPropertyMode) { 334 434 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++); 338 436 } 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++); 340 438 } 341 439 342 440 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 }); 345 455 } 346 456 }
Note: See TracChangeset
for help on using the changeset viewer.