chrome-devtools-frontend 1.0.1535712 → 1.0.1536371

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 (77) hide show
  1. package/docs/contributing/images/issues-nearestslo.png +0 -0
  2. package/docs/contributing/issues.md +17 -21
  3. package/front_end/core/common/Console.ts +1 -8
  4. package/front_end/core/common/ParsedURL.ts +10 -20
  5. package/front_end/core/common/SegmentedRange.ts +1 -2
  6. package/front_end/core/common/StringOutputStream.ts +1 -4
  7. package/front_end/core/i18n/i18nImpl.ts +0 -24
  8. package/front_end/core/sdk/AnimationModel.ts +1 -2
  9. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -2
  10. package/front_end/core/sdk/CSSModel.ts +1 -1
  11. package/front_end/core/sdk/CSSProperty.ts +3 -6
  12. package/front_end/core/sdk/CSSStyleDeclaration.ts +4 -4
  13. package/front_end/core/sdk/DebuggerModel.ts +1 -2
  14. package/front_end/core/sdk/EnhancedTracesParser.ts +4 -0
  15. package/front_end/core/sdk/SourceMap.ts +2 -3
  16. package/front_end/entrypoints/node_app/NodeConnectionsPanel.ts +2 -1
  17. package/front_end/generated/InspectorBackendCommands.js +1 -2
  18. package/front_end/generated/SupportedCSSProperties.js +19 -0
  19. package/front_end/generated/protocol.ts +0 -27
  20. package/front_end/panels/accessibility/AccessibilityNodeView.ts +18 -17
  21. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +9 -12
  22. package/front_end/panels/ai_assistance/components/ChatView.ts +5 -4
  23. package/front_end/panels/application/AppManifestView.ts +7 -6
  24. package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -4
  25. package/front_end/panels/application/OpenedWindowDetailsView.ts +6 -6
  26. package/front_end/panels/application/StorageView.ts +9 -8
  27. package/front_end/panels/application/components/BackForwardCacheView.ts +333 -314
  28. package/front_end/panels/application/components/ProtocolHandlersView.ts +3 -2
  29. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +2 -1
  30. package/front_end/panels/browser_debugger/ObjectEventListenersSidebarPane.ts +8 -8
  31. package/front_end/panels/common/BadgeNotification.ts +2 -1
  32. package/front_end/panels/common/GdpSignUpDialog.ts +2 -1
  33. package/front_end/panels/console/ConsoleInsightTeaser.ts +8 -2
  34. package/front_end/panels/console/ConsolePinPane.ts +12 -7
  35. package/front_end/panels/developer_resources/DeveloperResourcesView.ts +9 -9
  36. package/front_end/panels/elements/ComputedStyleWidget.ts +7 -7
  37. package/front_end/panels/elements/EventListenersWidget.ts +9 -9
  38. package/front_end/panels/elements/NodeStackTraceWidget.ts +6 -6
  39. package/front_end/panels/elements/PlatformFontsWidget.ts +5 -5
  40. package/front_end/panels/elements/PropertiesWidget.ts +8 -8
  41. package/front_end/panels/layer_viewer/Layers3DView.ts +2 -1
  42. package/front_end/panels/layer_viewer/PaintProfilerView.ts +3 -3
  43. package/front_end/panels/network/RequestCookiesView.ts +2 -1
  44. package/front_end/panels/network/RequestTimingView.ts +2 -1
  45. package/front_end/panels/recorder/RecorderController.ts +34 -23
  46. package/front_end/panels/recorder/components/CreateRecordingView.ts +249 -240
  47. package/front_end/panels/security/CookieControlsView.ts +2 -1
  48. package/front_end/panels/security/CookieReportView.ts +3 -2
  49. package/front_end/panels/settings/AISettingsTab.ts +2 -1
  50. package/front_end/panels/settings/KeybindsSettingsTab.ts +6 -0
  51. package/front_end/panels/settings/components/SyncSection.ts +2 -1
  52. package/front_end/panels/sources/DebuggerPausedMessage.ts +4 -3
  53. package/front_end/panels/sources/ResourceOriginPlugin.ts +3 -2
  54. package/front_end/panels/sources/SourcesNavigator.ts +2 -1
  55. package/front_end/panels/sources/TabbedEditorContainer.ts +3 -2
  56. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +9 -9
  57. package/front_end/panels/timeline/TimelineUIUtils.ts +3 -2
  58. package/front_end/panels/timeline/components/DetailsView.ts +5 -4
  59. package/front_end/panels/timeline/components/FieldSettingsDialog.ts +2 -1
  60. package/front_end/panels/timeline/components/LiveMetricsView.ts +5 -4
  61. package/front_end/panels/timeline/components/MetricCompareStrings.ts +25 -24
  62. package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +2 -1
  63. package/front_end/third_party/chromium/README.chromium +1 -1
  64. package/front_end/ui/components/docs/tooltip/basic.ts +1 -1
  65. package/front_end/ui/components/tooltips/Tooltip.ts +32 -17
  66. package/front_end/ui/i18n/i18n.ts +31 -0
  67. package/front_end/ui/legacy/SoftDropDown.ts +1 -12
  68. package/front_end/ui/legacy/ViewManager.ts +2 -4
  69. package/front_end/ui/legacy/Widget.ts +33 -17
  70. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +2 -1
  71. package/front_end/ui/legacy/legacy.ts +0 -2
  72. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  73. package/mcp/mcp.ts +1 -0
  74. package/package.json +1 -1
  75. package/front_end/ui/components/docs/recorder_create_recording_view/basic.html +0 -20
  76. package/front_end/ui/components/docs/recorder_create_recording_view/basic.ts +0 -27
  77. package/front_end/ui/legacy/ThrottledWidget.ts +0 -48
