chrome-devtools-frontend 1.0.1539728 → 1.0.1541169

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/eslint.config.mjs +167 -151
  2. package/front_end/core/common/Debouncer.ts +2 -2
  3. package/front_end/core/common/Gzip.ts +1 -1
  4. package/front_end/core/common/Revealer.ts +5 -0
  5. package/front_end/core/common/Throttler.ts +3 -3
  6. package/front_end/core/host/GdpClient.ts +4 -0
  7. package/front_end/core/host/InspectorFrontendHost.ts +10 -10
  8. package/front_end/core/protocol_client/DevToolsCDPConnection.ts +181 -0
  9. package/front_end/core/protocol_client/InspectorBackend.ts +36 -203
  10. package/front_end/core/protocol_client/protocol_client.ts +2 -2
  11. package/front_end/core/sdk/DebuggerModel.ts +3 -16
  12. package/front_end/core/sdk/NetworkManager.ts +16 -11
  13. package/front_end/core/sdk/RemoteObject.ts +4 -0
  14. package/front_end/core/sdk/Target.ts +3 -6
  15. package/front_end/core/sdk/TargetManager.ts +1 -2
  16. package/front_end/core/sdk/sdk-meta.ts +0 -35
  17. package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +1 -3
  18. package/front_end/entrypoints/node_app/app/NodeMain.ts +3 -2
  19. package/front_end/entrypoints/shell/shell.ts +1 -0
  20. package/front_end/entrypoints/trace_app/trace_app.ts +1 -0
  21. package/front_end/generated/Deprecation.ts +8 -0
  22. package/front_end/generated/InspectorBackendCommands.ts +8 -5
  23. package/front_end/generated/SupportedCSSProperties.js +58 -4
  24. package/front_end/generated/protocol.ts +60 -4
  25. package/front_end/models/ai_assistance/EvaluateAction.ts +88 -5
  26. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +121 -56
  27. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +104 -62
  28. package/front_end/models/ai_assistance/injected.ts +15 -2
  29. package/front_end/models/ai_assistance/performance/AIQueries.ts +56 -2
  30. package/front_end/{panels/issues → models/issues_manager}/IssueAggregator.ts +83 -65
  31. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  32. package/front_end/models/live-metrics/web-vitals-injected/README.md +1 -1
  33. package/front_end/models/trace/Processor.ts +5 -4
  34. package/front_end/models/trace/Styles.ts +1 -1
  35. package/front_end/models/trace/insights/types.ts +1 -1
  36. package/front_end/models/trace/types/TraceEvents.ts +1 -1
  37. package/front_end/models/workspace/IgnoreListManager.ts +41 -47
  38. package/front_end/models/workspace/workspace-meta.ts +40 -0
  39. package/front_end/panels/ai_assistance/PatchWidget.ts +22 -12
  40. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -1
  41. package/front_end/panels/animation/AnimationTimeline.ts +4 -4
  42. package/front_end/panels/animation/AnimationUI.ts +28 -34
  43. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +4 -4
  44. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +2 -2
  45. package/front_end/panels/elements/ElementsTreeElement.ts +37 -9
  46. package/front_end/panels/elements/LayoutPane.ts +2 -2
  47. package/front_end/panels/elements/PropertiesWidget.ts +3 -2
  48. package/front_end/panels/elements/components/AdornerManager.ts +9 -9
  49. package/front_end/panels/elements/layoutPane.css +5 -9
  50. package/front_end/panels/event_listeners/EventListenersView.ts +10 -6
  51. package/front_end/panels/explain/components/ConsoleInsight.ts +498 -449
  52. package/front_end/panels/issues/AffectedResourcesView.ts +3 -4
  53. package/front_end/panels/issues/CorsIssueDetailsView.ts +1 -2
  54. package/front_end/panels/issues/IssueView.ts +1 -1
  55. package/front_end/panels/issues/IssuesPane.ts +12 -15
  56. package/front_end/panels/issues/issues.ts +0 -2
  57. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +3 -3
  58. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +2 -2
  59. package/front_end/panels/network/NetworkDataGridNode.ts +2 -1
  60. package/front_end/panels/network/RequestConditionsDrawer.ts +149 -46
  61. package/front_end/panels/network/RequestPayloadView.ts +2 -1
  62. package/front_end/panels/network/RequestTimingView.ts +17 -10
  63. package/front_end/panels/network/components/RequestHeadersView.ts +24 -17
  64. package/front_end/panels/network/network-meta.ts +11 -0
  65. package/front_end/panels/protocol_monitor/JSONEditor.ts +2 -2
  66. package/front_end/panels/recorder/RecorderController.ts +6 -7
  67. package/front_end/panels/recorder/models/RecordingPlayer.ts +3 -3
  68. package/front_end/panels/settings/components/SyncSection.ts +1 -1
  69. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +1 -1
  70. package/front_end/panels/sources/BreakpointsView.ts +3 -3
  71. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  72. package/front_end/panels/sources/ScopeChainSidebarPane.ts +4 -3
  73. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +4 -3
  74. package/front_end/panels/sources/breakpointsView.css +1 -1
  75. package/front_end/panels/sources/sourcesPanel.css +2 -2
  76. package/front_end/panels/timeline/TimelineFlameChartView.ts +3 -3
  77. package/front_end/panels/timeline/TimelinePanel.ts +3 -3
  78. package/front_end/panels/timeline/components/FieldSettingsDialog.ts +9 -5
  79. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +16 -10
  80. package/front_end/panels/timeline/components/LiveMetricsView.ts +20 -9
  81. package/front_end/panels/timeline/components/MetricCard.ts +4 -2
  82. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +2 -0
  83. package/front_end/services/puppeteer/PuppeteerConnection.ts +2 -1
  84. package/front_end/third_party/chromium/README.chromium +1 -1
  85. package/front_end/third_party/puppeteer/README.chromium +2 -2
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +5 -0
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +1 -0
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +3 -0
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +1 -0
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +21 -0
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/ExtensionTransport.d.ts.map +1 -1
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/ExtensionTransport.js +5 -1
  99. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/ExtensionTransport.js.map +1 -1
  100. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +1 -0
  101. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +6 -0
  103. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  104. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.d.ts +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.js +29 -27
  107. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.js.map +1 -1
  108. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  110. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  111. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +5 -0
  113. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +61 -26
  114. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +5 -0
  115. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  116. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +1 -0
  118. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
  119. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +3 -0
  120. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
  121. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +1 -0
  122. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  123. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +21 -0
  124. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  125. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/ExtensionTransport.d.ts.map +1 -1
  126. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/ExtensionTransport.js +5 -1
  127. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/ExtensionTransport.js.map +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +1 -0
  129. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +6 -0
  131. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  132. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.d.ts +1 -1
  133. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.js +30 -28
  135. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.js.map +1 -1
  136. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  137. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  138. package/front_end/third_party/puppeteer/package/lib/types.d.ts +5 -0
  139. package/front_end/third_party/puppeteer/package/package.json +1 -1
  140. package/front_end/third_party/puppeteer/package/src/api/Page.ts +6 -0
  141. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +4 -0
  142. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +32 -0
  143. package/front_end/third_party/puppeteer/package/src/cdp/ExtensionTransport.ts +5 -1
  144. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +8 -0
  145. package/front_end/third_party/puppeteer/package/src/cdp/TargetManager.ts +36 -43
  146. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  147. package/front_end/ui/components/dialogs/ButtonDialog.ts +15 -5
  148. package/front_end/ui/components/expandable_list/ExpandableList.ts +1 -1
  149. package/front_end/ui/components/helpers/helpers.ts +0 -2
  150. package/front_end/ui/components/markdown_view/MarkdownView.ts +1 -0
  151. package/front_end/ui/components/menus/Menu.ts +5 -3
  152. package/front_end/ui/components/snackbars/Snackbars.docs.ts +46 -0
  153. package/front_end/ui/components/survey_link/SurveyLink.docs.ts +22 -0
  154. package/front_end/ui/components/tree_outline/TreeOutline.ts +1 -2
  155. package/front_end/ui/{components/docs/context_menu/basic.ts → legacy/ContextMenu.docs.ts} +58 -25
  156. package/front_end/ui/legacy/SelectMenu.docs.ts +14 -0
  157. package/front_end/ui/legacy/UIUtils.ts +2 -1
  158. package/front_end/ui/legacy/components/inline_editor/BezierEditor.ts +1 -1
  159. package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +3 -1
  160. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +558 -439
  161. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +3 -3
  162. package/front_end/ui/legacy/components/perf_ui/pieChart.css +1 -1
  163. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  164. package/front_end/ui/legacy/inspectorCommon.css +3 -2
  165. package/mcp/mcp.ts +15 -1
  166. package/package.json +2 -1
  167. package/front_end/core/protocol_client/NodeURL.ts +0 -40
  168. package/front_end/ui/components/docs/combo_box/basic.html +0 -20
  169. package/front_end/ui/components/docs/combo_box/basic.ts +0 -49
  170. package/front_end/ui/components/docs/context_menu/basic.html +0 -45
  171. package/front_end/ui/components/docs/legacy_color_invert/basic.html +0 -77
  172. package/front_end/ui/components/docs/legacy_color_invert/basic.ts +0 -98
  173. package/front_end/ui/components/docs/linkifier/simple-url.html +0 -25
  174. package/front_end/ui/components/docs/linkifier/simple-url.ts +0 -25
  175. package/front_end/ui/components/docs/panel_feedback/basic.html +0 -25
  176. package/front_end/ui/components/docs/panel_feedback/basic.ts +0 -21
  177. package/front_end/ui/components/docs/panel_feedback/button.html +0 -25
  178. package/front_end/ui/components/docs/panel_feedback/button.ts +0 -19
  179. package/front_end/ui/components/docs/panel_introduction_steps/basic.html +0 -25
  180. package/front_end/ui/components/docs/panel_introduction_steps/basic.ts +0 -28
  181. package/front_end/ui/components/docs/perf_piechart/basic-with-legend.html +0 -20
  182. package/front_end/ui/components/docs/perf_piechart/basic-with-legend.ts +0 -20
  183. package/front_end/ui/components/docs/perf_piechart/basic-without-legend.html +0 -20
  184. package/front_end/ui/components/docs/perf_piechart/basic-without-legend.ts +0 -18
  185. package/front_end/ui/components/docs/snackbars/basic.html +0 -17
  186. package/front_end/ui/components/docs/snackbars/basic.ts +0 -50
  187. package/front_end/ui/components/docs/survey_link/basic.html +0 -20
  188. package/front_end/ui/components/docs/survey_link/basic.ts +0 -28
  189. package/front_end/ui/components/docs/tree_outline/basic.html +0 -33
  190. package/front_end/ui/components/docs/tree_outline/basic.ts +0 -38
  191. package/front_end/ui/components/docs/tree_outline/custom-renderers.html +0 -32
  192. package/front_end/ui/components/docs/tree_outline/custom-renderers.ts +0 -61
  193. package/front_end/ui/components/docs/tree_outline/lazy-children.html +0 -32
  194. package/front_end/ui/components/docs/tree_outline/lazy-children.ts +0 -91
  195. package/front_end/ui/components/docs/tree_outline/sample-data.ts +0 -67
  196. package/front_end/ui/components/helpers/directives.ts +0 -38
