chrome-devtools-frontend 1.0.1624583 → 1.0.1625079
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.
- package/.agents/skills/foundation-test-migration/SKILL.md +2 -2
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +6 -2
- package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
- package/front_end/models/trace/insights/DuplicatedJavaScript.ts +4 -0
- package/front_end/models/trace/insights/FontDisplay.ts +4 -0
- package/front_end/models/trace/insights/LegacyJavaScript.ts +4 -0
- package/front_end/panels/emulation/DeviceModeToolbar.ts +144 -128
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +12 -4
- package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +4 -0
- package/front_end/panels/timeline/components/insights/FontDisplay.ts +4 -0
- package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +4 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +328 -47
- package/package.json +1 -1
|
@@ -111,9 +111,9 @@ Protocol requests/responses dynamically generated by Chrome (like Network condit
|
|
|
111
111
|
|
|
112
112
|
```ts
|
|
113
113
|
const emulateSpy = sinon.spy();
|
|
114
|
-
connection.
|
|
114
|
+
connection.setSuccessHandler('Network.emulateNetworkConditionsByRule', request => {
|
|
115
115
|
emulateSpy(request);
|
|
116
|
-
return {
|
|
116
|
+
return {ruleIds: []};
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
// Matches only the fields you care about, ignoring extra protocol fields
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import type * as SDK from '../../../core/sdk/sdk.js';
|
|
6
|
-
import
|
|
6
|
+
import * as Protocol from '../../../generated/protocol.js';
|
|
7
7
|
import * as Annotations from '../../annotations/annotations.js';
|
|
8
8
|
import * as Logs from '../../logs/logs.js';
|
|
9
9
|
import * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
|
|
@@ -115,7 +115,11 @@ export class NetworkRequestFormatter {
|
|
|
115
115
|
}): string {
|
|
116
116
|
const lines = [];
|
|
117
117
|
if (reasons.blockedReason) {
|
|
118
|
-
|
|
118
|
+
if (reasons.blockedReason === Protocol.Network.BlockedReason.Inspector) {
|
|
119
|
+
lines.push('Blocked reason: a custom network condition in DevTools is blocking this request');
|
|
120
|
+
} else {
|
|
121
|
+
lines.push(`Blocked reason: ${reasons.blockedReason}`);
|
|
122
|
+
}
|
|
119
123
|
}
|
|
120
124
|
if (reasons.corsErrorStatus) {
|
|
121
125
|
lines.push(`CORS error: ${reasons.corsErrorStatus.corsError} ${reasons.corsErrorStatus.failedParameter}`);
|
|
@@ -7024,6 +7024,10 @@ export const NativeFunctions = [
|
|
|
7024
7024
|
name: "prependHTMLUnsafe",
|
|
7025
7025
|
signatures: [["html","?options"]]
|
|
7026
7026
|
},
|
|
7027
|
+
{
|
|
7028
|
+
name: "matchContainer",
|
|
7029
|
+
signatures: [["query"]]
|
|
7030
|
+
},
|
|
7027
7031
|
{
|
|
7028
7032
|
name: "scrollIntoViewIfNeeded",
|
|
7029
7033
|
signatures: [["?centerIfNeeded"]]
|
|
@@ -31,6 +31,10 @@ export const UIStrings = {
|
|
|
31
31
|
columnSource: 'Source',
|
|
32
32
|
/** Label for a column in a data table; entries will be the number of wasted bytes due to duplication of a web resource. */
|
|
33
33
|
columnDuplicatedBytes: 'Duplicated bytes',
|
|
34
|
+
/**
|
|
35
|
+
* @description Message shown when no duplicated JavaScript is found.
|
|
36
|
+
*/
|
|
37
|
+
noDuplicatedJavaScript: 'No duplicated JavaScript found',
|
|
34
38
|
} as const;
|
|
35
39
|
|
|
36
40
|
const str_ = i18n.i18n.registerUIStrings('models/trace/insights/DuplicatedJavaScript.ts', UIStrings);
|
|
@@ -28,6 +28,10 @@ export const UIStrings = {
|
|
|
28
28
|
fontColumn: 'Font',
|
|
29
29
|
/** Column for the amount of time wasted. */
|
|
30
30
|
wastedTimeColumn: 'Wasted time',
|
|
31
|
+
/**
|
|
32
|
+
* @description Message shown when no fonts with suboptimal font-display are found.
|
|
33
|
+
*/
|
|
34
|
+
noFonts: 'No fonts with suboptimal font-display found',
|
|
31
35
|
} as const;
|
|
32
36
|
|
|
33
37
|
const str_ = i18n.i18n.registerUIStrings('models/trace/insights/FontDisplay.ts', UIStrings);
|
|
@@ -33,6 +33,10 @@ export const UIStrings = {
|
|
|
33
33
|
columnScript: 'Script',
|
|
34
34
|
/** Label for a column in a data table; entries will be the number of wasted bytes (aka the estimated savings in terms of bytes). */
|
|
35
35
|
columnWastedBytes: 'Wasted bytes',
|
|
36
|
+
/**
|
|
37
|
+
* @description Message shown when no legacy JavaScript is found.
|
|
38
|
+
*/
|
|
39
|
+
noLegacyJavaScript: 'No legacy JavaScript found',
|
|
36
40
|
} as const;
|
|
37
41
|
|
|
38
42
|
const str_ = i18n.i18n.registerUIStrings('models/trace/insights/LegacyJavaScript.ts', UIStrings);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
|
+
/* eslint-disable @devtools/no-lit-render-outside-of-view */
|
|
5
6
|
|
|
6
7
|
import '../../ui/legacy/legacy.js';
|
|
7
8
|
|
|
@@ -11,8 +12,8 @@ import * as i18n from '../../core/i18n/i18n.js';
|
|
|
11
12
|
import * as Platform from '../../core/platform/platform.js';
|
|
12
13
|
import * as EmulationModel from '../../models/emulation/emulation.js';
|
|
13
14
|
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
14
|
-
import * as uiI18n from '../../ui/i18n/i18n.js';
|
|
15
15
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
16
|
+
import {Directives, html, i18nTemplate, type LitTemplate, render} from '../../ui/lit/lit.js';
|
|
16
17
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
17
18
|
import * as MobileThrottling from '../mobile_throttling/mobile_throttling.js';
|
|
18
19
|
|
|
@@ -187,6 +188,9 @@ const UIStrings = {
|
|
|
187
188
|
} as const;
|
|
188
189
|
const str_ = i18n.i18n.registerUIStrings('panels/emulation/DeviceModeToolbar.ts', UIStrings);
|
|
189
190
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
191
|
+
const {ref, classMap} = Directives;
|
|
192
|
+
const {widget} = UI.Widget;
|
|
193
|
+
const {bindToSetting} = UI.UIUtils;
|
|
190
194
|
|
|
191
195
|
/**
|
|
192
196
|
* Even though the emulation panel uses all UI elements, the tooltips are not supported.
|
|
@@ -218,8 +222,8 @@ export class DeviceModeToolbar {
|
|
|
218
222
|
private spanButton!: Buttons.Button.Button;
|
|
219
223
|
private postureItem!: HTMLSelectElement;
|
|
220
224
|
private modeButton!: Buttons.Button.Button;
|
|
221
|
-
private widthInput
|
|
222
|
-
private heightInput
|
|
225
|
+
private widthInput!: HTMLInputElement;
|
|
226
|
+
private heightInput!: HTMLInputElement;
|
|
223
227
|
private deviceScaleItem!: HTMLSelectElement;
|
|
224
228
|
private deviceScaleItems: HTMLElement[] = [];
|
|
225
229
|
private deviceSelectItem!: HTMLSelectElement;
|
|
@@ -255,9 +259,6 @@ export class DeviceModeToolbar {
|
|
|
255
259
|
|
|
256
260
|
this.lastMode = new Map();
|
|
257
261
|
|
|
258
|
-
this.widthInput = this.createSizeInput(i18nString(UIStrings.width), 'width');
|
|
259
|
-
this.heightInput = this.createSizeInput(i18nString(UIStrings.heightLeaveEmptyForFull), 'height');
|
|
260
|
-
|
|
261
262
|
this.#element = document.createElement('div');
|
|
262
263
|
this.#element.classList.add('device-mode-toolbar');
|
|
263
264
|
this.#element.setAttribute('jslog', `${VisualLogging.toolbar('device-mode').track({resize: true})}`);
|
|
@@ -304,16 +305,22 @@ export class DeviceModeToolbar {
|
|
|
304
305
|
return element;
|
|
305
306
|
}
|
|
306
307
|
|
|
307
|
-
private createSizeInput(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
308
|
+
private createSizeInput(
|
|
309
|
+
title: string, jslogContext: string, value: string|undefined, disabled: boolean|undefined,
|
|
310
|
+
refCallback: (el: Element|undefined) => void, onChange: () => void): LitTemplate {
|
|
311
|
+
return html`
|
|
312
|
+
<input type="number"
|
|
313
|
+
max=${EmulationModel.DeviceModeModel.MaxDeviceSize}
|
|
314
|
+
min=${EmulationModel.DeviceModeModel.MinDeviceSize}
|
|
315
|
+
title=${title}
|
|
316
|
+
class="device-mode-size-input"
|
|
317
|
+
.disabled=${disabled ?? false}
|
|
318
|
+
jslog=${VisualLogging.textField().track({change: true}).context(jslogContext)}
|
|
319
|
+
.value=${value ?? ''}
|
|
320
|
+
@change=${onChange}
|
|
321
|
+
${ref(refCallback)}
|
|
322
|
+
@keydown=${(event: Event): void => {
|
|
323
|
+
const input = event.target as HTMLInputElement;
|
|
317
324
|
let modifiedValue = UI.UIUtils.modifiedFloatNumber(Number(input.value), event);
|
|
318
325
|
if (modifiedValue === null) {
|
|
319
326
|
return;
|
|
@@ -324,125 +331,134 @@ export class DeviceModeToolbar {
|
|
|
324
331
|
event.preventDefault();
|
|
325
332
|
input.value = String(modifiedValue);
|
|
326
333
|
input.dispatchEvent(new Event('change'));
|
|
327
|
-
}
|
|
328
|
-
return input;
|
|
334
|
+
}}>`;
|
|
329
335
|
}
|
|
330
336
|
|
|
331
337
|
private createMainToolbar(): UI.Toolbar.Toolbar {
|
|
332
338
|
const mainToolbar = this.#element.createChild('devtools-toolbar', 'main-toolbar');
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
339
|
+
// clang-format off
|
|
340
|
+
render(html`
|
|
341
|
+
<div class="device-mode-empty-toolbar-element"></div>
|
|
342
|
+
${i18nTemplate(str_, UIStrings.dimensions, {PH1: html`
|
|
343
|
+
<select class="dark-text toolbar-has-dropdown-shrinkable"
|
|
344
|
+
title=${i18nString(UIStrings.deviceType)}
|
|
345
|
+
aria-label=${i18nString(UIStrings.deviceType)}
|
|
346
|
+
@change=${this.onDeviceChange.bind(this)}
|
|
347
|
+
jslog=${VisualLogging.dropDown().track({change: true}).context('device')}
|
|
348
|
+
${ref(el => { this.deviceSelectItem = el as HTMLSelectElement; })}>
|
|
349
|
+
</select>`})}
|
|
350
|
+
|
|
351
|
+
${this.createSizeInput(
|
|
352
|
+
i18nString(UIStrings.width), 'width', this.widthInput?.value, this.widthInput?.disabled,
|
|
353
|
+
el => { this.widthInput = el as HTMLInputElement; },
|
|
354
|
+
() => {
|
|
355
|
+
const width = Number(this.widthInput.value);
|
|
356
|
+
if (this.autoAdjustScaleSetting.get()) {
|
|
357
|
+
this.model.setWidthAndScaleToFit(width);
|
|
358
|
+
} else {
|
|
359
|
+
this.model.setWidth(width);
|
|
360
|
+
}
|
|
361
|
+
})}
|
|
362
|
+
|
|
363
|
+
<div class="device-mode-x" ${ref(el => { this.xItem = el as HTMLElement; })}>×</div>
|
|
364
|
+
${this.createSizeInput(
|
|
365
|
+
i18nString(UIStrings.heightLeaveEmptyForFull), 'height', this.heightInput?.value, this.heightInput?.disabled,
|
|
366
|
+
el => { this.heightInput = el as HTMLInputElement; },
|
|
367
|
+
() => {
|
|
368
|
+
const height = Number(this.heightInput.value);
|
|
369
|
+
if (this.autoAdjustScaleSetting.get()) {
|
|
370
|
+
this.model.setHeightAndScaleToFit(height);
|
|
371
|
+
} else {
|
|
372
|
+
this.model.setHeight(height);
|
|
373
|
+
}
|
|
374
|
+
})}
|
|
375
|
+
|
|
376
|
+
<div class="device-mode-empty-toolbar-element"></div>
|
|
377
|
+
<select class="dark-text toolbar-has-dropdown-shrinkable"
|
|
378
|
+
title=${i18nString(UIStrings.zoom)}
|
|
379
|
+
aria-label=${i18nString(UIStrings.zoom)}
|
|
380
|
+
@change=${this.onScaleChange.bind(this)}
|
|
381
|
+
jslog=${VisualLogging.dropDown().track({change: true}).context('scale')}
|
|
382
|
+
${ref((el: Element|undefined) => { this.scaleItem = el as HTMLSelectElement; })}>
|
|
383
|
+
</select>
|
|
384
|
+
|
|
385
|
+
<devtools-button .data=${{variant: Buttons.Button.Variant.TOOLBAR, iconName: 'center-focus-weak',
|
|
386
|
+
toggledIconName: 'center-focus-weak', toggleType: Buttons.Button.ToggleType.PRIMARY} as Buttons.Button.ButtonData}
|
|
387
|
+
class="toolbar-button" title=${i18nString(UIStrings.autoadjustZoom)}
|
|
388
|
+
${bindToSetting(this.autoAdjustScaleSetting)}>
|
|
389
|
+
</devtools-button>
|
|
390
|
+
|
|
391
|
+
<div class="device-mode-empty-toolbar-element"></div>
|
|
392
|
+
|
|
393
|
+
<span>
|
|
394
|
+
${i18nTemplate(str_, UIStrings.dpr, {
|
|
395
|
+
PH1: html`
|
|
396
|
+
<select class="dark-text toolbar-has-dropdown-shrinkable"
|
|
397
|
+
title=${i18nString(UIStrings.devicePixelRatio)}
|
|
398
|
+
aria-label=${i18nString(UIStrings.devicePixelRatio)}
|
|
399
|
+
@change=${this.onDeviceScaleChange.bind(this)}
|
|
400
|
+
jslog=${VisualLogging.dropDown().track({change: true}).context('device-pixel-ratio')}
|
|
401
|
+
${ref(el => { this.deviceScaleItem = el as HTMLSelectElement; })}>
|
|
402
|
+
</select>`
|
|
403
|
+
})}
|
|
404
|
+
</span>
|
|
405
|
+
|
|
406
|
+
<div class="device-mode-empty-toolbar-element"></div>
|
|
407
|
+
<select class="dark-text toolbar-has-dropdown-shrinkable
|
|
408
|
+
${classMap({hidden: !this.showUserAgentTypeSetting.get()})}"
|
|
409
|
+
title=${i18nString(UIStrings.deviceType)}
|
|
410
|
+
aria-label=${i18nString(UIStrings.deviceType)}
|
|
411
|
+
@change=${this.onUAChange.bind(this)}
|
|
412
|
+
jslog=${VisualLogging.dropDown().track({change: true}).context('device-type')}
|
|
413
|
+
${ref(el => { this.uaItem = el as HTMLSelectElement; })}>
|
|
414
|
+
</select>
|
|
415
|
+
<select class="dark-text" ${widget(MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect, {
|
|
416
|
+
title: i18nString(UIStrings.throttling),
|
|
417
|
+
bindToGlobalConditions: true,
|
|
418
|
+
})}></select>
|
|
419
|
+
<select class="dark-text toolbar-has-dropdown-shrinkable" ${widget(
|
|
420
|
+
MobileThrottling.ThrottlingManager.SaveDataOverrideSelect)}></select>
|
|
421
|
+
|
|
422
|
+
<div class="device-mode-empty-toolbar-element"></div>
|
|
423
|
+
<devtools-button class="toolbar-button"
|
|
424
|
+
.data=${{variant: Buttons.Button.Variant.ICON, iconName: 'screen-rotation'} as Buttons.Button.ButtonData}
|
|
425
|
+
jslog=${VisualLogging.action('screen-rotation').track({click: true})}
|
|
426
|
+
@click=${this.modeMenuClicked.bind(this)}
|
|
427
|
+
${ref(el => { this.modeButton = el as Buttons.Button.Button; })}>
|
|
428
|
+
</devtools-button>
|
|
429
|
+
|
|
430
|
+
<!-- Show dual screen toolbar -->
|
|
431
|
+
<devtools-button class="toolbar-button"
|
|
432
|
+
.data=${{variant: Buttons.Button.Variant.ICON, iconName: 'device-fold'} as Buttons.Button.ButtonData}
|
|
433
|
+
jslog=${VisualLogging.action('device-fold').track({click: true})}
|
|
434
|
+
@click=${this.spanClicked.bind(this)}
|
|
435
|
+
${ref(el => { this.spanButton = el as Buttons.Button.Button; })}>
|
|
436
|
+
</devtools-button>
|
|
437
|
+
|
|
438
|
+
<!-- Show posture toolbar menu for foldable devices. -->
|
|
439
|
+
<div class="device-mode-empty-toolbar-element"></div>
|
|
440
|
+
<select class="dark-text toolbar-has-dropdown-shrinkable"
|
|
441
|
+
title=${i18nString(UIStrings.devicePosture)}
|
|
442
|
+
aria-label=${i18nString(UIStrings.devicePosture)}
|
|
443
|
+
@change=${this.onPostureChange.bind(this)}
|
|
444
|
+
jslog=${VisualLogging.dropDown().track({change: true}).context('device-posture')}
|
|
445
|
+
${ref(el => { this.postureItem = el as HTMLSelectElement; })}>
|
|
446
|
+
</select>`,
|
|
447
|
+
mainToolbar);
|
|
448
|
+
// clang-format on
|
|
449
|
+
const deviceScaleSpan = this.deviceScaleItem.parentElement as HTMLElement;
|
|
391
450
|
for (const node of Array.from(deviceScaleSpan.childNodes)) {
|
|
392
|
-
if (node
|
|
393
|
-
this.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
this.deviceScaleItems.push(item.element);
|
|
400
|
-
mainToolbar.appendToolbarItem(item);
|
|
451
|
+
if (node instanceof HTMLElement) {
|
|
452
|
+
this.deviceScaleItems.push(node);
|
|
453
|
+
} else if (node instanceof Text) {
|
|
454
|
+
const span = document.createElement('span');
|
|
455
|
+
span.classList.add('toolbar-text');
|
|
456
|
+
span.appendChild(node);
|
|
457
|
+
this.deviceScaleItems.push(span);
|
|
401
458
|
}
|
|
402
459
|
}
|
|
403
|
-
|
|
404
|
-
this.
|
|
405
|
-
this.uaItem.classList.add('dark-text', 'toolbar-has-dropdown-shrinkable');
|
|
406
|
-
this.uaItem.title = i18nString(UIStrings.deviceType);
|
|
407
|
-
UI.ARIAUtils.setLabel(this.uaItem, i18nString(UIStrings.deviceType));
|
|
408
|
-
this.uaItem.addEventListener('change', this.onUAChange.bind(this));
|
|
409
|
-
this.uaItem.setAttribute('jslog', `${VisualLogging.dropDown().track({change: true}).context('device-type')}`);
|
|
410
|
-
this.uaItem.classList.toggle('hidden', !this.showUserAgentTypeSetting.get());
|
|
411
|
-
mainToolbar.append(this.uaItem);
|
|
412
|
-
|
|
413
|
-
MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect.createForGlobalConditions(
|
|
414
|
-
mainToolbar, i18nString(UIStrings.throttling));
|
|
415
|
-
const saveDataItem = MobileThrottling.ThrottlingManager.throttlingManager().createSaveDataOverrideSelector();
|
|
416
|
-
saveDataItem.classList.add('dark-text', 'toolbar-has-dropdown-shrinkable');
|
|
417
|
-
mainToolbar.append(saveDataItem);
|
|
418
|
-
|
|
419
|
-
mainToolbar.append(this.createEmptyToolbarElement());
|
|
420
|
-
this.modeButton = new Buttons.Button.Button();
|
|
421
|
-
this.modeButton.classList.add('toolbar-button');
|
|
422
|
-
this.modeButton.data = {variant: Buttons.Button.Variant.ICON, iconName: 'screen-rotation'};
|
|
423
|
-
this.modeButton.setAttribute('jslog', `${VisualLogging.action('screen-rotation').track({click: true})}`);
|
|
424
|
-
this.modeButton.addEventListener('click', this.modeMenuClicked.bind(this));
|
|
425
|
-
mainToolbar.append(this.modeButton);
|
|
426
|
-
|
|
427
|
-
// Show dual screen toolbar.
|
|
428
|
-
this.spanButton = new Buttons.Button.Button();
|
|
429
|
-
this.spanButton.classList.add('toolbar-button');
|
|
430
|
-
this.spanButton.data = {variant: Buttons.Button.Variant.ICON, iconName: 'device-fold'};
|
|
431
|
-
this.spanButton.setAttribute('jslog', `${VisualLogging.action('device-fold').track({click: true})}`);
|
|
432
|
-
this.spanButton.addEventListener('click', this.spanClicked.bind(this));
|
|
433
|
-
mainToolbar.append(this.spanButton);
|
|
434
|
-
|
|
435
|
-
// Show posture toolbar menu for foldable devices.
|
|
436
|
-
mainToolbar.append(this.createEmptyToolbarElement());
|
|
437
|
-
this.postureItem = document.createElement('select');
|
|
438
|
-
this.postureItem.classList.add('dark-text', 'toolbar-has-dropdown-shrinkable');
|
|
439
|
-
this.postureItem.title = i18nString(UIStrings.devicePosture);
|
|
440
|
-
UI.ARIAUtils.setLabel(this.postureItem, i18nString(UIStrings.devicePosture));
|
|
441
|
-
this.postureItem.addEventListener('change', this.onPostureChange.bind(this));
|
|
442
|
-
this.postureItem.setAttribute(
|
|
443
|
-
'jslog', `${VisualLogging.dropDown().track({change: true}).context('device-posture')}`);
|
|
444
|
-
mainToolbar.append(this.postureItem);
|
|
445
|
-
|
|
460
|
+
deviceScaleSpan.replaceWith(...this.deviceScaleItems);
|
|
461
|
+
this.updateDeviceScaleFactorVisibility();
|
|
446
462
|
return mainToolbar;
|
|
447
463
|
}
|
|
448
464
|
|
|
@@ -206,7 +206,6 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
|
|
|
206
206
|
const activeInsight = this.#data.activeInsight;
|
|
207
207
|
const agentFocus = AIAssistance.AIContext.AgentFocus.fromInsight(this.#data.parsedTrace, model);
|
|
208
208
|
const isActiveInsight = activeInsight?.model === model;
|
|
209
|
-
|
|
210
209
|
const componentClass = INSIGHT_NAME_TO_COMPONENT[insightName as keyof typeof INSIGHT_NAME_TO_COMPONENT];
|
|
211
210
|
const widgetConfig = {
|
|
212
211
|
selected: isActiveInsight,
|
|
@@ -222,10 +221,19 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
|
|
|
222
221
|
fieldMetrics,
|
|
223
222
|
};
|
|
224
223
|
|
|
224
|
+
const items = [{componentClass, widgetConfig}];
|
|
225
|
+
|
|
225
226
|
// clang-format off
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
// We use `repeat` to force the widget to be recreated if the model
|
|
228
|
+
// changes (e.g. on new trace import). If Lit tries to reuse DOM
|
|
229
|
+
// across different traces, where the ordering of the sidebar
|
|
230
|
+
// insights changes, this causes errors.
|
|
231
|
+
const output = Lit.Directives.repeat(items, data => data.widgetConfig.model, data => {
|
|
232
|
+
return html`<devtools-widget class="insight-component-widget" ?highlight-insight=${isActiveInsight && this.#isActiveInsightHighlighted}
|
|
233
|
+
${widget(data.componentClass, data.widgetConfig)}
|
|
234
|
+
></devtools-widget>`;
|
|
235
|
+
});
|
|
236
|
+
return html`${output}`;
|
|
229
237
|
// clang-format on
|
|
230
238
|
}
|
|
231
239
|
|
|
@@ -58,6 +58,10 @@ export class DuplicatedJavaScript extends BaseInsightComponent<DuplicatedJavaScr
|
|
|
58
58
|
return Lit.nothing;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
if (this.model.duplicationGroupedByNodeModules.size === 0) {
|
|
62
|
+
return html`<div class="insight-section">${i18nString(UIStrings.noDuplicatedJavaScript)}</div>`;
|
|
63
|
+
}
|
|
64
|
+
|
|
61
65
|
const rows: TableDataRow[] =
|
|
62
66
|
[...this.model.duplicationGroupedByNodeModules.entries()].slice(0, 10).map(([source, data]) => {
|
|
63
67
|
const scriptToOverlay = new Map();
|
|
@@ -73,6 +73,10 @@ export class FontDisplay extends BaseInsightComponent<FontDisplayInsightModel> {
|
|
|
73
73
|
return Lit.nothing;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
if (this.model.fonts.length === 0) {
|
|
77
|
+
return html`<div class="insight-section">${i18nString(UIStrings.noFonts)}</div>`;
|
|
78
|
+
}
|
|
79
|
+
|
|
76
80
|
const rows = createLimitedRows(this.model.fonts, this);
|
|
77
81
|
|
|
78
82
|
// clang-format off
|
|
@@ -58,6 +58,10 @@ export class LegacyJavaScript extends BaseInsightComponent<LegacyJavaScriptInsig
|
|
|
58
58
|
return Lit.nothing;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
if (this.model.legacyJavaScriptResults.size === 0) {
|
|
62
|
+
return html`<div class="insight-section">${i18nString(UIStrings.noLegacyJavaScript)}</div>`;
|
|
63
|
+
}
|
|
64
|
+
|
|
61
65
|
const rows: TableDataRow[] =
|
|
62
66
|
[...this.model.legacyJavaScriptResults.entries()].slice(0, 10).map(([script, result]) => {
|
|
63
67
|
const overlays: Trace.Types.Overlays.Overlay[] = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Name: Dependencies sourced from the upstream `chromium` repository
|
|
2
2
|
URL: Internal
|
|
3
3
|
Version: N/A
|
|
4
|
-
Revision:
|
|
4
|
+
Revision: 9f3e9aaccba63bd2ec30334e45e0bfd07ebcc8f1
|
|
5
5
|
Update Mechanism: Manual (https://crbug.com/428069060)
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
License File: LICENSE
|
|
@@ -152,18 +152,236 @@ interface NodeChildren {
|
|
|
152
152
|
export interface ObjectTreeOptions {
|
|
153
153
|
readonly propertiesMode: ObjectPropertiesMode;
|
|
154
154
|
readonly readOnly: boolean;
|
|
155
|
+
readonly expansionTracker?: ObjectTreeExpansionTracker;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
type KeyTypes = {
|
|
159
|
+
[K in keyof Required<NodeChildren>]: Key<Required<NodeChildren>[K][0]>;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
type Key<T> = T extends ObjectTreeNode ? string : T extends ArrayGroupTreeNode ? ArrayGroupRange : never;
|
|
163
|
+
interface TrackingKey {
|
|
164
|
+
type: keyof KeyTypes;
|
|
165
|
+
key: KeyTypes[keyof KeyTypes];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
class NodeExpansionLog {
|
|
169
|
+
properties = new Map<string, NodeExpansionLog>();
|
|
170
|
+
internalProperties = new Map<string, NodeExpansionLog>();
|
|
171
|
+
arrayRanges = new Map<string, NodeExpansionLog>();
|
|
172
|
+
accessors = new Map<string, NodeExpansionLog>();
|
|
173
|
+
|
|
174
|
+
remove(key: TrackingKey): boolean {
|
|
175
|
+
return this[key.type].delete(NodeExpansionLog.#serializeKey(key));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
get(key: TrackingKey): NodeExpansionLog|undefined {
|
|
179
|
+
return this[key.type].get(NodeExpansionLog.#serializeKey(key));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getOrInsert(key: TrackingKey): NodeExpansionLog {
|
|
183
|
+
const map = this[key.type];
|
|
184
|
+
const serializedKey = NodeExpansionLog.#serializeKey(key);
|
|
185
|
+
const log = map.get(serializedKey) ?? new NodeExpansionLog();
|
|
186
|
+
if (!map.has(serializedKey)) {
|
|
187
|
+
map.set(serializedKey, log);
|
|
188
|
+
}
|
|
189
|
+
return log;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
clear(type: keyof KeyTypes): void {
|
|
193
|
+
this[type].clear();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
clearMissing(type: keyof KeyTypes, seen: TrackingKey[]): void {
|
|
197
|
+
const seenSet = new Set(seen.map(NodeExpansionLog.#serializeKey));
|
|
198
|
+
const map = this[type];
|
|
199
|
+
for (const key of map.keys()) {
|
|
200
|
+
if (!seenSet.has(key)) {
|
|
201
|
+
map.delete(key);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get empty(): boolean {
|
|
207
|
+
return this.properties.size === 0 && this.internalProperties.size === 0 && this.arrayRanges.size === 0 &&
|
|
208
|
+
this.accessors.size === 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
static #serializeKey(key: TrackingKey): string {
|
|
212
|
+
if (typeof key.key === 'string') {
|
|
213
|
+
return `${key.type}:${key.key}`;
|
|
214
|
+
}
|
|
215
|
+
return `${key.type}:${key.key.fromIndex}-${key.key.toIndex}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export class ObjectTreeExpansionTracker {
|
|
220
|
+
#log: NodeExpansionLog|null = null;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* For nodes physically nested within a parent's [[Prototype]] internal property, the node's
|
|
224
|
+
* parent property points to the logical parent (skipping the [[Prototype]] node). This helper
|
|
225
|
+
* finds that skipped [[Prototype]] node if it contains the given node.
|
|
226
|
+
*/
|
|
227
|
+
static #protoParent(node: ObjectTreeNodeBase): ObjectTreeNode|undefined {
|
|
228
|
+
if (!(node instanceof ObjectTreeNode)) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
return node.parent?.children?.internalProperties?.find(
|
|
232
|
+
p => p.name === '[[Prototype]]' && p.children?.properties?.includes(node));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static #keyType(node: ObjectTreeNodeBase): keyof NodeChildren|null {
|
|
236
|
+
if (node instanceof ObjectTreeNode) {
|
|
237
|
+
if (node.parent?.children?.properties?.includes(node)) {
|
|
238
|
+
return 'properties';
|
|
239
|
+
}
|
|
240
|
+
if (node.parent?.children?.internalProperties?.includes(node)) {
|
|
241
|
+
return 'internalProperties';
|
|
242
|
+
}
|
|
243
|
+
if (node.parent?.children?.accessors?.includes(node)) {
|
|
244
|
+
return 'accessors';
|
|
245
|
+
}
|
|
246
|
+
const proto = ObjectTreeExpansionTracker.#protoParent(node);
|
|
247
|
+
if (proto) {
|
|
248
|
+
return 'properties';
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (node instanceof ArrayGroupTreeNode && node.parent?.children?.arrayRanges?.includes(node)) {
|
|
252
|
+
return 'arrayRanges';
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
static #key(type: keyof KeyTypes, node: ObjectTreeNodeBase): TrackingKey|null {
|
|
258
|
+
switch (type) {
|
|
259
|
+
case 'arrayRanges':
|
|
260
|
+
return node instanceof ArrayGroupTreeNode ? {type, key: node.range} : null;
|
|
261
|
+
default:
|
|
262
|
+
return node instanceof ObjectTreeNode ? {type, key: node.name} : null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
clear(): void {
|
|
267
|
+
this.#log = null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async #apply(log: NodeExpansionLog, node: ObjectTreeNodeBase): Promise<void> {
|
|
271
|
+
const apply = async<KeyType extends keyof KeyTypes>(type: KeyType): Promise<void> => {
|
|
272
|
+
const nodes = node.children?.[type];
|
|
273
|
+
if (!nodes) {
|
|
274
|
+
log.clear(type);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const seen: TrackingKey[] = [];
|
|
278
|
+
for (const childNode of nodes) {
|
|
279
|
+
const key = ObjectTreeExpansionTracker.#key(type, childNode);
|
|
280
|
+
if (!key) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const childLog = log.get(key);
|
|
284
|
+
if (childLog) {
|
|
285
|
+
await this.#apply(childLog, childNode);
|
|
286
|
+
seen.push(key);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
log.clearMissing(type, seen);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
node.expanded = true;
|
|
293
|
+
await node.populateChildrenIfNeeded();
|
|
294
|
+
await apply('properties');
|
|
295
|
+
await apply('internalProperties');
|
|
296
|
+
await apply('arrayRanges');
|
|
297
|
+
await apply('accessors');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async apply(node: ObjectTree): Promise<void> {
|
|
301
|
+
if (this.#log) {
|
|
302
|
+
return await this.#apply(this.#log, node);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
static * #path(node: ObjectTreeNodeBase|undefined): Generator<TrackingKey> {
|
|
307
|
+
if (!node) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const proto = ObjectTreeExpansionTracker.#protoParent(node);
|
|
312
|
+
if (proto) {
|
|
313
|
+
yield* this.#path(proto);
|
|
314
|
+
} else {
|
|
315
|
+
yield* this.#path(node.parent);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const keyType = this.#keyType(node);
|
|
319
|
+
const key = keyType && ObjectTreeExpansionTracker.#key(keyType, node);
|
|
320
|
+
if (key) {
|
|
321
|
+
yield key;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
collapse(node: ObjectTreeNodeBase): void {
|
|
326
|
+
if (!this.#log) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (node instanceof ObjectTree) {
|
|
330
|
+
this.#log = null;
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
let lastKey;
|
|
335
|
+
let parent: NodeExpansionLog|null = null;
|
|
336
|
+
let log: NodeExpansionLog = this.#log;
|
|
337
|
+
for (const key of ObjectTreeExpansionTracker.#path(node)) {
|
|
338
|
+
lastKey = key;
|
|
339
|
+
parent = log;
|
|
340
|
+
const nextLog = log.get(key);
|
|
341
|
+
if (!nextLog) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
log = nextLog;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (lastKey && parent) {
|
|
348
|
+
parent.remove(lastKey);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
expand(node: ObjectTreeNodeBase): void {
|
|
353
|
+
if (!this.#log) {
|
|
354
|
+
this.#log = new NodeExpansionLog();
|
|
355
|
+
}
|
|
356
|
+
let log: NodeExpansionLog = this.#log;
|
|
357
|
+
for (const key of ObjectTreeExpansionTracker.#path(node)) {
|
|
358
|
+
log = log.getOrInsert(key);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
155
361
|
}
|
|
156
362
|
|
|
157
363
|
export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrapper<ObjectTreeNodeBase.EventTypes> {
|
|
158
364
|
#children?: NodeChildren;
|
|
159
365
|
protected filter: {includeNullOrUndefinedValues: boolean, regex: RegExp|null}|null = null;
|
|
160
366
|
protected extraProperties: ObjectTreeNode[] = [];
|
|
161
|
-
expanded = false;
|
|
367
|
+
#expanded = false;
|
|
162
368
|
|
|
163
369
|
constructor(readonly parent: ObjectTreeNodeBase|undefined, protected readonly options: ObjectTreeOptions) {
|
|
164
370
|
super();
|
|
165
371
|
this.filter = parent?.filter ?? null;
|
|
166
372
|
}
|
|
373
|
+
get expanded(): boolean {
|
|
374
|
+
return this.#expanded;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
set expanded(val: boolean) {
|
|
378
|
+
if (val) {
|
|
379
|
+
this.options.expansionTracker?.expand(this);
|
|
380
|
+
} else {
|
|
381
|
+
this.options.expansionTracker?.collapse(this);
|
|
382
|
+
}
|
|
383
|
+
this.#expanded = val;
|
|
384
|
+
}
|
|
167
385
|
|
|
168
386
|
get readOnly(): boolean {
|
|
169
387
|
return this.options.readOnly;
|
|
@@ -253,11 +471,22 @@ export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrap
|
|
|
253
471
|
return this.#children;
|
|
254
472
|
}
|
|
255
473
|
|
|
474
|
+
#populatePromise?: Promise<NodeChildren>;
|
|
256
475
|
async populateChildrenIfNeeded(): Promise<NodeChildren> {
|
|
257
|
-
if (
|
|
258
|
-
this.#children
|
|
476
|
+
if (this.#children) {
|
|
477
|
+
return this.#children;
|
|
259
478
|
}
|
|
260
|
-
|
|
479
|
+
if (!this.#populatePromise) {
|
|
480
|
+
this.#populatePromise = this.populateChildrenIfNeededImpl()
|
|
481
|
+
.then(children => {
|
|
482
|
+
this.#children = children;
|
|
483
|
+
return children;
|
|
484
|
+
})
|
|
485
|
+
.finally(() => {
|
|
486
|
+
this.#populatePromise = undefined;
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
return await this.#populatePromise;
|
|
261
490
|
}
|
|
262
491
|
|
|
263
492
|
protected async populateChildrenIfNeededImpl(): Promise<NodeChildren> {
|
|
@@ -271,9 +500,12 @@ export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrap
|
|
|
271
500
|
if (this.arrayLength > ARRAY_LOAD_THRESHOLD) {
|
|
272
501
|
const ranges = await arrayRangeGroups(object, 0, this.arrayLength - 1);
|
|
273
502
|
const arrayRanges = ranges?.ranges.map(
|
|
274
|
-
([fromIndex, toIndex, count]) =>
|
|
275
|
-
object, {fromIndex, toIndex, count}, effectiveParent,
|
|
276
|
-
|
|
503
|
+
([fromIndex, toIndex, count]) =>
|
|
504
|
+
new ArrayGroupTreeNode(object, {fromIndex, toIndex, count}, effectiveParent, {
|
|
505
|
+
readOnly: this.readOnly,
|
|
506
|
+
propertiesMode: this.propertiesMode,
|
|
507
|
+
expansionTracker: this.options.expansionTracker
|
|
508
|
+
}));
|
|
277
509
|
if (!arrayRanges) {
|
|
278
510
|
return {};
|
|
279
511
|
}
|
|
@@ -282,15 +514,18 @@ export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrap
|
|
|
282
514
|
await SDK.RemoteObject.RemoteObject.loadFromObjectPerProto(
|
|
283
515
|
this.object, true /* generatePreview */, true /* nonIndexedPropertiesOnly */);
|
|
284
516
|
|
|
285
|
-
const properties = objectProperties?.map(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
517
|
+
const properties = objectProperties?.map(p => new ObjectTreeNode(p, effectiveParent, {
|
|
518
|
+
readOnly: this.readOnly,
|
|
519
|
+
propertiesMode: ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
520
|
+
expansionTracker: this.options.expansionTracker
|
|
521
|
+
}));
|
|
522
|
+
|
|
523
|
+
const internalProperties =
|
|
524
|
+
objectInternalProperties?.map(p => new ObjectTreeNode(p, effectiveParent, {
|
|
525
|
+
readOnly: this.readOnly,
|
|
526
|
+
propertiesMode: ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
527
|
+
expansionTracker: this.options.expansionTracker
|
|
528
|
+
}));
|
|
294
529
|
return {arrayRanges, properties, internalProperties};
|
|
295
530
|
}
|
|
296
531
|
|
|
@@ -307,18 +542,21 @@ export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrap
|
|
|
307
542
|
break;
|
|
308
543
|
}
|
|
309
544
|
|
|
310
|
-
const properties = objectProperties?.map(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
545
|
+
const properties = objectProperties?.map(p => new ObjectTreeNode(p, effectiveParent, {
|
|
546
|
+
readOnly: this.readOnly,
|
|
547
|
+
propertiesMode: ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
548
|
+
expansionTracker: this.options.expansionTracker
|
|
549
|
+
}));
|
|
314
550
|
properties?.push(...this.extraProperties);
|
|
315
551
|
properties?.sort(ObjectPropertiesSection.compareProperties);
|
|
316
|
-
const accessors = properties && ObjectTreeNodeBase.getGettersAndSetters(properties);
|
|
317
|
-
|
|
318
|
-
const internalProperties =
|
|
319
|
-
p => new ObjectTreeNode(
|
|
320
|
-
|
|
321
|
-
|
|
552
|
+
const accessors = properties && ObjectTreeNodeBase.getGettersAndSetters(properties, this.options);
|
|
553
|
+
|
|
554
|
+
const internalProperties =
|
|
555
|
+
objectInternalProperties?.map(p => new ObjectTreeNode(p, effectiveParent, {
|
|
556
|
+
readOnly: this.readOnly,
|
|
557
|
+
propertiesMode: ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
558
|
+
expansionTracker: this.options.expansionTracker
|
|
559
|
+
}));
|
|
322
560
|
return {properties, internalProperties, accessors};
|
|
323
561
|
}
|
|
324
562
|
|
|
@@ -336,26 +574,34 @@ export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrap
|
|
|
336
574
|
}
|
|
337
575
|
|
|
338
576
|
addExtraProperties(...properties: SDK.RemoteObject.RemoteObjectProperty[]): void {
|
|
339
|
-
this.extraProperties.push(...properties.map(
|
|
340
|
-
|
|
341
|
-
|
|
577
|
+
this.extraProperties.push(...properties.map(p => new ObjectTreeNode(p, this, {
|
|
578
|
+
readOnly: this.readOnly,
|
|
579
|
+
propertiesMode: ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
580
|
+
expansionTracker: this.options.expansionTracker
|
|
581
|
+
})));
|
|
342
582
|
}
|
|
343
583
|
|
|
344
|
-
static getGettersAndSetters(properties: ObjectTreeNode[]): ObjectTreeNode[] {
|
|
584
|
+
static getGettersAndSetters(properties: ObjectTreeNode[], options: ObjectTreeOptions): ObjectTreeNode[] {
|
|
345
585
|
const gettersAndSetters = [];
|
|
346
586
|
for (const property of properties) {
|
|
347
587
|
if (property.property.isOwn) {
|
|
348
588
|
if (property.property.getter) {
|
|
349
589
|
const getterProperty = new SDK.RemoteObject.RemoteObjectProperty(
|
|
350
590
|
'get ' + property.property.name, property.property.getter, false);
|
|
351
|
-
gettersAndSetters.push(new ObjectTreeNode(
|
|
352
|
-
|
|
591
|
+
gettersAndSetters.push(new ObjectTreeNode(getterProperty, property.parent, {
|
|
592
|
+
propertiesMode: property.propertiesMode,
|
|
593
|
+
readOnly: property.readOnly,
|
|
594
|
+
expansionTracker: options.expansionTracker
|
|
595
|
+
}));
|
|
353
596
|
}
|
|
354
597
|
if (property.property.setter) {
|
|
355
598
|
const setterProperty = new SDK.RemoteObject.RemoteObjectProperty(
|
|
356
599
|
'set ' + property.property.name, property.property.setter, false);
|
|
357
|
-
gettersAndSetters.push(new ObjectTreeNode(
|
|
358
|
-
|
|
600
|
+
gettersAndSetters.push(new ObjectTreeNode(setterProperty, property.parent, {
|
|
601
|
+
propertiesMode: property.propertiesMode,
|
|
602
|
+
readOnly: property.readOnly,
|
|
603
|
+
expansionTracker: options.expansionTracker
|
|
604
|
+
}));
|
|
359
605
|
}
|
|
360
606
|
}
|
|
361
607
|
}
|
|
@@ -388,12 +634,18 @@ export class ObjectTree extends ObjectTreeNodeBase {
|
|
|
388
634
|
}
|
|
389
635
|
}
|
|
390
636
|
|
|
391
|
-
|
|
637
|
+
interface ArrayGroupRange {
|
|
638
|
+
fromIndex: number;
|
|
639
|
+
toIndex: number;
|
|
640
|
+
count: number;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export class ArrayGroupTreeNode extends ObjectTreeNodeBase {
|
|
392
644
|
readonly #object: SDK.RemoteObject.RemoteObject;
|
|
393
|
-
readonly #range:
|
|
645
|
+
readonly #range: ArrayGroupRange;
|
|
394
646
|
constructor(
|
|
395
|
-
object: SDK.RemoteObject.RemoteObject, range:
|
|
396
|
-
|
|
647
|
+
object: SDK.RemoteObject.RemoteObject, range: ArrayGroupRange, parent: ObjectTreeNodeBase,
|
|
648
|
+
options: ObjectTreeOptions) {
|
|
397
649
|
super(parent, options);
|
|
398
650
|
this.#object = object;
|
|
399
651
|
this.#range = range;
|
|
@@ -403,9 +655,11 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
|
|
|
403
655
|
if (this.#range.count > ArrayGroupingTreeElement.bucketThreshold) {
|
|
404
656
|
const ranges = await arrayRangeGroups(this.object, this.#range.fromIndex, this.#range.toIndex);
|
|
405
657
|
const arrayRanges = ranges?.ranges.map(
|
|
406
|
-
([fromIndex, toIndex, count]) => new ArrayGroupTreeNode(
|
|
407
|
-
|
|
408
|
-
|
|
658
|
+
([fromIndex, toIndex, count]) => new ArrayGroupTreeNode(this.object, {fromIndex, toIndex, count}, this, {
|
|
659
|
+
readOnly: this.readOnly,
|
|
660
|
+
propertiesMode: this.propertiesMode,
|
|
661
|
+
expansionTracker: this.options.expansionTracker
|
|
662
|
+
}));
|
|
409
663
|
return {arrayRanges};
|
|
410
664
|
}
|
|
411
665
|
|
|
@@ -421,11 +675,14 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
|
|
|
421
675
|
const allProperties =
|
|
422
676
|
await arrayFragment.getAllProperties(false /* accessorPropertiesOnly */, true /* generatePreview */);
|
|
423
677
|
arrayFragment.release();
|
|
424
|
-
const properties = allProperties.properties?.map(
|
|
425
|
-
|
|
678
|
+
const properties = allProperties.properties?.map(p => new ObjectTreeNode(p, this, {
|
|
679
|
+
propertiesMode: this.propertiesMode,
|
|
680
|
+
readOnly: this.readOnly,
|
|
681
|
+
expansionTracker: this.options.expansionTracker
|
|
682
|
+
}));
|
|
426
683
|
properties?.push(...this.extraProperties);
|
|
427
684
|
properties?.sort(ObjectPropertiesSection.compareProperties);
|
|
428
|
-
const accessors = properties && ObjectTreeNodeBase.getGettersAndSetters(properties);
|
|
685
|
+
const accessors = properties && ObjectTreeNodeBase.getGettersAndSetters(properties, this.options);
|
|
429
686
|
return {properties, accessors};
|
|
430
687
|
}
|
|
431
688
|
|
|
@@ -433,7 +690,7 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
|
|
|
433
690
|
return this.#range.fromIndex === this.#range.toIndex;
|
|
434
691
|
}
|
|
435
692
|
|
|
436
|
-
get range():
|
|
693
|
+
get range(): ArrayGroupRange {
|
|
437
694
|
return this.#range;
|
|
438
695
|
}
|
|
439
696
|
|
|
@@ -559,8 +816,10 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
|
|
|
559
816
|
object: SDK.RemoteObject.RemoteObject, title?: string|Element|null, linkifier?: Components.Linkifier.Linkifier,
|
|
560
817
|
showOverflow?: boolean, editable = true) {
|
|
561
818
|
super();
|
|
562
|
-
this.root = new ObjectTree(
|
|
563
|
-
|
|
819
|
+
this.root = new ObjectTree(object, {
|
|
820
|
+
readOnly: !editable,
|
|
821
|
+
propertiesMode: ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
|
|
822
|
+
});
|
|
564
823
|
if (!showOverflow) {
|
|
565
824
|
this.setHideOverflow(true);
|
|
566
825
|
}
|
|
@@ -995,15 +1254,20 @@ export class RootElement extends UI.TreeOutline.TreeElement {
|
|
|
995
1254
|
this.toggleOnClick = true;
|
|
996
1255
|
this.listItemElement.classList.add('object-properties-section-root-element');
|
|
997
1256
|
this.listItemElement.addEventListener('contextmenu', this.onContextMenu.bind(this), false);
|
|
1257
|
+
if (object.expanded) {
|
|
1258
|
+
this.expand();
|
|
1259
|
+
}
|
|
998
1260
|
}
|
|
999
1261
|
|
|
1000
1262
|
override onexpand(): void {
|
|
1263
|
+
this.object.expanded = true;
|
|
1001
1264
|
if (this.treeOutline) {
|
|
1002
1265
|
this.treeOutline.element.classList.add('expanded');
|
|
1003
1266
|
}
|
|
1004
1267
|
}
|
|
1005
1268
|
|
|
1006
1269
|
override oncollapse(): void {
|
|
1270
|
+
this.object.expanded = false;
|
|
1007
1271
|
if (this.treeOutline) {
|
|
1008
1272
|
this.treeOutline.element.classList.remove('expanded');
|
|
1009
1273
|
}
|
|
@@ -1350,7 +1614,11 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1350
1614
|
this.maxNumPropertiesToShow = InitialVisibleChildrenLimit;
|
|
1351
1615
|
this.listItemElement.addEventListener('contextmenu', this.contextMenuFired.bind(this), false);
|
|
1352
1616
|
this.listItemElement.dataset.objectPropertyNameForTest = property.name;
|
|
1617
|
+
this.updateExpandable();
|
|
1353
1618
|
this.setExpandRecursively(property.name !== '[[Prototype]]');
|
|
1619
|
+
if (property.expanded) {
|
|
1620
|
+
this.expand();
|
|
1621
|
+
}
|
|
1354
1622
|
}
|
|
1355
1623
|
|
|
1356
1624
|
static async populate(
|
|
@@ -1523,10 +1791,12 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1523
1791
|
}
|
|
1524
1792
|
|
|
1525
1793
|
override onexpand(): void {
|
|
1794
|
+
this.property.expanded = true;
|
|
1526
1795
|
this.#widget.expanded = true;
|
|
1527
1796
|
}
|
|
1528
1797
|
|
|
1529
1798
|
override oncollapse(): void {
|
|
1799
|
+
this.property.expanded = false;
|
|
1530
1800
|
this.#widget.expanded = false;
|
|
1531
1801
|
}
|
|
1532
1802
|
|
|
@@ -1750,6 +2020,9 @@ export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1750
2020
|
this.#child.addEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.onpopulate, this);
|
|
1751
2021
|
this.toggleOnClick = true;
|
|
1752
2022
|
this.linkifier = linkifier;
|
|
2023
|
+
if (child.expanded) {
|
|
2024
|
+
this.expand();
|
|
2025
|
+
}
|
|
1753
2026
|
}
|
|
1754
2027
|
|
|
1755
2028
|
static async populate(
|
|
@@ -1773,6 +2046,14 @@ export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1773
2046
|
ObjectPropertyTreeElement.populateWithProperties(treeNode, children, false, false, linkifier);
|
|
1774
2047
|
}
|
|
1775
2048
|
|
|
2049
|
+
override onexpand(): void {
|
|
2050
|
+
this.#child.expanded = true;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
override oncollapse(): void {
|
|
2054
|
+
this.#child.expanded = false;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
1776
2057
|
override async onpopulate(): Promise<void> {
|
|
1777
2058
|
this.removeChildren();
|
|
1778
2059
|
await ObjectPropertyTreeElement.populate(this, this.#child, false, false, this.linkifier);
|
package/package.json
CHANGED