chrome-devtools-frontend 1.0.1724140 → 1.0.1726686
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/review-ui-widgets/SKILL.md +7 -7
- package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +12 -0
- package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +3 -3
- package/.agents/skills/ui-widgets/SKILL.md +2 -1
- package/docs/cookbook/dependencies.md +6 -4
- package/front_end/core/i18n/i18nImpl.ts +1 -1
- package/front_end/core/root/Runtime.ts +1 -1
- package/front_end/core/sdk/ResourceTreeModel.ts +16 -0
- package/front_end/entrypoints/js_app/js_app.ts +2 -5
- package/front_end/entrypoints/node_app/node_app.ts +2 -3
- package/front_end/generated/InspectorBackendCommands.ts +1 -1
- package/front_end/generated/protocol.ts +1 -0
- package/front_end/models/ai_assistance/skills/lighthouse.md +2 -1
- package/front_end/models/ai_assistance/skills/network.md +1 -0
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +26 -2
- package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +1 -1
- package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +2 -7
- package/front_end/models/ai_assistance/tools/RunLighthouse.ts +2 -2
- package/front_end/models/comment_manager/CommentManager.ts +0 -6
- package/front_end/models/comment_manager/CommentThread.ts +23 -19
- package/front_end/models/elements/StyleChanges.ts +66 -0
- package/front_end/models/trace/EventsSerializer.ts +2 -7
- package/front_end/models/trace/ModelImpl.ts +0 -2
- package/front_end/models/trace/handlers/FlowsHandler.ts +37 -29
- package/front_end/models/trace/helpers/SyntheticEvents.ts +10 -37
- package/front_end/models/trace/insights/RenderBlocking.ts +3 -1
- package/front_end/models/trace/lantern/simulation/Simulator.ts +4 -0
- package/front_end/models/trace/lantern/types/Lantern.ts +1 -0
- package/front_end/panels/accessibility/SourceOrderView.ts +1 -1
- package/front_end/panels/application/ApplicationPanelSidebar.ts +90 -0
- package/front_end/panels/application/StorageView.ts +196 -174
- package/front_end/panels/application/SubAppsView.ts +2 -2
- package/front_end/panels/application/preloading/PreloadingView.ts +360 -336
- package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +2 -2
- package/front_end/panels/application/preloading/components/PreloadingGrid.ts +6 -3
- package/front_end/panels/application/preloading/components/RuleSetGrid.ts +17 -17
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -2
- package/front_end/panels/application/preloading/preloadingView.css +12 -4
- package/front_end/panels/application/preloading/preloadingViewDropDown.css +1 -1
- package/front_end/panels/application/storageView.css +16 -21
- package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +2 -2
- package/front_end/panels/browser_debugger/browser_debugger-meta.ts +6 -8
- package/front_end/panels/comments/CommentsPane.ts +26 -9
- package/front_end/panels/comments/commentsPane.css +25 -6
- package/front_end/panels/common/CommentThreadWidget.ts +2 -2
- package/front_end/panels/common/CommentsOverlayWidget.ts +94 -35
- package/front_end/panels/common/commentThreadWidget.css +17 -0
- package/front_end/panels/common/commentsOverlay.css +5 -10
- package/front_end/panels/common/common-meta.ts +11 -1
- package/front_end/panels/elements/ClassesPaneWidget.ts +130 -88
- package/front_end/panels/elements/ColorSwatchPopoverIcon.ts +7 -33
- package/front_end/panels/elements/DOMTreeWidget.ts +7 -3
- package/front_end/panels/elements/ElementStatePaneWidget.ts +11 -1
- package/front_end/panels/elements/PropertyRenderer.ts +3 -2
- package/front_end/panels/elements/StyleEditorWidget.ts +60 -41
- package/front_end/panels/elements/StylePropertiesSection.ts +32 -5
- package/front_end/panels/elements/StylePropertyTreeElement.ts +227 -162
- package/front_end/panels/event_listeners/EventListenersView.ts +11 -8
- package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +1 -1
- package/front_end/panels/network/NetworkDataGridNode.ts +3 -1
- package/front_end/panels/network/RequestConditionsDrawer.ts +1 -1
- package/front_end/panels/network/RequestPayloadView.ts +12 -5
- package/front_end/panels/profiler/HeapProfileView.ts +28 -6
- package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +7 -3
- package/front_end/panels/protocol_monitor/JSONEditor.ts +0 -1
- package/front_end/panels/recorder/RecorderPanel.ts +2 -2
- package/front_end/panels/settings/SettingsScreen.ts +1 -1
- package/front_end/panels/sources/NavigatorView.ts +105 -45
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +8 -5
- package/front_end/panels/sources/SourcesNavigator.ts +421 -213
- package/front_end/panels/sources/SourcesPanel.ts +6 -4
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +10 -5
- package/front_end/panels/sources/sources-meta.ts +4 -15
- package/front_end/panels/sources/sourcesNavigator.css +5 -0
- package/front_end/panels/timeline/ModificationsManager.ts +0 -2
- package/front_end/panels/timeline/README.md +2 -0
- package/front_end/panels/timeline/TimelineCommentController.ts +132 -30
- package/front_end/panels/timeline/TimelinePanel.ts +4 -0
- package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -1
- package/front_end/panels/timeline/utils/Treemap.ts +8 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/comments/CommentAnchorResolver.ts +3 -0
- package/front_end/ui/comments/CommentOverlayManager.ts +2 -4
- package/front_end/ui/components/lists/List.ts +14 -1
- package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +23 -14
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +3 -0
- package/front_end/ui/legacy/Toolbar.ts +0 -1
- package/front_end/ui/legacy/Treeoutline.ts +7 -2
- package/front_end/ui/legacy/UIUtils.ts +4 -1
- package/front_end/ui/legacy/components/inline_editor/Swatches.ts +19 -6
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +20 -11
- package/front_end/ui/legacy/components/source_frame/XMLView.ts +27 -7
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -14,7 +14,7 @@ import * as Types from '../types/types.js';
|
|
|
14
14
|
// included, while the amount of `step` phase events can be >= 0.
|
|
15
15
|
|
|
16
16
|
// A flow phase event is assigned to a trace event when their cat, tid,
|
|
17
|
-
// pid and ts are equal (see
|
|
17
|
+
// pid and ts are equal (see findBinding()).
|
|
18
18
|
|
|
19
19
|
// It's possible for a single event to belong to multiple flows. In that
|
|
20
20
|
// case, it will have multiple corresponding flow phase events (one
|
|
@@ -26,24 +26,27 @@ import * as Types from '../types/types.js';
|
|
|
26
26
|
// there are any corresponding flow phase events at all).
|
|
27
27
|
let flowDataByGroupToken = new Map<string, number>();
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* The flows bound to one flow binding tuple (timestamp, process id,
|
|
31
|
+
* thread id and category).
|
|
32
|
+
*/
|
|
33
|
+
interface FlowBinding {
|
|
34
|
+
ts: Types.Timing.Micro;
|
|
35
|
+
pid: Types.Events.ProcessID;
|
|
36
|
+
tid: Types.Events.ThreadID;
|
|
37
|
+
cat: string;
|
|
30
38
|
flows: Set<number>;
|
|
31
39
|
bindingParsed: boolean;
|
|
32
40
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// corresponding flow data. However, this token would be calculated for
|
|
43
|
-
// every event in a trace, resulting in a lot of memory overhead and
|
|
44
|
-
// major GC triggering. So we are trading off readability for
|
|
45
|
-
// performance.
|
|
46
|
-
let boundFlowData: FlowBindingTuple = new Map();
|
|
41
|
+
|
|
42
|
+
// Trace timestamps are microseconds and too large to be small integers
|
|
43
|
+
// in V8, so a Map keyed by the timestamp hashes a heap number on every
|
|
44
|
+
// lookup. Every non-flow event in a trace is looked up here, so this
|
|
45
|
+
// map is keyed by the low 30 bits of the timestamp instead, which are
|
|
46
|
+
// always a small integer. Bindings that share a key are told apart by
|
|
47
|
+
// comparing the full flow binding tuple.
|
|
48
|
+
const TS_KEY_MASK = 0x3fffffff;
|
|
49
|
+
let boundFlowData = new Map<number, FlowBinding[]>();
|
|
47
50
|
|
|
48
51
|
let flowsById = new Map<number, Map<Types.Timing.Micro, Types.Events.Event>>();
|
|
49
52
|
let flowEvents: Types.Events.FlowEvent[] = [];
|
|
@@ -67,8 +70,17 @@ export function handleEvent(event: Types.Events.Event): void {
|
|
|
67
70
|
nonFlowEvents.push(event);
|
|
68
71
|
}
|
|
69
72
|
|
|
73
|
+
function findBinding(bindings: FlowBinding[], event: Types.Events.Event): FlowBinding|undefined {
|
|
74
|
+
return bindings.find(binding => binding.ts === event.ts && binding.pid === event.pid && binding.tid === event.tid &&
|
|
75
|
+
binding.cat === event.cat);
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
function processNonFlowEvent(event: Types.Events.Event): void {
|
|
71
|
-
const
|
|
79
|
+
const bindings = boundFlowData.get(event.ts & TS_KEY_MASK);
|
|
80
|
+
if (!bindings) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const flowDataForEvent = findBinding(bindings, event);
|
|
72
84
|
if (!flowDataForEvent) {
|
|
73
85
|
return;
|
|
74
86
|
}
|
|
@@ -95,9 +107,8 @@ function processFlowEvent(flowPhaseEvent: Types.Events.FlowEvent): void {
|
|
|
95
107
|
const flowGroup = flowGroupTokenForFlowPhaseEvent(flowPhaseEvent);
|
|
96
108
|
switch (flowPhaseEvent.ph) {
|
|
97
109
|
case (Types.Events.Phase.FLOW_START): {
|
|
98
|
-
const flowMetadata = {flowId: flowPhaseEvent.id, times: new Map([[flowPhaseEvent.ts, undefined]])};
|
|
99
110
|
flowDataByGroupToken.set(flowGroup, flowPhaseEvent.id);
|
|
100
|
-
addFlowIdToEventBinding(flowPhaseEvent,
|
|
111
|
+
addFlowIdToEventBinding(flowPhaseEvent, flowPhaseEvent.id);
|
|
101
112
|
return;
|
|
102
113
|
}
|
|
103
114
|
case (Types.Events.Phase.FLOW_STEP): {
|
|
@@ -125,22 +136,19 @@ function processFlowEvent(flowPhaseEvent: Types.Events.FlowEvent): void {
|
|
|
125
136
|
}
|
|
126
137
|
}
|
|
127
138
|
|
|
128
|
-
type MapValueType<T extends Map<unknown, unknown>> = NonNullable<ReturnType<T['get']>>;
|
|
129
139
|
/**
|
|
130
140
|
* A single trace event can belong to multiple flows. This method
|
|
131
141
|
* tracks which flows (flowId) an event belongs to given its flow
|
|
132
142
|
* binding tuple (made of its ts, pid, tid and cat).
|
|
133
143
|
*/
|
|
134
144
|
function addFlowIdToEventBinding(event: Types.Events.Event, flowId: number): void {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
Platform.MapUtilities.getWithDefault(flowsByCat, event.cat, () => ({flows: new Set(), bindingParsed: false}));
|
|
143
|
-
flowData.flows.add(flowId);
|
|
145
|
+
const bindings = Platform.MapUtilities.getWithDefault(boundFlowData, event.ts & TS_KEY_MASK, () => []);
|
|
146
|
+
let binding = findBinding(bindings, event);
|
|
147
|
+
if (!binding) {
|
|
148
|
+
binding = {ts: event.ts, pid: event.pid, tid: event.tid, cat: event.cat, flows: new Set(), bindingParsed: false};
|
|
149
|
+
bindings.push(binding);
|
|
150
|
+
}
|
|
151
|
+
binding.flows.add(flowId);
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
/**
|
|
@@ -8,12 +8,14 @@ let activeManager: SyntheticEventsManager|null = null;
|
|
|
8
8
|
|
|
9
9
|
export class SyntheticEventsManager {
|
|
10
10
|
/**
|
|
11
|
-
* All synthetic entries created in a trace
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* All synthetic entries created in a trace, keyed by the raw trace event
|
|
12
|
+
* they are based on. (ProfileCalls and layout shift clusters are excluded
|
|
13
|
+
* because they are not based on a real trace event.) Keying by the raw
|
|
14
|
+
* event rather than by its index in the raw events array means
|
|
15
|
+
* registration does not need to look up the index, which would require a
|
|
16
|
+
* map of every raw event in the trace.
|
|
15
17
|
*/
|
|
16
|
-
#
|
|
18
|
+
#syntheticEventByRawEvent = new WeakMap<Types.Events.Event, Types.Events.SyntheticBased>();
|
|
17
19
|
/**
|
|
18
20
|
* All raw entries from a trace.
|
|
19
21
|
*/
|
|
@@ -56,55 +58,26 @@ export class SyntheticEventsManager {
|
|
|
56
58
|
this.#rawTraceEvents = rawEvents;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
#rawEventIndexes: Map<Types.Events.Event, number>|null = null;
|
|
60
|
-
|
|
61
|
-
#getRawEventIndex(event: Types.Events.Event): number {
|
|
62
|
-
if (!this.#rawEventIndexes) {
|
|
63
|
-
this.#rawEventIndexes = new Map();
|
|
64
|
-
for (let i = 0; i < this.#rawTraceEvents.length; i++) {
|
|
65
|
-
this.#rawEventIndexes.set(this.#rawTraceEvents[i], i);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return this.#rawEventIndexes.get(event) ?? -1;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Clears the transient raw event index map.
|
|
73
|
-
* During trace parsing, this map provides O(1) lookups for registering synthetic events.
|
|
74
|
-
* Once parsing is complete, queries look up synthetic events exclusively by index in
|
|
75
|
-
* `#syntheticTraces`, so this map is discarded to reclaim memory.
|
|
76
|
-
*/
|
|
77
|
-
clearRegistrationIndex(): void {
|
|
78
|
-
this.#rawEventIndexes = null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
61
|
/**
|
|
82
62
|
* Registers and returns a branded synthetic event. Synthetic events need to
|
|
83
63
|
* be created with this method to ensure they are registered and made
|
|
84
64
|
* available to load events using serialized keys.
|
|
85
65
|
*/
|
|
86
66
|
#registerSyntheticEvent<T extends Types.Events.SyntheticBased>(syntheticEvent: Omit<T, '_tag'>): T {
|
|
87
|
-
const rawIndex = this.#getRawEventIndex(syntheticEvent.rawSourceEvent);
|
|
88
|
-
if (rawIndex < 0) {
|
|
89
|
-
throw new Error('Attempted to register a synthetic event paired to an unknown raw event.');
|
|
90
|
-
}
|
|
91
67
|
const eventAsSynthetic = syntheticEvent as T;
|
|
92
|
-
this.#
|
|
68
|
+
this.#syntheticEventByRawEvent.set(syntheticEvent.rawSourceEvent, eventAsSynthetic);
|
|
93
69
|
return eventAsSynthetic;
|
|
94
70
|
}
|
|
95
71
|
|
|
96
72
|
syntheticEventForRawEventIndex(rawEventIndex: number): Types.Events.SyntheticBased {
|
|
97
|
-
const
|
|
73
|
+
const rawEvent = this.#rawTraceEvents.at(rawEventIndex);
|
|
74
|
+
const syntheticEvent = rawEvent && this.#syntheticEventByRawEvent.get(rawEvent);
|
|
98
75
|
if (!syntheticEvent) {
|
|
99
76
|
throw new Error(`Attempted to get a synthetic event from an unknown raw event index: ${rawEventIndex}`);
|
|
100
77
|
}
|
|
101
78
|
return syntheticEvent;
|
|
102
79
|
}
|
|
103
80
|
|
|
104
|
-
getSyntheticTraces(): Types.Events.SyntheticBased[] {
|
|
105
|
-
return this.#syntheticTraces;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
81
|
getRawTraceEvents(): readonly Types.Events.Event[] {
|
|
109
82
|
return this.#rawTraceEvents;
|
|
110
83
|
}
|
|
@@ -82,18 +82,20 @@ function getNodesAndTimingByRequestId(nodeTimings: Lantern.Simulation.Result['no
|
|
|
82
82
|
|
|
83
83
|
let duration = nodeTiming.duration;
|
|
84
84
|
let startTime = nodeTiming.startTime;
|
|
85
|
+
let queuedTime = nodeTiming.queuedTime;
|
|
85
86
|
for (const redirect of node.request.redirects ?? []) {
|
|
86
87
|
const redirectTiming = timingByNodeId.get(redirect.requestId);
|
|
87
88
|
if (redirectTiming) {
|
|
88
89
|
duration += redirectTiming.duration;
|
|
89
90
|
startTime = Math.min(startTime, redirectTiming.startTime);
|
|
91
|
+
queuedTime = Math.min(queuedTime, redirectTiming.queuedTime);
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
const backendRequestId = node.request.rawRequest?.args.data.requestId ?? node.request.requestId;
|
|
94
96
|
requestIdToNode.set(backendRequestId, {
|
|
95
97
|
node,
|
|
96
|
-
nodeTiming: {startTime, endTime: nodeTiming.endTime, duration},
|
|
98
|
+
nodeTiming: {startTime, endTime: nodeTiming.endTime, queuedTime, duration},
|
|
97
99
|
});
|
|
98
100
|
}
|
|
99
101
|
|
|
@@ -262,6 +262,9 @@ class Simulator<T = Lantern.AnyNetworkObject> {
|
|
|
262
262
|
if (!connection) {
|
|
263
263
|
return;
|
|
264
264
|
}
|
|
265
|
+
if (this.nodeTimings.getQueued(node).queuedTime === totalElapsedTime) {
|
|
266
|
+
connection.setH2OverflowBytesDownloaded(0);
|
|
267
|
+
}
|
|
265
268
|
}
|
|
266
269
|
|
|
267
270
|
this.markNodeAsInProgress(node, totalElapsedTime);
|
|
@@ -430,6 +433,7 @@ class Simulator<T = Lantern.AnyNetworkObject> {
|
|
|
430
433
|
{
|
|
431
434
|
startTime: timing.startTime,
|
|
432
435
|
endTime: timing.endTime,
|
|
436
|
+
queuedTime: timing.queuedTime,
|
|
433
437
|
duration: timing.endTime - timing.startTime,
|
|
434
438
|
},
|
|
435
439
|
];
|
|
@@ -72,7 +72,7 @@ const DEFAULT_VIEW: View = (input, _output, target) => {
|
|
|
72
72
|
}
|
|
73
73
|
<devtools-checkbox class="source-order-checkbox"
|
|
74
74
|
jslog=${VisualLogging.toggle().track({click: true})}
|
|
75
|
-
|
|
75
|
+
.checked=${Boolean(input.showSourceOrder)}
|
|
76
76
|
@change=${onShowSourceOrderChanged}>
|
|
77
77
|
${i18nString(UIStrings.showSourceOrder)}
|
|
78
78
|
</devtools-checkbox>
|
|
@@ -81,6 +81,7 @@ import {ServiceWorkerCacheTreeElement} from './ServiceWorkerCacheTreeElement.js'
|
|
|
81
81
|
import {ServiceWorkersView} from './ServiceWorkersView.js';
|
|
82
82
|
import {StorageBucketsTreeParentElement} from './StorageBucketsTreeElement.js';
|
|
83
83
|
import {StorageView} from './StorageView.js';
|
|
84
|
+
import {type SubAppEntry, SubAppsView} from './SubAppsView.js';
|
|
84
85
|
import {TrustTokensTreeElement} from './TrustTokensTreeElement.js';
|
|
85
86
|
import {WebMCPTreeElement} from './WebMCPTreeElement.js';
|
|
86
87
|
|
|
@@ -97,6 +98,14 @@ const UIStrings = {
|
|
|
97
98
|
* @description Context menu item to explain cookies with AI in the Application panel sidebar.
|
|
98
99
|
*/
|
|
99
100
|
explainCookies: 'Explain cookies',
|
|
101
|
+
/**
|
|
102
|
+
* @description Tree item label for Sub Apps in the sidebar of the Application panel.
|
|
103
|
+
*/
|
|
104
|
+
subApps: 'Sub apps',
|
|
105
|
+
/**
|
|
106
|
+
* @description Tree item label for Sibling Sub Apps in the sidebar of the Application panel.
|
|
107
|
+
*/
|
|
108
|
+
siblingSubApps: 'Sibling sub apps',
|
|
100
109
|
/**
|
|
101
110
|
* @description Section header for the application section in the sidebar of the Application panel.
|
|
102
111
|
*/
|
|
@@ -335,6 +344,7 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
|
335
344
|
pushMessagingTreeElement: BackgroundServiceTreeElement;
|
|
336
345
|
reportingApiTreeElement: ReportingApiTreeElement;
|
|
337
346
|
webMcpTreeElement?: WebMCPTreeElement;
|
|
347
|
+
subAppsTreeElement: SubAppsTreeElement;
|
|
338
348
|
adsTreeElement?: ApplicationPanelTreeElement;
|
|
339
349
|
storageTreeElement?: StorageTreeElement;
|
|
340
350
|
deviceBoundSessionsRootTreeElement: DeviceBoundSessionsRootTreeElement|undefined;
|
|
@@ -374,6 +384,8 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
|
374
384
|
const manifestTreeElement = new AppManifestTreeElement(panel);
|
|
375
385
|
this.applicationTreeElement.appendChild(manifestTreeElement);
|
|
376
386
|
manifestTreeElement.generateChildren();
|
|
387
|
+
this.subAppsTreeElement = new SubAppsTreeElement(panel);
|
|
388
|
+
this.applicationTreeElement.appendChild(this.subAppsTreeElement);
|
|
377
389
|
this.serviceWorkersTreeElement = new ServiceWorkersTreeElement(panel);
|
|
378
390
|
this.applicationTreeElement.appendChild(this.serviceWorkersTreeElement);
|
|
379
391
|
this.storageTreeElement = new StorageTreeElement(panel);
|
|
@@ -623,6 +635,11 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
|
623
635
|
if (preloadingModel) {
|
|
624
636
|
this.preloadingSummaryTreeElement?.initialize(preloadingModel);
|
|
625
637
|
}
|
|
638
|
+
|
|
639
|
+
const resourceTreeModel = this.target?.model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
640
|
+
if (resourceTreeModel) {
|
|
641
|
+
void this.subAppsTreeElement.update(resourceTreeModel);
|
|
642
|
+
}
|
|
626
643
|
}
|
|
627
644
|
|
|
628
645
|
private domStorageModelAdded(model: SDK.DOMStorageModel.DOMStorageModel): void {
|
|
@@ -721,6 +738,7 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
|
721
738
|
|
|
722
739
|
if (frame.isOutermostFrame()) {
|
|
723
740
|
this.reset();
|
|
741
|
+
void this.subAppsTreeElement.update(frame.resourceTreeModel());
|
|
724
742
|
const selectedElement = this.sidebarTree.selectedTreeElement;
|
|
725
743
|
if (selectedElement instanceof ExpandableApplicationPanelTreeElement) {
|
|
726
744
|
// Only update AI Assistance storage context for expandable category headers,
|
|
@@ -1146,6 +1164,78 @@ export class AppManifestTreeElement extends ApplicationPanelTreeElement {
|
|
|
1146
1164
|
}
|
|
1147
1165
|
}
|
|
1148
1166
|
|
|
1167
|
+
export class SubAppsTreeElement extends ApplicationPanelTreeElement {
|
|
1168
|
+
#view?: SubAppsView;
|
|
1169
|
+
#subApps: SubAppEntry[] = [];
|
|
1170
|
+
#isSubApp = false;
|
|
1171
|
+
#latestUpdate?: Promise<void>;
|
|
1172
|
+
|
|
1173
|
+
constructor(storagePanel: ResourcesPanel) {
|
|
1174
|
+
super(storagePanel, i18nString(UIStrings.subApps), false, 'sub-apps');
|
|
1175
|
+
const icon = createIcon('table');
|
|
1176
|
+
this.setLeadingIcons([icon]);
|
|
1177
|
+
this.hidden = true;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
override get itemURL(): Platform.DevToolsPath.UrlString {
|
|
1181
|
+
return 'sub-apps://' as Platform.DevToolsPath.UrlString;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
override onselect(selectedByUser?: boolean): boolean {
|
|
1185
|
+
super.onselect(selectedByUser);
|
|
1186
|
+
if (!this.#view) {
|
|
1187
|
+
this.#view = new SubAppsView();
|
|
1188
|
+
}
|
|
1189
|
+
this.#view.subApps = this.#subApps;
|
|
1190
|
+
this.#view.isSiblingSubApps = this.#isSubApp;
|
|
1191
|
+
this.showView(this.#view);
|
|
1192
|
+
UI.UIUserMetrics.UIUserMetrics.instance().panelShown('sub-apps');
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Fetches the Sub Apps of the inspected app, or its Sibling Sub Apps when
|
|
1198
|
+
* the inspected app is itself a Sub App. Only the most recent update is
|
|
1199
|
+
* applied; results of superseded updates are dropped.
|
|
1200
|
+
*/
|
|
1201
|
+
update(resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel): Promise<void> {
|
|
1202
|
+
const subAppsFetch = Promise.all([resourceTreeModel.getSubApps(), resourceTreeModel.getSiblingSubApps()]);
|
|
1203
|
+
const update: Promise<void> = subAppsFetch.then(([subApps, siblingSubApps]) => {
|
|
1204
|
+
if (update !== this.#latestUpdate) {
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
if (siblingSubApps.length > 0) {
|
|
1208
|
+
this.#setSubApps(siblingSubApps, /* isSubApp= */ true);
|
|
1209
|
+
} else {
|
|
1210
|
+
this.#setSubApps(subApps, /* isSubApp= */ false);
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
this.#latestUpdate = update;
|
|
1214
|
+
return update;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
get latestUpdateForTest(): Promise<void>|undefined {
|
|
1218
|
+
return this.#latestUpdate;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
#setSubApps(subApps: SubAppEntry[], isSubApp: boolean): void {
|
|
1222
|
+
this.#subApps = subApps;
|
|
1223
|
+
this.#isSubApp = isSubApp;
|
|
1224
|
+
this.hidden = subApps.length === 0;
|
|
1225
|
+
const title = isSubApp ? i18nString(UIStrings.siblingSubApps) : i18nString(UIStrings.subApps);
|
|
1226
|
+
this.title = title;
|
|
1227
|
+
UI.ARIAUtils.setLabel(this.listItemElement, title);
|
|
1228
|
+
if (this.#view) {
|
|
1229
|
+
this.#view.subApps = this.#subApps;
|
|
1230
|
+
this.#view.isSiblingSubApps = this.#isSubApp;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
getSubApps(): SubAppEntry[] {
|
|
1235
|
+
return this.#subApps;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1149
1239
|
export class StorageTreeElement extends ApplicationPanelTreeElement {
|
|
1150
1240
|
private view?: StorageView;
|
|
1151
1241
|
constructor(storagePanel: ResourcesPanel) {
|