@@ -34,10 +34,12 @@ import * as Host from '../../../../core/host/host.js';
34
34
  import * as i18n from '../../../../core/i18n/i18n.js';
35
35
  import * as Platform from '../../../../core/platform/platform.js';
36
36
  import * as SDK from '../../../../core/sdk/sdk.js';
37
+ import type * as Protocol from '../../../../generated/protocol.js';
37
38
  import * as TextUtils from '../../../../models/text_utils/text_utils.js';
38
39
  import * as uiI18n from '../../../../ui/i18n/i18n.js';
39
40
  import * as IconButton from '../../../components/icon_button/icon_button.js';
40
41
  import * as TextEditor from '../../../components/text_editor/text_editor.js';
42
+ import {Directives, html, render} from '../../../lit/lit.js';
41
43
  import * as VisualLogging from '../../../visual_logging/visual_logging.js';
42
44
  import * as UI from '../../legacy.js';
43
45
  import type * as Components from '../utils/utils.js';
@@ -48,6 +50,7 @@ import objectPropertiesSectionStyles from './objectPropertiesSection.css.js';
48
50
  import objectValueStyles from './objectValue.css.js';
49
51
  import {createSpansForNodeTitle, RemoteObjectPreviewFormatter} from './RemoteObjectPreviewFormatter.js';
50
52
 
53
+ const {ifDefined} = Directives;
51
54
  const UIStrings = {
52
55
  /**
53
56
  * @description Text in Object Properties Section
@@ -136,15 +139,214 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
136
139
  const EXPANDABLE_MAX_LENGTH = 50;
137
140
  const EXPANDABLE_MAX_DEPTH = 100;
138
141
 
139
- const parentMap = new WeakMap<SDK.RemoteObject.RemoteObjectProperty, SDK.RemoteObject.RemoteObject|null>();
140
142
  const objectPropertiesSectionMap = new WeakMap<Element, ObjectPropertiesSection>();
141
143
 
144
+ interface NodeChildren {
145
+ properties?: ObjectTreeNode[];
146
+ internalProperties?: ObjectTreeNode[];
147
+ arrayRanges?: ArrayGroupTreeNode[];
148
+ }
149
+
150
+ abstract class ObjectTreeNodeBase {
151
+ #children?: NodeChildren;
152
+ protected extraProperties: ObjectTreeNode[] = [];
153
+ constructor(
154
+ readonly parent?: ObjectTreeNodeBase,
155
+ readonly propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED) {
156
+ }
157
+
158
+ abstract get object(): SDK.RemoteObject.RemoteObject|undefined;
159
+
160
+ removeChildren(): void {
161
+ this.#children = undefined;
162
+ }
163
+
164
+ removeChild(child: ObjectTreeNodeBase): void {
165
+ remove(this.#children?.arrayRanges, child);
166
+ remove(this.#children?.internalProperties, child);
167
+ remove(this.#children?.properties, child);
168
+
169
+ function remove<T>(array: T[]|undefined, element: T): void {
170
+ if (!array) {
171
+ return;
172
+ }
173
+ const index = array.indexOf(element);
174
+ if (index >= 0) {
175
+ array.splice(index, 1);
176
+ }
177
+ }
178
+ }
179
+
180
+ protected selfOrParentIfInternal(): ObjectTreeNodeBase {
181
+ return this;
182
+ }
183
+
184
+ async children(): Promise<NodeChildren> {
185
+ if (!this.#children) {
186
+ this.#children = await this.populateChildren();
187
+ }
188
+ return this.#children;
189
+ }
190
+
191
+ protected async populateChildren(): Promise<NodeChildren> {
192
+ const object = this.object;
193
+ if (!object) {
194
+ return {};
195
+ }
196
+
197
+ const effectiveParent = this.selfOrParentIfInternal();
198
+
199
+ if (this.arrayLength > ARRAY_LOAD_THRESHOLD) {
200
+ const ranges = await arrayRangeGroups(object, 0, this.arrayLength - 1);
201
+ const arrayRanges = ranges?.ranges.map(
202
+ ([fromIndex, toIndex, count]) => new ArrayGroupTreeNode(object, {fromIndex, toIndex, count}));
203
+ if (!arrayRanges) {
204
+ return {};
205
+ }
206
+
207
+ const {properties: objectProperties, internalProperties: objectInternalProperties} =
208
+ await SDK.RemoteObject.RemoteObject.loadFromObjectPerProto(
209
+ this.object, true /* generatePreview */, true /* nonIndexedPropertiesOnly */);
210
+
211
+ const properties = objectProperties?.map(p => new ObjectTreeNode(p, undefined, effectiveParent, undefined));
212
+
213
+ const internalProperties =
214
+ objectInternalProperties?.map(p => new ObjectTreeNode(p, undefined, effectiveParent, undefined));
215
+ return {arrayRanges, properties, internalProperties};
216
+ }
217
+
218
+ let objectProperties: SDK.RemoteObject.RemoteObjectProperty[]|null = null;
219
+ let objectInternalProperties: SDK.RemoteObject.RemoteObjectProperty[]|null = null;
220
+ switch (this.propertiesMode) {
221
+ case ObjectPropertiesMode.ALL:
222
+ ({properties: objectProperties} =
223
+ await object.getAllProperties(false /* accessorPropertiesOnly */, true /* generatePreview */));
224
+ break;
225
+ case ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED:
226
+ ({properties: objectProperties, internalProperties: objectInternalProperties} =
227
+ await SDK.RemoteObject.RemoteObject.loadFromObjectPerProto(object, true /* generatePreview */));
228
+ break;
229
+ }
230
+
231
+ const properties = objectProperties?.map(p => new ObjectTreeNode(p, undefined, effectiveParent, undefined));
232
+ properties?.push(...this.extraProperties);
233
+
234
+ const internalProperties =
235
+ objectInternalProperties?.map(p => new ObjectTreeNode(p, undefined, effectiveParent, undefined));
236
+ return {properties, internalProperties};
237
+ }
238
+
239
+ get hasChildren(): boolean {
240
+ return this.object?.hasChildren ?? false;
241
+ }
242
+
243
+ get arrayLength(): number {
244
+ return this.object?.arrayLength() ?? 0;
245
+ }
246
+
247
+ // This is used in web tests
248
+ async setPropertyValue(name: string|Protocol.Runtime.CallArgument, value: string): Promise<string|undefined> {
249
+ return await this.object?.setPropertyValue(name, value);
250
+ }
251
+
252
+ addExtraProperties(...properties: SDK.RemoteObject.RemoteObjectProperty[]): void {
253
+ this.extraProperties.push(...properties.map(p => new ObjectTreeNode(p, undefined, this, undefined)));
254
+ }
255
+ }
256
+
257
+ export class ObjectTree extends ObjectTreeNodeBase {
258
+ readonly #object: SDK.RemoteObject.RemoteObject;
259
+
260
+ constructor(
261
+ object: SDK.RemoteObject.RemoteObject,
262
+ propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED) {
263
+ super(undefined, propertiesMode);
264
+ this.#object = object;
265
+ }
266
+ override get object(): SDK.RemoteObject.RemoteObject {
267
+ return this.#object;
268
+ }
269
+ }
270
+
271
+ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
272
+ readonly #object: SDK.RemoteObject.RemoteObject;
273
+ readonly #range: {fromIndex: number, toIndex: number, count: number};
274
+ constructor(
275
+ object: SDK.RemoteObject.RemoteObject, range: {fromIndex: number, toIndex: number, count: number},
276
+ parent?: ObjectTreeNodeBase,
277
+ propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED) {
278
+ super(parent, propertiesMode);
279
+ this.#object = object;
280
+ this.#range = range;
281
+ }
282
+
283
+ protected override async populateChildren(): Promise<NodeChildren> {
284
+ if (this.#range.count > ArrayGroupingTreeElement.bucketThreshold) {
285
+ const ranges = await arrayRangeGroups(this.object, this.#range.fromIndex, this.#range.toIndex);
286
+ const arrayRanges = ranges?.ranges.map(
287
+ ([fromIndex, toIndex, count]) => new ArrayGroupTreeNode(this.object, {fromIndex, toIndex, count}));
288
+ return {arrayRanges};
289
+ }
290
+
291
+ const result = await this.#object.callFunction(buildArrayFragment, [
292
+ {value: this.#range.fromIndex},
293
+ {value: this.#range.toIndex},
294
+ {value: ArrayGroupingTreeElement.sparseIterationThreshold},
295
+ ]);
296
+ if (!result.object || result.wasThrown) {
297
+ return {};
298
+ }
299
+ const arrayFragment = result.object;
300
+ const allProperties =
301
+ await arrayFragment.getAllProperties(false /* accessorPropertiesOnly */, true /* generatePreview */);
302
+ arrayFragment.release();
303
+ const properties = allProperties.properties?.map(p => new ObjectTreeNode(p, this.propertiesMode, this, undefined));
304
+ properties?.push(...this.extraProperties);
305
+ properties?.sort(ObjectPropertiesSection.compareProperties);
306
+ return {properties};
307
+ }
308
+
309
+ get singular(): boolean {
310
+ return this.#range.fromIndex === this.#range.toIndex;
311
+ }
312
+
313
+ get range(): {fromIndex: number, toIndex: number, count: number} {
314
+ return this.#range;
315
+ }
316
+
317
+ override get object(): SDK.RemoteObject.RemoteObject {
318
+ return this.#object;
319
+ }
320
+ }
321
+
322
+ export class ObjectTreeNode extends ObjectTreeNodeBase {
323
+ constructor(
324
+ readonly property: SDK.RemoteObject.RemoteObjectProperty,
325
+ propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
326
+ parent?: ObjectTreeNodeBase,
327
+ readonly nonSyntheticParent?: SDK.RemoteObject.RemoteObject,
328
+ ) {
329
+ super(parent, propertiesMode);
330
+ }
331
+ override get object(): SDK.RemoteObject.RemoteObject|undefined {
332
+ return this.property.value;
333
+ }
334
+
335
+ get name(): string {
336
+ return this.property.name;
337
+ }
338
+
339
+ override selfOrParentIfInternal(): ObjectTreeNodeBase {
340
+ return this.name === '[[Prototype]]' ? (this.parent ?? this) : this;
341
+ }
342
+ }
343
+
142
344
  export const getObjectPropertiesSectionFrom = (element: Element): ObjectPropertiesSection|undefined => {
143
345
  return objectPropertiesSectionMap.get(element);
144
346
  };