@@ -154,10 +154,322 @@ const enum ScreenStatusType {
154
154
  RESULT = 'Result',
155
155
  }
156
156
 
157
+ function renderMainFrameInformation(
158
+ frame: SDK.ResourceTreeModel.ResourceTreeFrame|null,
159
+ frameTreeData: {node: FrameTreeNodeData, frameCount: number, issueCount: number}|undefined,
160
+ reasonToFramesMap: Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>, screenStatus: ScreenStatusType,
161
+ navigateAwayAndBack: () => Promise<void>): Lit.TemplateResult {
162
+ if (!frame) {
163
+ // clang-format of
164
+ return html`
165
+ <devtools-report-key>
166
+ ${i18nString(UIStrings.mainFrame)}
167
+ </devtools-report-key>
168
+ <devtools-report-value>
169
+ ${i18nString(UIStrings.unavailable)}
170
+ </devtools-report-value>`;
171
+ // clang-format on
172
+ }
173
+ const isTestRunning = (screenStatus === ScreenStatusType.RUNNING);
174
+ // Prevent running BFCache test on the DevTools window itself via DevTools on DevTools
175
+ const isTestingForbidden = Common.ParsedURL.schemeIs(frame.url, 'devtools:');
176
+ // clang-format off
177
+ return html`
178
+ ${renderBackForwardCacheStatus(frame.backForwardCacheDetails.restoredFromCache)}
179
+ <devtools-report-key>${i18nString(UIStrings.url)}</devtools-report-key>
180
+ <devtools-report-value>${frame.url}</devtools-report-value>
181
+ ${maybeRenderFrameTree(frameTreeData)}
182
+ <devtools-report-section>
183
+ <devtools-button
184
+ aria-label=${i18nString(UIStrings.runTest)}
185
+ .disabled=${isTestRunning || isTestingForbidden}
186
+ .spinner=${isTestRunning}
187
+ .variant=${Buttons.Button.Variant.PRIMARY}
188
+ @click=${navigateAwayAndBack}
189
+ jslog=${VisualLogging.action('back-forward-cache.run-test').track({click: true})}>
190
+ ${isTestRunning ? html`
191
+ ${i18nString(UIStrings.runningTest)}`:`
192
+ ${i18nString(UIStrings.runTest)}
193
+ `}
194
+ </devtools-button>
195
+ </devtools-report-section>
196
+ <devtools-report-divider>
197
+ </devtools-report-divider>
198
+ ${maybeRenderExplanations(frame.backForwardCacheDetails.explanations,
199
+ frame.backForwardCacheDetails.explanationsTree,
200
+ reasonToFramesMap)}
201
+ <devtools-report-section>
202
+ <x-link href="https://web.dev/bfcache/" class="link"
203
+ jslog=${VisualLogging.action('learn-more.eligibility').track({click: true})}>
204
+ ${i18nString(UIStrings.learnMore)}
205
+ </x-link>
206
+ </devtools-report-section>`;
207
+ // clang-format on
208
+ }
209
+
210
+ function maybeRenderFrameTree(
211
+ frameTreeData: {node: FrameTreeNodeData, frameCount: number, issueCount: number}|undefined): Lit.LitTemplate {
212
+ if (!frameTreeData || (frameTreeData.frameCount === 0 && frameTreeData.issueCount === 0)) {
213
+ return Lit.nothing;
214
+ }
215
+
216
+ function treeNodeRenderer(node: TreeOutline.TreeOutlineUtils.TreeNode<FrameTreeNodeData>): Lit.TemplateResult {
217
+ // clang-format off
218
+ return html`
219
+ <div class="text-ellipsis">
220
+ ${node.treeNodeData.iconName ? html`
221
+ <devtools-icon class="inline-icon extra-large" .name=${node.treeNodeData.iconName} style="margin-bottom: -3px;">
222
+ </devtools-icon>
223
+ ` : Lit.nothing}
224
+ ${node.treeNodeData.text}
225
+ </div>`;
226
+ // clang-format on
227
+ }
228
+
229
+ const frameTreeNode = buildFrameTree(frameTreeData.node);
230
+ // Override the icon for the outermost frame.
231
+ frameTreeNode.treeNodeData.iconName = 'frame';
232
+ let title = '';
233
+ // The translation pipeline does not support nested plurals. We avoid this
234
+ // here by pulling out the logic for one of the plurals into code instead.
235
+ if (frameTreeData.frameCount === 1) {
236
+ title = i18nString(UIStrings.issuesInSingleFrame, {n: frameTreeData.issueCount});
237
+ } else {
238
+ title = i18nString(UIStrings.issuesInMultipleFrames, {n: frameTreeData.issueCount, m: frameTreeData.frameCount});
239
+ }
240
+ const root: TreeOutline.TreeOutlineUtils.TreeNode<FrameTreeNodeData> = {
241
+ treeNodeData: {
242
+ text: title,
243
+ },
244
+ id: 'root',
245
+ children: () => Promise.resolve([frameTreeNode]),
246
+ };
247
+
248
+ // clang-format off
249
+ return html`
250
+ <devtools-report-key jslog=${VisualLogging.section('frames')}>${i18nString(UIStrings.framesTitle)}</devtools-report-key>
251
+ <devtools-report-value>
252
+ <devtools-tree-outline .data=${{
253
+ tree: [root],
254
+ defaultRenderer: treeNodeRenderer,
255
+ compact: true,
256
+ } as TreeOutline.TreeOutline.TreeOutlineData<FrameTreeNodeData>}>
257
+ </devtools-tree-outline>
258
+ </devtools-report-value>`;
259
+ // clang-format on
260
+ }
261
+
262
+ let nextNodeId = 0;
263
+
264
+ function buildFrameTree(data: FrameTreeNodeData): TreeOutline.TreeOutlineUtils.TreeNode<FrameTreeNodeData> {
265
+ const children = data.children;
266
+ const node = {
267
+ treeNodeData: {
268
+ text: data.text,
269
+ ...(data.iconName ? {iconName: data.iconName} : {}),
270
+ },
271
+ ...(children?.length ? {children: () => Promise.resolve(children.map(child => buildFrameTree(child)))} : {}),
272
+ id: String(nextNodeId++),
273
+ };
274
+ return node;
275
+ }
276
+
277
+ function renderBackForwardCacheStatus(status: boolean|undefined): Lit.TemplateResult {
278
+ switch (status) {
279
+ case true:
280
+ // clang-format off
281
+ return html`
282
+ <devtools-report-section>
283
+ <div class="status extra-large">
284
+ <devtools-icon class="inline-icon extra-large" name="check-circle" style="color: var(--icon-checkmark-green);">
285
+ </devtools-icon>
286
+ </div>
287
+ ${i18nString(UIStrings.restoredFromBFCache)}
288
+ </devtools-report-section>`;
289
+ // clang-format on
290
+ case false:
291
+ // clang-format off
292
+ return html`
293
+ <devtools-report-section>
294
+ <div class="status">
295
+ <devtools-icon class="inline-icon extra-large" name="clear">
296
+ </devtools-icon>
297
+ </div>
298
+ ${i18nString(UIStrings.normalNavigation)}
299
+ </devtools-report-section>`;
300
+ // clang-format on
301
+ }
302
+ // clang-format off
303
+ return html`
304
+ <devtools-report-section>
305
+ ${i18nString(UIStrings.unknown)}
306
+ </devtools-report-section>`;
307
+ // clang-format on
308
+ }
309
+
310
+ function maybeRenderExplanations(
311
+ explanations: Protocol.Page.BackForwardCacheNotRestoredExplanation[],
312
+ explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree|undefined,
313
+ reasonToFramesMap: Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>): Lit.LitTemplate {
314
+ if (explanations.length === 0) {
315
+ return Lit.nothing;
316
+ }
317
+
318
+ const pageSupportNeeded = explanations.filter(
319
+ explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.PageSupportNeeded);
320
+ const supportPending = explanations.filter(
321
+ explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.SupportPending);
322
+ const circumstantial = explanations.filter(
323
+ explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.Circumstantial);
324
+
325
+ // Disabled until https://crbug.com/1079231 is fixed.
326
+ // clang-format off
327
+ return html`
328
+ ${renderExplanations(i18nString(UIStrings.pageSupportNeeded), i18nString(UIStrings.pageSupportNeededExplanation), pageSupportNeeded, reasonToFramesMap)}
329
+ ${renderExplanations(i18nString(UIStrings.supportPending), i18nString(UIStrings.supportPendingExplanation), supportPending, reasonToFramesMap)}
330
+ ${renderExplanations(i18nString(UIStrings.circumstantial), i18nString(UIStrings.circumstantialExplanation), circumstantial, reasonToFramesMap)}`;
331
+ // clang-format on
332
+ }
333
+
334
+ function renderExplanations(
335
+ category: Platform.UIString.LocalizedString, explainerText: Platform.UIString.LocalizedString,
336
+ explanations: Protocol.Page.BackForwardCacheNotRestoredExplanation[],
337
+ reasonToFramesMap: Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>): Lit.TemplateResult {
338
+ // Disabled until https://crbug.com/1079231 is fixed.
339
+ // clang-format off
340
+ return html`
341
+ ${explanations.length > 0 ? html`
342
+ <devtools-report-section-header>
343
+ ${category}
344
+ <div class="help-outline-icon">
345
+ <devtools-icon class="inline-icon medium" name="help" title=${explainerText}>
346
+ </devtools-icon>
347
+ </div>
348
+ </devtools-report-section-header>
349
+ ${explanations.map(explanation => renderReason(explanation, reasonToFramesMap.get(explanation.reason)))}
350
+ ` : Lit.nothing}`;
351
+ // clang-format on
352
+ }
353
+
354
+ function maybeRenderReasonContext(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation): Lit.LitTemplate {
355
+ if (explanation.reason ===
356
+ Protocol.Page.BackForwardCacheNotRestoredReason.EmbedderExtensionSentMessageToCachedFrame &&
357
+ explanation.context) {
358
+ const link = 'chrome://extensions/?id=' + explanation.context as Platform.DevToolsPath.UrlString;
359
+ // clang-format off
360
+ return html`${i18nString(UIStrings.blockingExtensionId)}
361
+ <devtools-chrome-link .href=${link}>${explanation.context}</devtools-chrome-link>`;
362
+ // clang-format on
363
+ }
364
+ return Lit.nothing;
365
+ }
366
+
367
+ function renderFramesPerReason(frames: string[]|undefined): Lit.LitTemplate {
368
+ if (frames === undefined || frames.length === 0) {
369
+ return Lit.nothing;
370
+ }
371
+ const rows = [html`<div>${i18nString(UIStrings.framesPerIssue, {n: frames.length})}</div>`];
372
+ rows.push(...frames.map(url => html`<div class="text-ellipsis" title=${url}
373
+ jslog=${VisualLogging.treeItem()}>${url}</div>`));
374
+ return html`
375
+ <div class="details-list"
376
+ jslog=${VisualLogging.tree('frames-per-issue')}>
377
+ <devtools-expandable-list .data=${{
378
+ rows,
379
+ title: i18nString(UIStrings.framesPerIssue, {n: frames.length}),
380
+ } as ExpandableList.ExpandableList.ExpandableListData}
381
+ jslog=${VisualLogging.treeItem()}></devtools-expandable-list>
382
+ </div>
383
+ `;
384
+ }
385
+
386
+ function maybeRenderDeepLinkToUnload(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation):
387
+ Lit.LitTemplate {
388
+ if (explanation.reason === Protocol.Page.BackForwardCacheNotRestoredReason.UnloadHandlerExistsInMainFrame ||
389
+ explanation.reason === Protocol.Page.BackForwardCacheNotRestoredReason.UnloadHandlerExistsInSubFrame) {
390
+ return html`
391
+ <x-link href="https://web.dev/bfcache/#never-use-the-unload-event" class="link"
392
+ jslog=${VisualLogging.action('learn-more.never-use-unload').track({
393
+ click: true,
394
+ })}>
395
+ ${i18nString(UIStrings.neverUseUnload)}
396
+ </x-link>`;
397
+ }
398
+ return Lit.nothing;
399
+ }
400
+
401
+ function maybeRenderJavaScriptDetails(details: Protocol.Page.BackForwardCacheBlockingDetails[]|undefined):
402
+ Lit.LitTemplate {
403
+ if (details === undefined || details.length === 0) {
404
+ return Lit.nothing;
405
+ }
406
+ const maxLengthForDisplayedURLs = 50;
407
+ const linkifier = new Components.Linkifier.Linkifier(maxLengthForDisplayedURLs);
408
+ const rows = [html`<div>${i18nString(UIStrings.filesPerIssue, {n: details.length})}</div>`];
409
+ rows.push(...details.map(
410
+ detail => html`${
411
+ linkifier.linkifyScriptLocation(
412
+ null, null, detail.url as Platform.DevToolsPath.UrlString, detail.lineNumber, {
413
+ columnNumber: detail.columnNumber,
414
+ showColumnNumber: true,
415
+ inlineFrameIndex: 0,
416
+ })}`));
417
+ return html`
418
+ <div class="details-list">
419
+ <devtools-expandable-list .data=${
420
+ {rows} as ExpandableList.ExpandableList.ExpandableListData}></devtools-expandable-list>
421
+ </div>
422
+ `;
423
+ }
424
+
425
+ function renderReason(
426
+ explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation, frames: string[]|undefined): Lit.TemplateResult {
427
+ // clang-format off
428
+ return html`
429
+ <devtools-report-section>
430
+ ${(explanation.reason in NotRestoredReasonDescription) ?
431
+ html`
432
+ <div class="circled-exclamation-icon">
433
+ <devtools-icon class="inline-icon medium" style="color: var(--icon-warning)" name="warning">
434
+ </devtools-icon>
435
+ </div>
436
+ <div>
437
+ ${NotRestoredReasonDescription[explanation.reason].name()}
438
+ ${maybeRenderDeepLinkToUnload(explanation)}
439
+ ${maybeRenderReasonContext(explanation)}
440
+ </div>` :
441
+ Lit.nothing}
442
+ </devtools-report-section>
443
+ <div class="gray-text">
444
+ ${explanation.reason}
445
+ </div>
446
+ ${maybeRenderJavaScriptDetails(explanation.details)}
447
+ ${renderFramesPerReason(frames)}`;
448
+ // clang-format on
449
+ }
450
+
451
+ function renderBackForwardCacheView(
452
+ frame: SDK.ResourceTreeModel.ResourceTreeFrame|null,
453
+ frameTreeData: {node: FrameTreeNodeData, frameCount: number, issueCount: number}|undefined,
454
+ reasonToFramesMap: Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>, screenStatus: ScreenStatusType,
455
+ navigateAwayAndBack: () => Promise<void>, target: ShadowRoot): void {
456
+ // Disabled until https://crbug.com/1079231 is fixed.
457
+ // clang-format off
458
+ Lit.render(html`
459
+ <style>${backForwardCacheViewStyles}</style>
460
+ <devtools-report .data=${
461
+ {reportTitle: i18nString(UIStrings.backForwardCacheTitle)} as ReportView.ReportView.ReportData
462
+ } jslog=${VisualLogging.pane('back-forward-cache')}>
463
+
464
+ ${renderMainFrameInformation(frame, frameTreeData, reasonToFramesMap, screenStatus, navigateAwayAndBack)}
465
+ </devtools-report>
466
+ `, target);
467
+ // clang-format on
468
+ }
469
+
157
470
  export class BackForwardCacheView extends LegacyWrapper.LegacyWrapper.WrappableComponent {
158
471
  readonly #shadow = this.attachShadow({mode: 'open'});
159
472
  #screenStatus = ScreenStatusType.RESULT;
160
- #nextNodeId = 0;
161
473
  #historyIndex = 0;
162
474
 
163
475
  constructor() {
@@ -182,18 +494,14 @@ export class BackForwardCacheView extends LegacyWrapper.LegacyWrapper.WrappableC
182
494
 
183
495
  override async render(): Promise<void> {
184
496
  await RenderCoordinator.write('BackForwardCacheView render', () => {
185
- // Disabled until https://crbug.com/1079231 is fixed.
186
- // clang-format off
187
- Lit.render(html`
188
- <style>${backForwardCacheViewStyles}</style>
189
- <devtools-report .data=${
190
- {reportTitle: i18nString(UIStrings.backForwardCacheTitle)} as ReportView.ReportView.ReportData
191
- } jslog=${VisualLogging.pane('back-forward-cache')}>
192
-
193
- ${this.#renderMainFrameInformation()}
194
- </devtools-report>
195
- `, this.#shadow, {host: this});
196
- // clang-format on
497
+ const reasonToFramesMap = new Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>();
498
+ const explanationTree = this.#getMainFrame()?.backForwardCacheDetails?.explanationsTree;
499
+ if (explanationTree) {
500
+ this.#buildReasonToFramesMap(explanationTree, {blankCount: 1}, reasonToFramesMap);
501
+ }
502
+ renderBackForwardCacheView(
503
+ this.#getMainFrame(), this.#buildFrameTreeDataRecursive(explanationTree, {blankCount: 1}), reasonToFramesMap,
504
+ this.#screenStatus, this.#navigateAwayAndBack.bind(this), this.#shadow);
197
505
  });
198
506
  }
199
507
 
@@ -258,120 +566,17 @@ export class BackForwardCacheView extends LegacyWrapper.LegacyWrapper.WrappableC
258
566
  void resourceTreeModel.navigate('chrome://terms' as Platform.DevToolsPath.UrlString);
259
567
  }
260
568
 
261
- #renderMainFrameInformation(): Lit.TemplateResult {
262
- const frame = this.#getMainFrame();
263
- if (!frame) {
264
- // clang-format off
265
- return html`
266
- <devtools-report-key>
267
- ${i18nString(UIStrings.mainFrame)}
268
- </devtools-report-key>
269
- <devtools-report-value>
270
- ${i18nString(UIStrings.unavailable)}
271
- </devtools-report-value>
272
- `;
273
- // clang-format on
274
- }
275
- const isTestRunning = (this.#screenStatus === ScreenStatusType.RUNNING);
276
- // Prevent running BFCache test on the DevTools window itself via DevTools on DevTools
277
- const isTestingForbidden = Common.ParsedURL.schemeIs(frame.url, 'devtools:');
278
- // clang-format off
279
- return html`
280
- ${this.#renderBackForwardCacheStatus(frame.backForwardCacheDetails.restoredFromCache)}
281
- <devtools-report-key>${i18nString(UIStrings.url)}</devtools-report-key>
282
- <devtools-report-value>${frame.url}</devtools-report-value>
283
- ${this.#maybeRenderFrameTree(frame.backForwardCacheDetails.explanationsTree)}
284
- <devtools-report-section>
285
- <devtools-button
286
- aria-label=${i18nString(UIStrings.runTest)}
287
- .disabled=${isTestRunning || isTestingForbidden}
288
- .spinner=${isTestRunning}
289
- .variant=${Buttons.Button.Variant.PRIMARY}
290
- @click=${this.#navigateAwayAndBack}
291
- jslog=${VisualLogging.action('back-forward-cache.run-test').track({click: true})}>
292
- ${isTestRunning ? html`
293
- ${i18nString(UIStrings.runningTest)}`:`
294
- ${i18nString(UIStrings.runTest)}
295
- `}
296
- </devtools-button>
297
- </devtools-report-section>
298
- <devtools-report-divider>
299
- </devtools-report-divider>
300
- ${this.#maybeRenderExplanations(frame.backForwardCacheDetails.explanations,
301
- frame.backForwardCacheDetails.explanationsTree)}
302
- <devtools-report-section>
303
- <x-link href="https://web.dev/bfcache/" class="link"
304
- jslog=${VisualLogging.action('learn-more.eligibility').track({click: true})}>
305
- ${i18nString(UIStrings.learnMore)}
306
- </x-link>
307
- </devtools-report-section>
308
- `;
309
- // clang-format on
310
- }
311
-
312
- #maybeRenderFrameTree(explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree|undefined):
313
- Lit.LitTemplate {
314
- if (!explanationTree || (explanationTree.explanations.length === 0 && explanationTree.children.length === 0)) {
315
- return Lit.nothing;
316
- }
317
-
318
- function treeNodeRenderer(node: TreeOutline.TreeOutlineUtils.TreeNode<FrameTreeNodeData>): Lit.TemplateResult {
319
- // clang-format off
320
- return html`
321
- <div class="text-ellipsis">
322
- ${node.treeNodeData.iconName ? html`
323
- <devtools-icon class="inline-icon extra-large" .name=${node.treeNodeData.iconName} style="margin-bottom: -3px;">
324
- </devtools-icon>
325
- ` : Lit.nothing}
326
- ${node.treeNodeData.text}
327
- </div>
328
- `;
329
- // clang-format on
330
- }
331
-
332
- const frameTreeData = this.#buildFrameTreeDataRecursive(explanationTree, {blankCount: 1});
333
- // Override the icon for the outermost frame.
334
- frameTreeData.node.treeNodeData.iconName = 'frame';
335
- let title = '';
336
- // The translation pipeline does not support nested plurals. We avoid this
337
- // here by pulling out the logic for one of the plurals into code instead.
338
- if (frameTreeData.frameCount === 1) {
339
- title = i18nString(UIStrings.issuesInSingleFrame, {n: frameTreeData.issueCount});
340
- } else {
341
- title = i18nString(UIStrings.issuesInMultipleFrames, {n: frameTreeData.issueCount, m: frameTreeData.frameCount});
342
- }
343
- const root: TreeOutline.TreeOutlineUtils.TreeNode<FrameTreeNodeData> = {
344
- treeNodeData: {
345
- text: title,
346
- },
347
- id: 'root',
348
- children: () => Promise.resolve([frameTreeData.node]),
349
- };
350
-
351
- // clang-format off
352
- return html`
353
- <devtools-report-key jslog=${VisualLogging.section('frames')}>${i18nString(UIStrings.framesTitle)}</devtools-report-key>
354
- <devtools-report-value>
355
- <devtools-tree-outline .data=${{
356
- tree: [root],
357
- defaultRenderer: treeNodeRenderer,
358
- compact: true,
359
- } as TreeOutline.TreeOutline.TreeOutlineData<FrameTreeNodeData>}>
360
- </devtools-tree-outline>
361
- </devtools-report-value>
362
- `;
363
- // clang-format on
364
- }
365
-
366
569
  // Builds a subtree of the frame tree, conaining only frames with BFCache issues and their ancestors.
367
570
  // Returns the root node, the number of frames in the subtree, and the number of issues in the subtree.
368
571
  #buildFrameTreeDataRecursive(
369
- explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree,
370
- nextBlankURLCount: {blankCount: number}):
371
- {node: TreeOutline.TreeOutlineUtils.TreeNode<FrameTreeNodeData>, frameCount: number, issueCount: number} {
572
+ explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree|undefined,
573
+ nextBlankURLCount: {blankCount: number}): {node: FrameTreeNodeData, frameCount: number, issueCount: number} {
574
+ if (!explanationTree) {
575
+ return {node: {text: ''}, frameCount: 0, issueCount: 0};
576
+ }
372
577
  let frameCount = 1;
373
578
  let issueCount = 0;
374
- const children: Array<TreeOutline.TreeOutlineUtils.TreeNode<FrameTreeNodeData>> = [];
579
+ const children: FrameTreeNodeData[] = [];
375
580
 
376
581
  let nodeUrlText = '';
377
582
  if (explanationTree.url.length) {
@@ -382,7 +587,7 @@ export class BackForwardCacheView extends LegacyWrapper.LegacyWrapper.WrappableC
382
587
  }
383
588
 
384
589
  for (const explanation of explanationTree.explanations) {
385
- const child = {treeNodeData: {text: explanation.reason}, id: String(this.#nextNodeId++)};
590
+ const child = {text: explanation.reason};
386
591
  issueCount += 1;
387
592
  children.push(child);
388
593
  }
@@ -395,18 +600,12 @@ export class BackForwardCacheView extends LegacyWrapper.LegacyWrapper.WrappableC
395
600
  }
396
601
  }
397
602
 
398
- let node: TreeOutline.TreeOutlineUtils.TreeNode<FrameTreeNodeData> = {
399
- treeNodeData: {
400
- text: `(${issueCount}) ${nodeUrlText}`,
401
- },
402
- id: String(this.#nextNodeId++),
603
+ let node: FrameTreeNodeData = {
604
+ text: `(${issueCount}) ${nodeUrlText}`,
403
605
  };
404
606
  if (children.length) {
405
- node = {
406
- ...node,
407
- children: () => Promise.resolve(children),
408
- };
409
- node.treeNodeData.iconName = 'iframe';
607
+ node = {...node, children};
608
+ node.iconName = 'iframe';
410
609
  } else if (!explanationTree.url.length) {
411
610
  // If the current node increased the blank count, but it has no children and
412
611
  // is therefore not shown, decrement the blank count again.
@@ -415,42 +614,6 @@ export class BackForwardCacheView extends LegacyWrapper.LegacyWrapper.WrappableC
415
614
  return {node, frameCount, issueCount};
416
615
  }
417
616
 
418
- #renderBackForwardCacheStatus(status: boolean|undefined): Lit.TemplateResult {
419
- switch (status) {
420
- case true:
421
- // clang-format off
422
- return html`
423
- <devtools-report-section>
424
- <div class="status extra-large">
425
- <devtools-icon class="inline-icon extra-large" name="check-circle" style="color: var(--icon-checkmark-green);">
426
- </devtools-icon>
427
- </div>
428
- ${i18nString(UIStrings.restoredFromBFCache)}
429
- </devtools-report-section>
430
- `;
431
- // clang-format on
432
- case false:
433
- // clang-format off
434
- return html`
435
- <devtools-report-section>
436
- <div class="status">
437
- <devtools-icon class="inline-icon extra-large" name="clear">
438
- </devtools-icon>
439
- </div>
440
- ${i18nString(UIStrings.normalNavigation)}
441
- </devtools-report-section>
442
- `;
443
- // clang-format on
444
- }
445
- // clang-format off
446
- return html`
447
- <devtools-report-section>
448
- ${i18nString(UIStrings.unknown)}
449
- </devtools-report-section>
450
- `;
451
- // clang-format on
452
- }
453
-
454
617
  #buildReasonToFramesMap(
455
618
  explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree,
456
619
  nextBlankURLCount: {blankCount: number},
@@ -473,156 +636,12 @@ export class BackForwardCacheView extends LegacyWrapper.LegacyWrapper.WrappableC
473
636
  this.#buildReasonToFramesMap(child, nextBlankURLCount, outputMap);
474
637
  });
475
638
  }
476
-
477
- #maybeRenderExplanations(
478
- explanations: Protocol.Page.BackForwardCacheNotRestoredExplanation[],
479
- explanationTree: Protocol.Page.BackForwardCacheNotRestoredExplanationTree|undefined): Lit.LitTemplate {
480
- if (explanations.length === 0) {
481
- return Lit.nothing;
482
- }
483
-
484
- const pageSupportNeeded = explanations.filter(
485
- explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.PageSupportNeeded);
486
- const supportPending = explanations.filter(
487
- explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.SupportPending);
488
- const circumstantial = explanations.filter(
489
- explanation => explanation.type === Protocol.Page.BackForwardCacheNotRestoredReasonType.Circumstantial);
490
-
491
- const reasonToFramesMap = new Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>();
492
- if (explanationTree) {
493
- this.#buildReasonToFramesMap(explanationTree, {blankCount: 1}, reasonToFramesMap);
494
- }
495
- // Disabled until https://crbug.com/1079231 is fixed.
496
- // clang-format off
497
- return html`
498
- ${this.#renderExplanations(i18nString(UIStrings.pageSupportNeeded), i18nString(UIStrings.pageSupportNeededExplanation), pageSupportNeeded, reasonToFramesMap)}
499
- ${this.#renderExplanations(i18nString(UIStrings.supportPending), i18nString(UIStrings.supportPendingExplanation), supportPending, reasonToFramesMap)}
500
- ${this.#renderExplanations(i18nString(UIStrings.circumstantial), i18nString(UIStrings.circumstantialExplanation), circumstantial, reasonToFramesMap)}
501
- `;
502
- // clang-format on
503
- }
504
-
505
- #renderExplanations(
506
- category: Platform.UIString.LocalizedString, explainerText: Platform.UIString.LocalizedString,
507
- explanations: Protocol.Page.BackForwardCacheNotRestoredExplanation[],
508
- reasonToFramesMap: Map<Protocol.Page.BackForwardCacheNotRestoredReason, string[]>): Lit.TemplateResult {
509
- // Disabled until https://crbug.com/1079231 is fixed.
510
- // clang-format off
511
- return html`
512
- ${explanations.length > 0 ? html`
513
- <devtools-report-section-header>
514
- ${category}
515
- <div class="help-outline-icon">
516
- <devtools-icon class="inline-icon medium" name="help" title=${explainerText}>
517
- </devtools-icon>
518
- </div>
519
- </devtools-report-section-header>
520
- ${explanations.map(explanation => this.#renderReason(explanation, reasonToFramesMap.get(explanation.reason)))}
521
- ` : Lit.nothing}
522
- `;
523
- // clang-format on
524
- }
525
-
526
- #maybeRenderReasonContext(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation): Lit.LitTemplate {
527
- if (explanation.reason ===
528
- Protocol.Page.BackForwardCacheNotRestoredReason.EmbedderExtensionSentMessageToCachedFrame &&
529
- explanation.context) {
530
- const link = 'chrome://extensions/?id=' + explanation.context as Platform.DevToolsPath.UrlString;
531
- // clang-format off
532
- return html`${i18nString(UIStrings.blockingExtensionId)}
533
- <devtools-chrome-link .href=${link}>${explanation.context}</devtools-chrome-link>`;
534
- // clang-format on
535
- }
536
- return Lit.nothing;
537
- }
538
-
539
- #renderFramesPerReason(frames: string[]|undefined): Lit.LitTemplate {
540
- if (frames === undefined || frames.length === 0) {
541
- return Lit.nothing;
542
- }
543
- const rows = [html`<div>${i18nString(UIStrings.framesPerIssue, {n: frames.length})}</div>`];
544
- rows.push(...frames.map(url => html`<div class="text-ellipsis" title=${url}
545
- jslog=${VisualLogging.treeItem()}>${url}</div>`));
546
- return html`
547
- <div class="details-list"
548
- jslog=${VisualLogging.tree('frames-per-issue')}>
549
- <devtools-expandable-list .data=${{
550
- rows,
551
- title: i18nString(UIStrings.framesPerIssue, {n: frames.length}),
552
- } as ExpandableList.ExpandableList.ExpandableListData}
553
- jslog=${VisualLogging.treeItem()}></devtools-expandable-list>
554
- </div>
555
- `;
556
- }
557
-
558
- #maybeRenderDeepLinkToUnload(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation): Lit.LitTemplate {
559
- if (explanation.reason === Protocol.Page.BackForwardCacheNotRestoredReason.UnloadHandlerExistsInMainFrame ||
560
- explanation.reason === Protocol.Page.BackForwardCacheNotRestoredReason.UnloadHandlerExistsInSubFrame) {
561
- return html`
562
- <x-link href="https://web.dev/bfcache/#never-use-the-unload-event" class="link"
563
- jslog=${VisualLogging.action('learn-more.never-use-unload').track({
564
- click: true,
565
- })}>
566
- ${i18nString(UIStrings.neverUseUnload)}
567
- </x-link>`;
568
- }
569
- return Lit.nothing;
570
- }
571
-
572
- #maybeRenderJavaScriptDetails(details: Protocol.Page.BackForwardCacheBlockingDetails[]|undefined): Lit.LitTemplate {
573
- if (details === undefined || details.length === 0) {
574
- return Lit.nothing;
575
- }
576
- const maxLengthForDisplayedURLs = 50;
577
- const linkifier = new Components.Linkifier.Linkifier(maxLengthForDisplayedURLs);
578
- const rows = [html`<div>${i18nString(UIStrings.filesPerIssue, {n: details.length})}</div>`];
579
- rows.push(...details.map(
580
- detail => html`${
581
- linkifier.linkifyScriptLocation(
582
- null, null, detail.url as Platform.DevToolsPath.UrlString, detail.lineNumber, {
583
- columnNumber: detail.columnNumber,
584
- showColumnNumber: true,
585
- inlineFrameIndex: 0,
586
- })}`));
587
- return html`
588
- <div class="details-list">
589
- <devtools-expandable-list .data=${
590
- {rows} as ExpandableList.ExpandableList.ExpandableListData}></devtools-expandable-list>
591
- </div>
592
- `;
593
- }
594
-
595
- #renderReason(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation, frames: string[]|undefined):
596
- Lit.TemplateResult {
597
- // clang-format off
598
- return html`
599
- <devtools-report-section>
600
- ${(explanation.reason in NotRestoredReasonDescription) ?
601
- html`
602
- <div class="circled-exclamation-icon">
603
- <devtools-icon class="inline-icon medium" style="color: var(--icon-warning)" name="warning">
604
- </devtools-icon>
605
- </div>
606
- <div>
607
- ${NotRestoredReasonDescription[explanation.reason].name()}
608
- ${this.#maybeRenderDeepLinkToUnload(explanation)}
609
- ${this.#maybeRenderReasonContext(explanation)}
610
- </div>` :
611
- Lit.nothing}
612
- </devtools-report-section>
613
- <div class="gray-text">
614
- ${explanation.reason}
615
- </div>
616
- ${this.#maybeRenderJavaScriptDetails(explanation.details)}
617
- ${this.#renderFramesPerReason(frames)}
618
- `;
619
- // clang-format on
620
- }
621
639
  }
622
640
 
623
641
  interface FrameTreeNodeData {
624
642
  text: string;
625
643
  iconName?: string;
644
+ children?: FrameTreeNodeData[];
626
645
  }
627
646
 
628
647
  customElements.define('devtools-resources-back-forward-cache-view', BackForwardCacheView);