145
347
 
146
348
  export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow {
147
- private readonly object: SDK.RemoteObject.RemoteObject;
349
+ private readonly root: ObjectTree;
148
350
  editable: boolean;
149
351
  readonly #objectTreeElement: RootElement;
150
352
  titleElement: Element;
@@ -153,14 +355,14 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
153
355
  object: SDK.RemoteObject.RemoteObject, title?: string|Element|null, linkifier?: Components.Linkifier.Linkifier,
154
356
  showOverflow?: boolean) {
155
357
  super();
156
- this.object = object;
358
+ this.root = new ObjectTree(object);
157
359
  this.editable = true;
158
360
  if (!showOverflow) {
159
361
  this.setHideOverflow(true);
160
362
  }
161
363
  this.setFocusable(true);
162
364
  this.setShowSelectionOnKeyboardFocus(true);
163
- this.#objectTreeElement = new RootElement(object, linkifier);
365
+ this.#objectTreeElement = new RootElement(this.root, linkifier);
164
366
  this.appendChild(this.#objectTreeElement);
165
367
  if (typeof title === 'string' || !title) {
166
368
  this.titleElement = this.element.createChild('span');
@@ -197,7 +399,7 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
197
399
  const shadowRoot = UI.UIUtils.createShadowRootWithCoreStyles(titleElement, {cssFile: objectValueStyles});
198
400
  const propertyValue =
199
401
  ObjectPropertiesSection.createPropertyValue(object, /* wasThrown */ false, /* showPreview */ true);
200
- shadowRoot.appendChild(propertyValue.element);
402
+ shadowRoot.appendChild(propertyValue);
201
403
  const objectPropertiesSection = new ObjectPropertiesSection(object, titleElement, linkifier);
202
404
  objectPropertiesSection.editable = false;
203
405
  if (skipProto) {
@@ -210,8 +412,16 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
210
412
  return objectPropertiesSection;
211
413
  }
212
414
 
415
+ // The RemoteObjectProperty overload is kept for web test compatibility for now.
213
416
  static compareProperties(
214
- propertyA: SDK.RemoteObject.RemoteObjectProperty, propertyB: SDK.RemoteObject.RemoteObjectProperty): number {
417
+ propertyA: ObjectTreeNode|SDK.RemoteObject.RemoteObjectProperty,
418
+ propertyB: ObjectTreeNode|SDK.RemoteObject.RemoteObjectProperty): number {
419
+ if (propertyA instanceof ObjectTreeNode) {
420
+ propertyA = propertyA.property;
421
+ }
422
+ if (propertyB instanceof ObjectTreeNode) {
423
+ propertyB = propertyB.property;
424
+ }
215
425
  if (!propertyA.synthetic && propertyB.synthetic) {
216
426
  return 1;
217
427
  }
@@ -269,7 +479,7 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
269
479
  }
270
480
 
271
481
  static valueElementForFunctionDescription(description?: string, includePreview?: boolean, defaultName?: string):
272
- Element {
482
+ HTMLElement {
273
483
  const valueElement = document.createElement('span');
274
484
  valueElement.classList.add('object-value-function');
275
485
  description = description || '';
@@ -352,12 +562,11 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
352
562
 
353
563
  static createPropertyValueWithCustomSupport(
354
564
  value: SDK.RemoteObject.RemoteObject, wasThrown: boolean, showPreview: boolean,
355
- linkifier?: Components.Linkifier.Linkifier, isSyntheticProperty?: boolean,
356
- variableName?: string): ObjectPropertyValue {
565
+ linkifier?: Components.Linkifier.Linkifier, isSyntheticProperty?: boolean, variableName?: string): HTMLElement {
357
566
  if (value.customPreview()) {
358
567
  const result = (new CustomPreviewComponent(value)).element;
359
568
  result.classList.add('object-properties-section-custom-section');
360
- return new ObjectPropertyValue(result);
569
+ return result;
361
570
  }
362
571
  return ObjectPropertiesSection.createPropertyValue(
363
572
  value, wasThrown, showPreview, linkifier, isSyntheticProperty, variableName);
@@ -392,9 +601,8 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
392
601
 
393
602
  static createPropertyValue(
394
603
  value: SDK.RemoteObject.RemoteObject, wasThrown: boolean, showPreview: boolean,
395
- linkifier?: Components.Linkifier.Linkifier, isSyntheticProperty = false,
396
- variableName?: string): ObjectPropertyValue {
397
- let propertyValue;
604
+ linkifier?: Components.Linkifier.Linkifier, isSyntheticProperty = false, variableName?: string): HTMLElement {
605
+ let propertyValue: HTMLElement;
398
606
  const type = value.type;
399
607
  const subtype = value.subtype;
400
608
  const description = value.description || '';
@@ -403,31 +611,31 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
403
611
  const rawLocation = value.debuggerModel().createRawLocationByScriptId(
404
612
  value.value.scriptId, value.value.lineNumber, value.value.columnNumber);
405
613
  if (rawLocation && linkifier) {
406
- return new ObjectPropertyValue(linkifier.linkifyRawLocation(rawLocation, Platform.DevToolsPath.EmptyUrlString));
614
+ return linkifier.linkifyRawLocation(rawLocation, Platform.DevToolsPath.EmptyUrlString);
407
615
  }
408
- propertyValue = new ObjectPropertyValue(createUnknownInternalLocationElement());
616
+ propertyValue = createUnknownInternalLocationElement();
409
617
  } else if (type === 'string' && typeof description === 'string') {
410
618
  propertyValue = createStringElement();
411
619
  } else if (type === 'object' && subtype === 'trustedtype') {
412
620
  propertyValue = createTrustedTypeElement();
413
621
  } else if (type === 'function') {
414
- propertyValue = new ObjectPropertyValue(ObjectPropertiesSection.valueElementForFunctionDescription(description));
622
+ propertyValue = ObjectPropertiesSection.valueElementForFunctionDescription(description);
415
623
  } else if (type === 'object' && subtype === 'node' && description) {
416
- propertyValue = new ObjectPropertyValue(createNodeElement());
624
+ propertyValue = createNodeElement();
417
625
  } else {
418
626
  const valueElement = document.createElement('span');
419
627
  valueElement.classList.add('object-value-' + (subtype || type));
420
628
  if (value.preview && showPreview) {
421
629
  const previewFormatter = new RemoteObjectPreviewFormatter();
422
630
  previewFormatter.appendObjectPreview(valueElement, value.preview, false /* isEntry */);
423
- propertyValue = new ObjectPropertyValue(valueElement);
424
- UI.Tooltip.Tooltip.install(propertyValue.element as HTMLElement, description || '');
631
+ propertyValue = valueElement;
632
+ UI.Tooltip.Tooltip.install(propertyValue as HTMLElement, description || '');
425
633
  } else if (description.length > maxRenderableStringLength) {
426
- propertyValue = new ExpandableTextPropertyValue(valueElement, description, EXPANDABLE_MAX_LENGTH);
634
+ propertyValue = new ExpandableTextPropertyValue(valueElement, description, EXPANDABLE_MAX_LENGTH).element;
427
635
  } else {
428
- propertyValue = new ObjectPropertyValue(valueElement);
429
- propertyValue.element.textContent = description;
430
- UI.Tooltip.Tooltip.install(propertyValue.element as HTMLElement, description);
636
+ propertyValue = valueElement;
637
+ propertyValue.textContent = description;
638
+ UI.Tooltip.Tooltip.install(propertyValue as HTMLElement, description);
431
639
  }
432
640
  if (!isSyntheticProperty) {
433
641
  this.appendMemoryIcon(valueElement, value, variableName);
@@ -438,54 +646,53 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
438
646
  const wrapperElement = document.createElement('span');
439
647
  wrapperElement.classList.add('error');
440
648
  wrapperElement.classList.add('value');
441
- wrapperElement.appendChild(
442
- uiI18n.getFormatLocalizedString(str_, UIStrings.exceptionS, {PH1: propertyValue.element}));
443
- propertyValue.element = wrapperElement;
649
+ wrapperElement.appendChild(uiI18n.getFormatLocalizedString(str_, UIStrings.exceptionS, {PH1: propertyValue}));
650
+ propertyValue = wrapperElement;
444
651
  }
445
- propertyValue.element.classList.add('value');
652
+ propertyValue.classList.add('value');
446
653
  return propertyValue;
447
654
 
448
- function createUnknownInternalLocationElement(): Element {
655
+ function createUnknownInternalLocationElement(): HTMLElement {
449
656
  const valueElement = document.createElement('span');
450
657
  valueElement.textContent = '<' + i18nString(UIStrings.unknown) + '>';
451
658
  UI.Tooltip.Tooltip.install(valueElement, description || '');
452
659
  return valueElement;
453
660
  }
454
661
 
455
- function createStringElement(): ObjectPropertyValue {
662
+ function createStringElement(): HTMLElement {
456
663
  const valueElement = document.createElement('span');
457
664
  valueElement.classList.add('object-value-string');
458
665
  const text = JSON.stringify(description);
459
- let propertyValue;
666
+ let propertyValue: HTMLElement;
460
667
  if (description.length > maxRenderableStringLength) {
461
- propertyValue = new ExpandableTextPropertyValue(valueElement, text, EXPANDABLE_MAX_LENGTH);
668
+ propertyValue = new ExpandableTextPropertyValue(valueElement, text, EXPANDABLE_MAX_LENGTH).element;
462
669
  } else {
463
670
  UI.UIUtils.createTextChild(valueElement, text);
464
- propertyValue = new ObjectPropertyValue(valueElement);
671
+ propertyValue = valueElement;
465
672
  UI.Tooltip.Tooltip.install(valueElement, description);
466
673
  }
467
674
  return propertyValue;
468
675
  }
469
676
 
470
- function createTrustedTypeElement(): ObjectPropertyValue {
677
+ function createTrustedTypeElement(): HTMLElement {
471
678
  const valueElement = document.createElement('span');
472
679
  valueElement.classList.add('object-value-trustedtype');
473
680
  const text = `${className} "${description}"`;
474
681
  let propertyValue;
475
682
  if (text.length > maxRenderableStringLength) {
476
- propertyValue = new ExpandableTextPropertyValue(valueElement, text, EXPANDABLE_MAX_LENGTH);
683
+ propertyValue = new ExpandableTextPropertyValue(valueElement, text, EXPANDABLE_MAX_LENGTH).element;
477
684
  } else {
478
685
  const contentString = createStringElement();
479
686
  UI.UIUtils.createTextChild(valueElement, `${className} `);
480
- valueElement.appendChild(contentString.element);
481
- propertyValue = new ObjectPropertyValue(valueElement);
687
+ valueElement.appendChild(contentString);
688
+ propertyValue = valueElement;
482
689
  UI.Tooltip.Tooltip.install(valueElement, text);
483
690
  }
484
691
 
485
692
  return propertyValue;
486
693
  }
487
694
 
488
- function createNodeElement(): Element {
695
+ function createNodeElement(): HTMLElement {
489
696
  const valueElement = document.createElement('span');
490
697
  valueElement.classList.add('object-value-node');
491
698
  createSpansForNodeTitle(valueElement, (description));
@@ -558,8 +765,8 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
558
765
 
559
766
  private contextMenuEventFired(event: Event): void {
560
767
  const contextMenu = new UI.ContextMenu.ContextMenu(event);
561
- contextMenu.appendApplicableItems(this.object);
562
- if (this.object instanceof SDK.RemoteObject.LocalJSONObject) {
768
+ contextMenu.appendApplicableItems(this.root);
769
+ if (this.root.object instanceof SDK.RemoteObject.LocalJSONObject) {
563
770
  contextMenu.viewSection().appendItem(
564
771
  i18nString(UIStrings.expandRecursively),
565
772
  this.#objectTreeElement.expandRecursively.bind(this.#objectTreeElement, EXPANDABLE_MAX_DEPTH),
@@ -604,27 +811,17 @@ export const enum ObjectPropertiesMode {
604
811
  }
605
812
 
606
813
  export class RootElement extends UI.TreeOutline.TreeElement {
607
- private readonly object: SDK.RemoteObject.RemoteObject;
814
+ private readonly object: ObjectTree;
608
815
  private readonly linkifier: Components.Linkifier.Linkifier|undefined;
609
816
  private readonly emptyPlaceholder: string|null|undefined;
610
- private readonly propertiesMode: ObjectPropertiesMode;
611
- private readonly extraProperties: SDK.RemoteObject.RemoteObjectProperty[];
612
- private readonly targetObject: SDK.RemoteObject.RemoteObject|undefined;
613
817
  override toggleOnClick: boolean;
614
- constructor(
615
- object: SDK.RemoteObject.RemoteObject, linkifier?: Components.Linkifier.Linkifier, emptyPlaceholder?: string|null,
616
- propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
617
- extraProperties: SDK.RemoteObject.RemoteObjectProperty[] = [],
618
- targetObject: SDK.RemoteObject.RemoteObject = object) {
818
+ constructor(object: ObjectTree, linkifier?: Components.Linkifier.Linkifier, emptyPlaceholder?: string|null) {
619
819
  const contentElement = document.createElement('slot');
620
820
  super(contentElement);
621
821
 
622
822
  this.object = object;
623
823
  this.linkifier = linkifier;
624
824
  this.emptyPlaceholder = emptyPlaceholder;
625
- this.propertiesMode = propertiesMode;
626
- this.extraProperties = extraProperties;
627
- this.targetObject = targetObject;
628
825
 
629
826
  this.setExpandable(true);
630
827
  this.selectable = true;
@@ -651,7 +848,7 @@ export class RootElement extends UI.TreeOutline.TreeElement {
651
848
 
652
849
  private onContextMenu(event: Event): void {
653
850
  const contextMenu = new UI.ContextMenu.ContextMenu(event);
654
- contextMenu.appendApplicableItems(this.object);
851
+ contextMenu.appendApplicableItems(this.object.object);
655
852
 
656
853
  if (this.object instanceof SDK.RemoteObject.LocalJSONObject) {
657
854
  const {value} = this.object;
@@ -676,8 +873,7 @@ export class RootElement extends UI.TreeOutline.TreeElement {
676
873
  const treeOutline = (this.treeOutline as ObjectPropertiesSection | null);
677
874
  const skipProto = treeOutline ? Boolean(treeOutline.skipProtoInternal) : false;
678
875
  return await ObjectPropertyTreeElement.populate(
679
- this, this.object, skipProto, false, this.linkifier, this.emptyPlaceholder, this.propertiesMode,
680
- this.extraProperties, this.targetObject);
876
+ this, this.object, skipProto, false, this.linkifier, this.emptyPlaceholder);
681
877
  }
682
878
  }
683
879
 
@@ -688,7 +884,7 @@ export class RootElement extends UI.TreeOutline.TreeElement {
688
884
  export const InitialVisibleChildrenLimit = 200;
689
885
 
690
886
  export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
691
- property: SDK.RemoteObject.RemoteObjectProperty;
887
+ property: ObjectTreeNode;
692
888
  override toggleOnClick: boolean;
693
889
  private highlightChanges: UI.UIUtils.HighlightChange[];
694
890
  private linkifier: Components.Linkifier.Linkifier|undefined;
@@ -699,9 +895,9 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
699
895
  readOnly!: boolean;
700
896
  private prompt!: ObjectPropertyPrompt|undefined;
701
897
  private editableDiv!: HTMLElement;
702
- propertyValue?: ObjectPropertyValue;
898
+ propertyValue?: HTMLElement;
703
899
  expandedValueElement?: Element|null;
704
- constructor(property: SDK.RemoteObject.RemoteObjectProperty, linkifier?: Components.Linkifier.Linkifier) {
900
+ constructor(property: ObjectTreeNode, linkifier?: Components.Linkifier.Linkifier) {
705
901
  // Pass an empty title, the title gets made later in onattach.
706
902
  super();
707
903
 
@@ -716,52 +912,30 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
716
912
  }
717
913
 
718
914
  static async populate(
719
- treeElement: UI.TreeOutline.TreeElement, value: SDK.RemoteObject.RemoteObject, skipProto: boolean,
720
- skipGettersAndSetters: boolean, linkifier?: Components.Linkifier.Linkifier, emptyPlaceholder?: string|null,
721
- propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
722
- extraProperties?: SDK.RemoteObject.RemoteObjectProperty[],
723
- targetValue?: SDK.RemoteObject.RemoteObject): Promise<void> {
724
- if (value.arrayLength() > ARRAY_LOAD_THRESHOLD) {
725
- treeElement.removeChildren();
726
- void ArrayGroupingTreeElement.populateArray(treeElement, value, 0, value.arrayLength() - 1, linkifier);
727
- return;
728
- }
729
-
730
- let properties, internalProperties = null;
731
- switch (propertiesMode) {
732
- case ObjectPropertiesMode.ALL:
733
- ({properties} = await value.getAllProperties(false /* accessorPropertiesOnly */, true /* generatePreview */));
734
- break;
735
- case ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED:
736
- ({properties, internalProperties} =
737
- await SDK.RemoteObject.RemoteObject.loadFromObjectPerProto(value, true /* generatePreview */));
738
- break;
739
- }
740
- treeElement.removeChildren();
741
- if (!properties) {
742
- return;
743
- }
744
-
745
- if (extraProperties !== undefined) {
746
- properties.push(...extraProperties);
915
+ treeElement: UI.TreeOutline.TreeElement,
916
+ value: ObjectTreeNodeBase,
917
+ skipProto: boolean,
918
+ skipGettersAndSetters: boolean,
919
+ linkifier?: Components.Linkifier.Linkifier,
920
+ emptyPlaceholder?: string|null,
921
+ ): Promise<void> {
922
+ const properties = await value.children();
923
+ if (properties.arrayRanges) {
924
+ await ArrayGroupingTreeElement.populate(treeElement, properties, linkifier);
925
+ } else {
926
+ ObjectPropertyTreeElement.populateWithProperties(
927
+ treeElement, properties, skipProto, skipGettersAndSetters, linkifier, emptyPlaceholder);
747
928
  }
748
-
749
- ObjectPropertyTreeElement.populateWithProperties(
750
- treeElement, properties, internalProperties, skipProto, skipGettersAndSetters, targetValue || value, linkifier,
751
- emptyPlaceholder);
752
929
  }
753
930
 
754
931
  static populateWithProperties(
755
- treeNode: UI.TreeOutline.TreeElement, properties: SDK.RemoteObject.RemoteObjectProperty[],
756
- internalProperties: SDK.RemoteObject.RemoteObjectProperty[]|null, skipProto: boolean,
757
- skipGettersAndSetters: boolean, value: SDK.RemoteObject.RemoteObject|null,
758
- linkifier?: Components.Linkifier.Linkifier, emptyPlaceholder?: string|null): void {
759
- properties.sort(ObjectPropertiesSection.compareProperties);
760
- internalProperties = internalProperties || [];
761
-
762
- const entriesProperty = internalProperties.find(property => property.name === '[[Entries]]');
932
+ treeNode: UI.TreeOutline.TreeElement, {properties, internalProperties}: NodeChildren, skipProto: boolean,
933
+ skipGettersAndSetters: boolean, linkifier?: Components.Linkifier.Linkifier,
934
+ emptyPlaceholder?: string|null): void {
935
+ properties?.sort(ObjectPropertiesSection.compareProperties);
936
+
937
+ const entriesProperty = internalProperties?.find(({property}) => property.name === '[[Entries]]');
763
938
  if (entriesProperty) {
764
- parentMap.set(entriesProperty, value);
765
939
  const treeElement = new ObjectPropertyTreeElement(entriesProperty, linkifier);
766
940
  treeElement.setExpandable(true);
767
941
  treeElement.expand();
@@ -769,34 +943,30 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
769
943
  }
770
944
 
771
945
  const tailProperties = [];
772
- for (let i = 0; i < properties.length; ++i) {
773
- const property = properties[i];
774
- parentMap.set(property, value);
775
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
776
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
777
- if (!ObjectPropertiesSection.isDisplayableProperty(property, (treeNode as any).property)) {
946
+ for (const property of properties ?? []) {
947
+ if (treeNode instanceof ObjectPropertyTreeElement &&
948
+ !ObjectPropertiesSection.isDisplayableProperty(property.property, treeNode.property?.property)) {
778
949
  continue;
779
950
  }
780
951
 
781
- if (property.isOwn && !skipGettersAndSetters) {
782
- if (property.getter) {
783
- const getterProperty =
784
- new SDK.RemoteObject.RemoteObjectProperty('get ' + property.name, property.getter, false);
785
- parentMap.set(getterProperty, value);
786
- tailProperties.push(getterProperty);
952
+ // FIXME move into node
953
+ if (property.property.isOwn && !skipGettersAndSetters) {
954
+ if (property.property.getter) {
955
+ const getterProperty = new SDK.RemoteObject.RemoteObjectProperty(
956
+ 'get ' + property.property.name, property.property.getter, false);
957
+ tailProperties.push(new ObjectTreeNode(getterProperty, property.propertiesMode, property.parent));
787
958
  }
788
- if (property.setter) {
789
- const setterProperty =
790
- new SDK.RemoteObject.RemoteObjectProperty('set ' + property.name, property.setter, false);
791
- parentMap.set(setterProperty, value);
792
- tailProperties.push(setterProperty);
959
+ if (property.property.setter) {
960
+ const setterProperty = new SDK.RemoteObject.RemoteObjectProperty(
961
+ 'set ' + property.property.name, property.property.setter, false);
962
+ tailProperties.push(new ObjectTreeNode(setterProperty, property.propertiesMode, property.parent));
793
963
  }
794
964
  }
795
965
 
796
- const canShowProperty = property.getter || !property.isAccessorProperty();
966
+ const canShowProperty = property.property.getter || !property.property.isAccessorProperty();
797
967
  if (canShowProperty) {
798
968
  const element = new ObjectPropertyTreeElement(property, linkifier);
799
- if (property.name === 'memories' && property.value?.className === 'Memories') {
969
+ if (property.property.name === 'memories' && property.object?.className === 'Memories') {
800
970
  element.updateExpandable();
801
971
  if (element.isExpandable()) {
802
972
  element.expand();
@@ -809,13 +979,12 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
809
979
  treeNode.appendChild(new ObjectPropertyTreeElement(tailProperties[i], linkifier));
810
980
  }
811
981
 
812
- for (const property of internalProperties) {
813
- parentMap.set(property, value);
982
+ for (const property of internalProperties ?? []) {
814
983
  const treeElement = new ObjectPropertyTreeElement(property, linkifier);
815
- if (property.name === '[[Entries]]') {
984
+ if (property.property.name === '[[Entries]]') {
816
985
  continue;
817
986
  }
818
- if (property.name === '[[Prototype]]' && skipProto) {
987
+ if (property.property.name === '[[Prototype]]' && skipProto) {
819
988
  continue;
820
989
  }
821
990
  treeNode.appendChild(treeElement);
@@ -877,8 +1046,8 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
877
1046
  this.revertHighlightChanges();
878
1047
 
879
1048
  this.applySearch(regex, this.nameElement, cssClasses);
880
- if (this.property.value) {
881
- const valueType = this.property.value.type;
1049
+ if (this.property.object) {
1050
+ const valueType = this.property.object.type;
882
1051
  if (valueType !== 'object') {
883
1052
  this.applySearch(regex, this.valueElement, cssClasses);
884
1053
  }
@@ -929,14 +1098,13 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
929
1098
  }
930
1099
 
931
1100
  override async onpopulate(): Promise<void> {
932
- const propertyValue = (this.property.value as SDK.RemoteObject.RemoteObject);
933
- console.assert(typeof propertyValue !== 'undefined');
934
1101
  const treeOutline = (this.treeOutline as ObjectPropertiesSection | null);
935
1102
  const skipProto = treeOutline ? Boolean(treeOutline.skipProtoInternal) : false;
936
- const targetValue = this.property.name !== '[[Prototype]]' ? propertyValue : parentMap.get(this.property);
937
- if (targetValue) {
938
- await ObjectPropertyTreeElement.populate(
939
- this, propertyValue, skipProto, false, this.linkifier, undefined, undefined, undefined, targetValue);
1103
+ this.removeChildren();
1104
+ this.property.removeChildren();
1105
+
1106
+ if (this.property.object) {
1107
+ await ObjectPropertyTreeElement.populate(this, this.property, skipProto, false, this.linkifier);
940
1108
  if (this.childCount() > this.maxNumPropertiesToShow) {
941
1109
  this.createShowAllPropertiesButton();
942
1110
  }
@@ -947,16 +1115,16 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
947
1115
  const target = (event.target as HTMLElement);
948
1116
  const inEditableElement = target.isSelfOrDescendant(this.valueElement) ||
949
1117
  (this.expandedValueElement && target.isSelfOrDescendant(this.expandedValueElement));
950
- if (this.property.value && !this.property.value.customPreview() && inEditableElement &&
951
- (this.property.writable || this.property.setter)) {
1118
+ if (this.property.object && !this.property.object.customPreview() && inEditableElement &&
1119
+ (this.property.property.writable || this.property.property.setter)) {
952
1120
  this.startEditing();
953
1121
  }
954
1122
  return false;
955
1123
  }
956
1124
 
957
1125
  override onenter(): boolean {
958
- if (this.property.value && !this.property.value.customPreview() &&
959
- (this.property.writable || this.property.setter)) {
1126
+ if (this.property.object && !this.property.object.customPreview() &&
1127
+ (this.property.property.writable || this.property.property.setter)) {
960
1128
  this.startEditing();
961
1129
  return true;
962
1130
  }
@@ -1011,46 +1179,47 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1011
1179
 
1012
1180
  update(): void {
1013
1181
  this.nameElement =
1014
- (ObjectPropertiesSection.createNameElement(this.property.name, this.property.private) as HTMLElement);
1015
- if (!this.property.enumerable) {
1182
+ (ObjectPropertiesSection.createNameElement(this.property.name, this.property.property.private) as HTMLElement);
1183
+ if (!this.property.property.enumerable) {
1016
1184
  this.nameElement.classList.add('object-properties-section-dimmed');
1017
1185
  }
1018
- if (this.property.isOwn) {
1186
+ if (this.property.property.isOwn) {
1019
1187
  this.nameElement.classList.add('own-property');
1020
1188
  }
1021
- if (this.property.synthetic) {
1189
+ if (this.property.property.synthetic) {
1022
1190
  this.nameElement.classList.add('synthetic-property');
1023
1191
  }
1024
1192
 
1025
1193
  this.updatePropertyPath();
1026
1194
 
1027
- const isInternalEntries = this.property.synthetic && this.property.name === '[[Entries]]';
1195
+ const isInternalEntries = this.property.property.synthetic && this.property.name === '[[Entries]]';
1028
1196
  if (isInternalEntries) {
1029
1197
  this.valueElement = document.createElement('span');
1030
1198
  this.valueElement.classList.add('value');
1031
- } else if (this.property.value) {
1199
+ } else if (this.property.object) {
1032
1200
  const showPreview = this.property.name !== '[[Prototype]]';
1033
1201
  this.propertyValue = ObjectPropertiesSection.createPropertyValueWithCustomSupport(
1034
- this.property.value, this.property.wasThrown, showPreview, this.linkifier, this.property.synthetic,
1035
- this.path() /* variableName */);
1036
- this.valueElement = (this.propertyValue.element as HTMLElement);
1037
- } else if (this.property.getter) {
1202
+ this.property.object, this.property.property.wasThrown, showPreview, this.linkifier,
1203
+ this.property.property.synthetic, this.path() /* variableName */);
1204
+ this.valueElement = this.propertyValue;
1205
+ } else if (this.property.property.getter) {
1038
1206
  this.valueElement = document.createElement('span');
1039
1207
  const element = this.valueElement.createChild('span');
1040
1208
  element.textContent = i18nString(UIStrings.dots);
1041
1209
  element.classList.add('object-value-calculate-value-button');
1042
1210
  UI.Tooltip.Tooltip.install(element, i18nString(UIStrings.invokePropertyGetter));
1043
- const object = parentMap.get(this.property) as SDK.RemoteObject.RemoteObject;
1044
- const getter = this.property.getter;
1211
+ const getter = this.property.property.getter;
1045
1212
  element.addEventListener('click', (event: Event) => {
1046
1213
  event.consume();
1047
1214
  const invokeGetter = `
1048
1215
  function invokeGetter(getter) {
1049
1216
  return Reflect.apply(getter, this, []);
1050
1217
  }`;
1051
- // @ts-expect-error No way to teach TypeScript to preserve the Function-ness of `getter`.
1052
1218
  // Also passing a string instead of a Function to avoid coverage implementation messing with it.
1053
- void object.callFunction(invokeGetter, [SDK.RemoteObject.RemoteObject.toCallArgument(getter)])
1219
+ void this.property.parent
1220
+ ?.object
1221
+ // @ts-expect-error No way to teach TypeScript to preserve the Function-ness of `getter`.
1222
+ ?.callFunction(invokeGetter, [SDK.RemoteObject.RemoteObject.toCallArgument(getter)])
1054
1223
  .then(this.onInvokeGetterClick.bind(this));
1055
1224
  }, false);
1056
1225
  } else {
@@ -1061,8 +1230,9 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1061
1230
  }
1062
1231
 
1063
1232
  const valueText = this.valueElement.textContent;
1064
- if (this.property.value && valueText && !this.property.wasThrown) {
1065
- this.expandedValueElement = this.createExpandedValueElement(this.property.value, this.property.synthetic);
1233
+ if (this.property.object && valueText && !this.property.property.wasThrown) {
1234
+ this.expandedValueElement =
1235
+ this.createExpandedValueElement(this.property.object, this.property.property.synthetic);
1066
1236
  }
1067
1237
 
1068
1238
  const adorner: Element|string = '';
@@ -1091,7 +1261,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1091
1261
 
1092
1262
  const name = this.property.name;
1093
1263
 
1094
- if (this.property.synthetic) {
1264
+ if (this.property.property.synthetic) {
1095
1265
  UI.Tooltip.Tooltip.install(this.nameElement, name);
1096
1266
  return;
1097
1267
  }
@@ -1101,11 +1271,11 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1101
1271
  const isInteger = /^(?:0|[1-9]\d*)$/;
1102
1272
 
1103
1273
  const parentPath = (this.parent instanceof ObjectPropertyTreeElement && this.parent.nameElement &&
1104
- !this.parent.property.synthetic) ?
1274
+ !this.parent.property.property.synthetic) ?
1105
1275
  this.parent.nameElement.title :
1106
1276
  '';
1107
1277
 
1108
- if (this.property.private || useDotNotation.test(name)) {
1278
+ if (this.property.property.private || useDotNotation.test(name)) {
1109
1279
  UI.Tooltip.Tooltip.install(this.nameElement, parentPath ? `${parentPath}.${name}` : name);
1110
1280
  } else if (isInteger.test(name)) {
1111
1281
  UI.Tooltip.Tooltip.install(this.nameElement, `${parentPath}[${name}]`);
@@ -1114,17 +1284,17 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1114
1284
  }
1115
1285
  }
1116
1286
 
1117
- private contextMenuFired(event: Event): void {
1287
+ getContextMenu(event: Event): UI.ContextMenu.ContextMenu {
1118
1288
  const contextMenu = new UI.ContextMenu.ContextMenu(event);
1119
1289
  contextMenu.appendApplicableItems(this);
1120
- if (this.property.symbol) {
1121
- contextMenu.appendApplicableItems(this.property.symbol);
1122
- }
1123
- if (this.property.value) {
1124
- contextMenu.appendApplicableItems(this.property.value);
1125
- if (parentMap.get(this.property) instanceof SDK.RemoteObject.LocalJSONObject) {
1126
- const {value: {value}} = this.property;
1127
- const propertyValue = typeof value === 'object' ? JSON.stringify(value, null, 2) : value;
1290
+ if (this.property.property.symbol) {
1291
+ contextMenu.appendApplicableItems(this.property.property.symbol);
1292
+ }
1293
+ if (this.property.object) {
1294
+ contextMenu.appendApplicableItems(this.property.object);
1295
+ if (this.property.parent?.object instanceof SDK.RemoteObject.LocalJSONObject) {
1296
+ const propertyValue = typeof this.property.object === 'object' ? JSON.stringify(this.property.object, null, 2) :
1297
+ this.property.object;
1128
1298
  const copyValueHandler = (): void => {
1129
1299
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue);
1130
1300
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText((propertyValue as string | undefined));
@@ -1133,13 +1303,13 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1133
1303
  i18nString(UIStrings.copyValue), copyValueHandler, {jslogContext: 'copy-value'});
1134
1304
  }
1135
1305
  }
1136
- if (!this.property.synthetic && this.nameElement?.title) {
1306
+ if (!this.property.property.synthetic && this.nameElement?.title) {
1137
1307
  const copyPathHandler = Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText.bind(
1138
1308
  Host.InspectorFrontendHost.InspectorFrontendHostInstance, this.nameElement.title);
1139
1309
  contextMenu.clipboardSection().appendItem(
1140
1310
  i18nString(UIStrings.copyPropertyPath), copyPathHandler, {jslogContext: 'copy-property-path'});
1141
1311
  }
1142
- if (parentMap.get(this.property) instanceof SDK.RemoteObject.LocalJSONObject) {
1312
+ if (this.property.parent?.object instanceof SDK.RemoteObject.LocalJSONObject) {
1143
1313
  contextMenu.viewSection().appendItem(
1144
1314
  i18nString(UIStrings.expandRecursively), this.expandRecursively.bind(this, EXPANDABLE_MAX_DEPTH),
1145
1315
  {jslogContext: 'expand-recursively'});
@@ -1147,9 +1317,11 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1147
1317
  i18nString(UIStrings.collapseChildren), this.collapseChildren.bind(this),
1148
1318
  {jslogContext: 'collapse-children'});
1149
1319
  }
1150
- if (this.propertyValue) {
1151
- this.propertyValue.appendApplicableItems(event, contextMenu, {});
1152
- }
1320
+ return contextMenu;
1321
+ }
1322
+
1323
+ private contextMenuFired(event: Event): void {
1324
+ const contextMenu = this.getContextMenu(event);
1153
1325
  void contextMenu.show();
1154
1326
  }
1155
1327
 
@@ -1160,9 +1332,9 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1160
1332
  }
1161
1333
  this.editableDiv = this.rowContainer.createChild('span', 'editable-div');
1162
1334
 
1163
- if (this.property.value) {
1164
- let text: string|(string | undefined) = this.property.value.description;
1165
- if (this.property.value.type === 'string' && typeof text === 'string') {
1335
+ if (this.property.object) {
1336
+ let text: string|(string | undefined) = this.property.object.description;
1337
+ if (this.property.object.type === 'string' && typeof text === 'string') {
1166
1338
  text = `"${text}"`;
1167
1339
  }
1168
1340
 
@@ -1233,13 +1405,13 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1233
1405
  }
1234
1406
 
1235
1407
  private async applyExpression(expression: string): Promise<void> {
1236
- const property = SDK.RemoteObject.RemoteObject.toCallArgument(this.property.symbol || this.property.name);
1408
+ const property = SDK.RemoteObject.RemoteObject.toCallArgument(this.property.property.symbol || this.property.name);
1237
1409
  expression = JavaScriptREPL.wrapObjectLiteral(expression.trim());
1238
1410
 
1239
- if (this.property.synthetic) {
1411
+ if (this.property.property.synthetic) {
1240
1412
  let invalidate = false;
1241
1413
  if (expression) {
1242
- invalidate = await this.property.setSyntheticValue(expression);
1414
+ invalidate = await this.property.property.setSyntheticValue(expression);
1243
1415
  }
1244
1416
  if (invalidate) {
1245
1417
  const parent = this.parent;
@@ -1253,7 +1425,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1253
1425
  return;
1254
1426
  }
1255
1427
 
1256
- const parentObject = (parentMap.get(this.property) as SDK.RemoteObject.RemoteObject);
1428
+ const parentObject = this.property.parent?.object as SDK.RemoteObject.RemoteObject;
1257
1429
  const errorPromise =
1258
1430
  expression ? parentObject.setPropertyValue(property, expression) : parentObject.deleteProperty(property);
1259
1431
  const error = await errorPromise;
@@ -1265,11 +1437,13 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1265
1437
  if (!expression) {
1266
1438
  // The property was deleted, so remove this tree element.
1267
1439
  this.parent?.removeChild(this);
1440
+ this.property.parent?.removeChild(this.property);
1268
1441
  } else {
1269
1442
  // Call updateSiblings since their value might be based on the value that just changed.
1270
1443
  const parent = this.parent;
1271
1444
  if (parent) {
1272
1445
  parent.invalidateChildren();
1446
+ this.property.parent?.removeChildren();
1273
1447
  void parent.onpopulate();
1274
1448
  }
1275
1449
  }
@@ -1279,8 +1453,8 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1279
1453
  if (!result.object) {
1280
1454
  return;
1281
1455
  }
1282
- this.property.value = result.object;
1283
- this.property.wasThrown = result.wasThrown || false;
1456
+ this.property.property.value = result.object;
1457
+ this.property.property.wasThrown = result.wasThrown || false;
1284
1458
 
1285
1459
  this.update();
1286
1460
  this.invalidateChildren();
@@ -1288,9 +1462,10 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1288
1462
  }
1289
1463
 
1290
1464
  private updateExpandable(): void {
1291
- if (this.property.value) {
1465
+ if (this.property.object) {
1292
1466
  this.setExpandable(
1293
- !this.property.value.customPreview() && this.property.value.hasChildren && !this.property.wasThrown);
1467
+ !this.property.object.customPreview() && this.property.object.hasChildren &&
1468
+ !this.property.property.wasThrown);
1294
1469
  } else {
1295
1470
  this.setExpandable(false);
1296
1471
  }
@@ -1301,249 +1476,185 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1301
1476
  }
1302
1477
  }
1303
1478
 
1304
- export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
1305
- override toggleOnClick: boolean;
1306
- private readonly fromIndex: number;
1307
- private readonly toIndex: number;
1308
- private readonly object: SDK.RemoteObject.RemoteObject;
1309
- private readonly propertyCount: number;
1310
- private readonly linkifier: Components.Linkifier.Linkifier|undefined;
1311
- constructor(
1312
- object: SDK.RemoteObject.RemoteObject, fromIndex: number, toIndex: number, propertyCount: number,
1313
- linkifier?: Components.Linkifier.Linkifier) {
1314
- super(Platform.StringUtilities.sprintf('[%d … %d]', fromIndex, toIndex), true);
1315
- this.toggleOnClick = true;
1316
- this.fromIndex = fromIndex;
1317
- this.toIndex = toIndex;
1318
- this.object = object;
1319
- this.propertyCount = propertyCount;
1320
- this.linkifier = linkifier;
1321
- }
1322
-
1323
- static async populateArray(
1324
- treeNode: UI.TreeOutline.TreeElement, object: SDK.RemoteObject.RemoteObject, fromIndex: number, toIndex: number,
1325
- linkifier?: Components.Linkifier.Linkifier): Promise<void> {
1326
- await ArrayGroupingTreeElement.populateRanges(treeNode, object, fromIndex, toIndex, true, linkifier);
1327
- }
1479
+ async function arrayRangeGroups(object: SDK.RemoteObject.RemoteObject, fromIndex: number, toIndex: number):
1480
+ Promise<{ranges: number[][]}|null|undefined> {
1481
+ return await object.callFunctionJSON(packArrayRanges, [
1482
+ {value: fromIndex},
1483
+ {value: toIndex},
1484
+ {value: ArrayGroupingTreeElement.bucketThreshold},
1485
+ {value: ArrayGroupingTreeElement.sparseIterationThreshold},
1486
+ ]);
1328
1487
 
1329
- private static async populateRanges(
1330
- treeNode: UI.TreeOutline.TreeElement, object: SDK.RemoteObject.RemoteObject, fromIndex: number, toIndex: number,
1331
- topLevel: boolean, linkifier?: Components.Linkifier.Linkifier): Promise<void> {
1332
- const jsonValue = await object.callFunctionJSON(packRanges, [
1333
- {value: fromIndex},
1334
- {value: toIndex},
1335
- {value: ArrayGroupingTreeElement.bucketThreshold},
1336
- {value: ArrayGroupingTreeElement.sparseIterationThreshold},
1337
- ]);
1488
+ /**
1489
+ * This function is called on the RemoteObject.
1490
+ * Note: must declare params as optional.
1491
+ */
1492
+ function packArrayRanges(
1493
+ this: Object, fromIndex?: number, toIndex?: number, bucketThreshold?: number,
1494
+ sparseIterationThreshold?: number): {
1495
+ ranges: number[][],
1496
+ }|undefined {
1497
+ if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined ||
1498
+ bucketThreshold === undefined) {
1499
+ return;
1500
+ }
1501
+ let ownPropertyNames: string[]|null = null;
1502
+ const consecutiveRange = (toIndex - fromIndex >= sparseIterationThreshold) && ArrayBuffer.isView(this);
1338
1503
 
1339
- await callback(jsonValue);
1340
-
1341
- /**
1342
- * Note: must declare params as optional.
1343
- */
1344
- function packRanges(
1345
- this: Object, fromIndex?: number, toIndex?: number, bucketThreshold?: number,
1346
- sparseIterationThreshold?: number): {
1347
- ranges: number[][],
1348
- }|undefined {
1349
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined ||
1350
- bucketThreshold === undefined) {
1504
+ function* arrayIndexes(object: Object): Generator<number, void, unknown> {
1505
+ if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
1351
1506
  return;
1352
1507
  }
1353
- let ownPropertyNames: string[]|null = null;
1354
- const consecutiveRange = (toIndex - fromIndex >= sparseIterationThreshold) && ArrayBuffer.isView(this);
1355
1508
 
1356
- function* arrayIndexes(object: Object): Generator<number, void, unknown> {
1357
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
1358
- return;
1509
+ if (toIndex - fromIndex < sparseIterationThreshold) {
1510
+ for (let i = fromIndex; i <= toIndex; ++i) {
1511
+ if (i in object) {
1512
+ yield i;
1513
+ }
1359
1514
  }
1515
+ } else {
1516
+ ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(object);
1517
+ for (let i = 0; i < ownPropertyNames.length; ++i) {
1518
+ const name = ownPropertyNames[i];
1360
1519
 
1361
- if (toIndex - fromIndex < sparseIterationThreshold) {
1362
- for (let i = fromIndex; i <= toIndex; ++i) {
1363
- if (i in object) {
1364
- yield i;
1365
- }
1366
- }
1367
- } else {
1368
- ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(object);
1369
- for (let i = 0; i < ownPropertyNames.length; ++i) {
1370
- const name = ownPropertyNames[i];
1371
-
1372
- const index = Number(name) >>> 0;
1373
- if ((String(index)) === name && fromIndex <= index && index <= toIndex) {
1374
- yield index;
1375
- }
1520
+ const index = Number(name) >>> 0;
1521
+ if ((String(index)) === name && fromIndex <= index && index <= toIndex) {
1522
+ yield index;
1376
1523
  }
1377
1524
  }
1378
1525
  }
1526
+ }
1379
1527
 
1380
- let count = 0;
1381
- if (consecutiveRange) {
1382
- count = toIndex - fromIndex + 1;
1383
- } else {
1384
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1385
- for (const ignored of arrayIndexes(this)) {
1386
- ++count;
1387
- }
1528
+ let count = 0;
1529
+ if (consecutiveRange) {
1530
+ count = toIndex - fromIndex + 1;
1531
+ } else {
1532
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1533
+ for (const ignored of arrayIndexes(this)) {
1534
+ ++count;
1388
1535
  }
1536
+ }
1389
1537
 
1390
- let bucketSize: number = count;
1391
- if (count <= bucketThreshold) {
1392
- bucketSize = count;
1393
- } else {
1394
- bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1);
1395
- }
1538
+ let bucketSize: number = count;
1539
+ if (count <= bucketThreshold) {
1540
+ bucketSize = count;
1541
+ } else {
1542
+ bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1);
1543
+ }
1396
1544
 
1397
- const ranges = [];
1398
- if (consecutiveRange) {
1399
- for (let i = fromIndex; i <= toIndex; i += bucketSize) {
1400
- const groupStart = i;
1401
- let groupEnd: number = groupStart + bucketSize - 1;
1402
- if (groupEnd > toIndex) {
1403
- groupEnd = toIndex;
1404
- }
1405
- ranges.push([groupStart, groupEnd, groupEnd - groupStart + 1]);
1545
+ const ranges = [];
1546
+ if (consecutiveRange) {
1547
+ for (let i = fromIndex; i <= toIndex; i += bucketSize) {
1548
+ const groupStart = i;
1549
+ let groupEnd: number = groupStart + bucketSize - 1;
1550
+ if (groupEnd > toIndex) {
1551
+ groupEnd = toIndex;
1406
1552
  }
1407
- } else {
1408
- count = 0;
1409
- let groupStart = -1;
1410
- let groupEnd = 0;
1411
- for (const i of arrayIndexes(this)) {
1412
- if (groupStart === -1) {
1413
- groupStart = i;
1414
- }
1415
- groupEnd = i;
1416
- if (++count === bucketSize) {
1417
- ranges.push([groupStart, groupEnd, count]);
1418
- count = 0;
1419
- groupStart = -1;
1420
- }
1553
+ ranges.push([groupStart, groupEnd, groupEnd - groupStart + 1]);
1554
+ }
1555
+ } else {
1556
+ count = 0;
1557
+ let groupStart = -1;
1558
+ let groupEnd = 0;
1559
+ for (const i of arrayIndexes(this)) {
1560
+ if (groupStart === -1) {
1561
+ groupStart = i;
1421
1562
  }
1422
- if (count > 0) {
1563
+ groupEnd = i;
1564
+ if (++count === bucketSize) {
1423
1565
  ranges.push([groupStart, groupEnd, count]);
1566
+ count = 0;
1567
+ groupStart = -1;
1424
1568
  }
1425
1569
  }
1426
-
1427
- return {ranges};
1570
+ if (count > 0) {
1571
+ ranges.push([groupStart, groupEnd, count]);
1572
+ }
1428
1573
  }
1429
1574
 
1430
- async function callback(result: {ranges: number[][]}|undefined|null): Promise<void> {
1431
- if (!result) {
1432
- return;
1433
- }
1434
- const ranges = (result.ranges);
1435
- if (ranges.length === 1) {
1436
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
1437
- // @ts-expect-error
1438
- await ArrayGroupingTreeElement.populateAsFragment(treeNode, object, ranges[0][0], ranges[0][1], linkifier);
1439
- } else {
1440
- for (let i = 0; i < ranges.length; ++i) {
1441
- const fromIndex = ranges[i][0];
1442
- const toIndex = ranges[i][1];
1443
- const count = ranges[i][2];
1444
- if (fromIndex === toIndex) {
1445
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
1446
- // @ts-expect-error
1447
- await ArrayGroupingTreeElement.populateAsFragment(treeNode, object, fromIndex, toIndex, linkifier);
1448
- } else {
1449
- treeNode.appendChild(new ArrayGroupingTreeElement(object, fromIndex, toIndex, count, linkifier));
1450
- }
1451
- }
1575
+ return {ranges};
1576
+ }
1577
+ }
1578
+
1579
+ /**
1580
+ * This function is called on the RemoteObject.
1581
+ */
1582
+ function buildArrayFragment(
1583
+ this: Record<number, Object>,
1584
+ fromIndex?: number,
1585
+ toIndex?: number,
1586
+ sparseIterationThreshold?: number,
1587
+ ): unknown {
1588
+ const result = Object.create(null);
1589
+
1590
+ if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
1591
+ return;
1592
+ }
1593
+
1594
+ if (toIndex - fromIndex < sparseIterationThreshold) {
1595
+ for (let i = fromIndex; i <= toIndex; ++i) {
1596
+ if (i in this) {
1597
+ result[i] = this[i];
1452
1598
  }
1453
- if (topLevel) {
1454
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
1455
- // @ts-expect-error
1456
- await ArrayGroupingTreeElement.populateNonIndexProperties(treeNode, object, linkifier);
1599
+ }
1600
+ } else {
1601
+ const ownPropertyNames = Object.getOwnPropertyNames(this);
1602
+ for (let i = 0; i < ownPropertyNames.length; ++i) {
1603
+ const name = ownPropertyNames[i];
1604
+ const index = Number(name) >>> 0;
1605
+ if (String(index) === name && fromIndex <= index && index <= toIndex) {
1606
+ result[index] = this[index];
1457
1607
  }
1458
1608
  }
1459
1609
  }
1610
+ return result;
1611
+ }
1460
1612
 
1461
- private static async populateAsFragment(
1462
- this: ArrayGroupingTreeElement, treeNode: UI.TreeOutline.TreeElement, object: SDK.RemoteObject.RemoteObject,
1463
- fromIndex: number, toIndex: number, linkifier?: Components.Linkifier.Linkifier): Promise<void> {
1464
- const result = await object.callFunction(
1465
- buildArrayFragment,
1466
- [{value: fromIndex}, {value: toIndex}, {value: ArrayGroupingTreeElement.sparseIterationThreshold}]);
1467
- if (!result.object || result.wasThrown) {
1468
- return;
1469
- }
1470
- const arrayFragment = result.object;
1471
- const allProperties =
1472
- await arrayFragment.getAllProperties(false /* accessorPropertiesOnly */, true /* generatePreview */);
1473
- arrayFragment.release();
1474
- const properties = allProperties.properties;
1475
- if (!properties) {
1613
+ export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
1614
+ override toggleOnClick: boolean;
1615
+ private readonly linkifier: Components.Linkifier.Linkifier|undefined;
1616
+ readonly #child: ArrayGroupTreeNode;
1617
+ constructor(child: ArrayGroupTreeNode, linkifier?: Components.Linkifier.Linkifier) {
1618
+ super(Platform.StringUtilities.sprintf('[%d … %d]', child.range.fromIndex, child.range.toIndex), true);
1619
+ this.#child = child;
1620
+ this.toggleOnClick = true;
1621
+ this.linkifier = linkifier;
1622
+ }
1623
+
1624
+ static async populate(
1625
+ treeNode: UI.TreeOutline.TreeElement, children: NodeChildren,
1626
+ linkifier?: Components.Linkifier.Linkifier): Promise<void> {
1627
+ if (!children.arrayRanges) {
1476
1628
  return;
1477
1629
  }
1478
- properties.sort(ObjectPropertiesSection.compareProperties);
1479
- for (let i = 0; i < properties.length; ++i) {
1480
- parentMap.set(properties[i], this.object);
1481
- const childTreeElement = new ObjectPropertyTreeElement(properties[i], linkifier);
1482
- childTreeElement.readOnly = true;
1483
- treeNode.appendChild(childTreeElement);
1484
- }
1485
-
1486
- function buildArrayFragment(
1487
- this: Record<number, Object>,
1488
- fromIndex?: number,
1489
- toIndex?: number,
1490
- sparseIterationThreshold?: number,
1491
- ): unknown {
1492
- const result = Object.create(null);
1493
-
1494
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
1495
- return;
1496
- }
1497
-
1498
- if (toIndex - fromIndex < sparseIterationThreshold) {
1499
- for (let i = fromIndex; i <= toIndex; ++i) {
1500
- if (i in this) {
1501
- result[i] = this[i];
1502
- }
1503
- }
1504
- } else {
1505
- const ownPropertyNames = Object.getOwnPropertyNames(this);
1506
- for (let i = 0; i < ownPropertyNames.length; ++i) {
1507
- const name = ownPropertyNames[i];
1508
- const index = Number(name) >>> 0;
1509
- if (String(index) === name && fromIndex <= index && index <= toIndex) {
1510
- result[index] = this[index];
1511
- }
1630
+ if (children.arrayRanges.length === 1) {
1631
+ await ObjectPropertyTreeElement.populate(treeNode, children.arrayRanges[0], false, false, linkifier);
1632
+ } else {
1633
+ for (const child of children.arrayRanges) {
1634
+ if (child.singular) {
1635
+ await ObjectPropertyTreeElement.populate(treeNode, child, false, false, linkifier);
1636
+ } else {
1637
+ treeNode.appendChild(new ArrayGroupingTreeElement(child, linkifier));
1512
1638
  }
1513
1639
  }
1514
- return result;
1515
1640
  }
1516
- }
1517
1641
 
1518
- private static async populateNonIndexProperties(
1519
- this: ArrayGroupingTreeElement, treeNode: UI.TreeOutline.TreeElement, object: SDK.RemoteObject.RemoteObject,
1520
- linkifier?: Components.Linkifier.Linkifier): Promise<void> {
1521
- const {properties, internalProperties} = await SDK.RemoteObject.RemoteObject.loadFromObjectPerProto(
1522
- object, true /* generatePreview */, true /* nonIndexedPropertiesOnly */);
1523
- if (!properties) {
1524
- return;
1525
- }
1526
- ObjectPropertyTreeElement.populateWithProperties(
1527
- treeNode, properties, internalProperties, false, false, object, linkifier);
1642
+ ObjectPropertyTreeElement.populateWithProperties(treeNode, children, false, false, linkifier);
1528
1643
  }
1529
1644
 
1530
1645
  override async onpopulate(): Promise<void> {
1531
- if (this.propertyCount >= ArrayGroupingTreeElement.bucketThreshold) {
1532
- await ArrayGroupingTreeElement.populateRanges(
1533
- this, this.object, this.fromIndex, this.toIndex, false, this.linkifier);
1534
- return;
1535
- }
1536
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
1537
- // @ts-expect-error
1538
- await ArrayGroupingTreeElement.populateAsFragment(this, this.object, this.fromIndex, this.toIndex, this.linkifier);
1646
+ this.removeChildren();
1647
+ this.#child.removeChildren();
1648
+ await ObjectPropertyTreeElement.populate(this, this.#child, false, false, this.linkifier);
1539
1649
  }
1540
1650
 
1541
1651
  override onattach(): void {
1542
1652
  this.listItemElement.classList.add('object-properties-section-name');
1543
1653
  }
1544
1654
 
1545
- private static bucketThreshold = 100;
1546
- private static sparseIterationThreshold = 250000;
1655
+ // These should be module constants but they are modified by layout tests.
1656
+ static bucketThreshold = 100;
1657
+ static sparseIterationThreshold = 250000;
1547
1658
  }
1548
1659
 
1549
1660
  export class ObjectPropertyPrompt extends UI.TextPrompt.TextPrompt {
@@ -1614,12 +1725,8 @@ export class ObjectPropertiesSectionsTreeExpandController {
1614
1725
  let result;
1615
1726
  while (current !== rootElement) {
1616
1727
  let currentName = '';
1617
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
1618
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1619
- if ((current as any).property) {
1620
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
1621
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1622
- currentName = (current as any).property.name;
1728
+ if (current instanceof ObjectPropertyTreeElement) {
1729
+ currentName = current.property.name;
1623
1730
  } else {
1624
1731
  currentName = typeof current.title === 'string' ? current.title : current.title.textContent || '';
1625
1732
  }
@@ -1667,74 +1774,86 @@ export class Renderer implements UI.UIUtils.Renderer {
1667
1774
  }
1668
1775
  }
1669
1776
 
1670
- export class ObjectPropertyValue implements UI.ContextMenu.Provider<Object> {
1671
- element: Element;
1672
- constructor(element: Element) {
1673
- this.element = element;
1674
- }
1675
-
1676
- appendApplicableItems(_event: Event, _contextMenu: UI.ContextMenu.ContextMenu, _object: Object): void {
1677
- }
1678
- }
1679
-
1680
- export class ExpandableTextPropertyValue extends ObjectPropertyValue {
1777
+ export class ExpandableTextPropertyValue {
1681
1778
  private readonly text: string;
1682
1779
  private readonly maxLength: number;
1683
- private expandElement: Element|null;
1684
1780
  private readonly maxDisplayableTextLength: number;
1685
- private readonly expandElementText: Common.UIString.LocalizedString|undefined;
1686
- private readonly copyButtonText: Common.UIString.LocalizedString;
1687
- constructor(element: Element, text: string, maxLength: number) {
1688
- // abbreviated text and expandable text controls are added as children to element
1689
- super(element);
1690
- const container = element.createChild('span');
1781
+ readonly #byteCount: number;
1782
+ #expanded = false;
1783
+ #element: HTMLElement;
1784
+
1785
+ constructor(element: HTMLElement, text: string, maxLength: number) {
1786
+ this.#element = element;
1691
1787
  this.text = text;
1692
1788
  this.maxLength = maxLength;
1693
- container.textContent = text.slice(0, maxLength);
1694
- UI.Tooltip.Tooltip.install(container as HTMLElement, `${text.slice(0, maxLength)}…`);
1695
-
1696
- this.expandElement = container.createChild('button');
1697
1789
  this.maxDisplayableTextLength = 10000000;
1790
+ this.#byteCount = Platform.StringUtilities.countWtf8Bytes(text);
1791
+ this.#render();
1792
+ }
1698
1793
 
1699
- const byteCount = Platform.StringUtilities.countWtf8Bytes(text);
1700
- const totalBytesText = i18n.ByteUtilities.bytesToString(byteCount);
1701
- if (this.text.length < this.maxDisplayableTextLength) {
1702
- this.expandElementText = i18nString(UIStrings.showMoreS, {PH1: totalBytesText});
1703
- this.expandElement.setAttribute('data-text', this.expandElementText);
1704
- this.expandElement.setAttribute('jslog', `${VisualLogging.action('expand').track({click: true})}`);
1705
- this.expandElement.classList.add('expandable-inline-button');
1706
- this.expandElement.addEventListener('click', this.expandText.bind(this));
1707
- } else {
1708
- this.expandElement.setAttribute('data-text', i18nString(UIStrings.longTextWasTruncatedS, {PH1: totalBytesText}));
1709
- this.expandElement.classList.add('undisplayable-text');
1710
- }
1711
-
1712
- this.copyButtonText = i18nString(UIStrings.copy);
1713
- const copyButton = container.createChild('button', 'expandable-inline-button');
1714
- copyButton.setAttribute('data-text', this.copyButtonText);
1715
- copyButton.setAttribute('jslog', `${VisualLogging.action('copy').track({click: true})}`);
1716
- copyButton.addEventListener('click', this.copyText.bind(this));
1794
+ get element(): HTMLElement {
1795
+ return this.#element;
1717
1796
  }
1718
1797
 
1719
- override appendApplicableItems(_event: Event, contextMenu: UI.ContextMenu.ContextMenu, _object: Object): void {
1720
- if (this.text.length < this.maxDisplayableTextLength && this.expandElement) {
1798
+ #render(): void {
1799
+ const totalBytesText = i18n.ByteUtilities.bytesToString(this.#byteCount);
1800
+ const onContextMenu = (e: Event): void => {
1801
+ const {target} = e;
1802
+ if (!(target instanceof Element)) {
1803
+ return;
1804
+ }
1805
+ const listItem = target.closest('li');
1806
+ const element = listItem && UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(listItem);
1807
+ if (!(element instanceof ObjectPropertyTreeElement)) {
1808
+ return;
1809
+ }
1810
+ const contextMenu = element.getContextMenu(e);
1811
+ if (this.text.length < this.maxDisplayableTextLength && !this.#expanded) {
1812
+ contextMenu.clipboardSection().appendItem(
1813
+ i18nString(UIStrings.showMoreS, {PH1: totalBytesText}), this.expandText.bind(this),
1814
+ {jslogContext: 'show-more'});
1815
+ }
1721
1816
  contextMenu.clipboardSection().appendItem(
1722
- this.expandElementText || '', this.expandText.bind(this), {jslogContext: 'show-more'});
1723
- }
1724
- contextMenu.clipboardSection().appendItem(this.copyButtonText, this.copyText.bind(this), {jslogContext: 'copy'});
1817
+ i18nString(UIStrings.copy), this.copyText.bind(this), {jslogContext: 'copy'});
1818
+ void contextMenu.show();
1819
+ e.consume(true);
1820
+ };
1821
+
1822
+ const croppedText = this.text.slice(0, this.maxLength);
1823
+
1824
+ // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
1825
+ render(
1826
+ // clang-format off
1827
+ html`<span title=${croppedText + '…'} @contextmenu=${onContextMenu}>
1828
+ ${this.#expanded ? this.text : croppedText}
1829
+ <button
1830
+ ?hidden=${this.#expanded}
1831
+ @click=${this.#canExpand ? this.expandText.bind(this) : undefined}
1832
+ jslog=${ifDefined(this.#canExpand ? VisualLogging.action('expand').track({click: true}) : undefined)}
1833
+ class=${this.#canExpand ? 'expandable-inline-button' : 'undisplayable-text'}
1834
+ data-text=${this.#canExpand ? i18nString(UIStrings.showMoreS, {PH1: totalBytesText}) :
1835
+ i18nString(UIStrings.longTextWasTruncatedS, {PH1: totalBytesText})}
1836
+ ></button>
1837
+ <button
1838
+ class=expandable-inline-button
1839
+ @click=${this.copyText.bind(this)}
1840
+ data-text=${i18nString(UIStrings.copy)}
1841
+ jslog=${VisualLogging.action('copy').track({click: true})}
1842
+ ></button>
1843
+ </span>`,
1844
+ // clang-format on
1845
+ this.#element);
1846
+ }
1847
+
1848
+ get #canExpand(): boolean {
1849
+ return this.text.length < this.maxDisplayableTextLength;
1725
1850
  }
1726
1851
 
1727
1852
  private expandText(): void {
1728
- if (!this.expandElement) {
1729
- return;
1730
- }
1731
-
1732
- if (this.expandElement.parentElement) {
1733
- this.expandElement.parentElement.insertBefore(
1734
- document.createTextNode(this.text.slice(this.maxLength)), this.expandElement);
1853
+ if (!this.#expanded) {
1854
+ this.#expanded = true;
1855
+ this.#render();
1735
1856
  }
1736
- this.expandElement.remove();
1737
- this.expandElement = null;
1738
1857
  }
1739
1858
 
1740
1859
  private copyText(): void {