chrome-devtools-frontend 1.0.1581449 → 1.0.1582745

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 (95) hide show
  1. package/agents/prompts/merging-devtools-module.md +144 -0
  2. package/agents/prompts/ui-widgets.md +351 -0
  3. package/agents/prompts/verification.md +2 -1
  4. package/docs/contributing/README.md +5 -6
  5. package/docs/contributing/changes.md +1 -2
  6. package/docs/styleguide/ux/README.md +1 -1
  7. package/front_end/core/sdk/OverlayModel.ts +4 -2
  8. package/front_end/core/sdk/RemoteObject.ts +7 -1
  9. package/front_end/core/sdk/StorageKeyManager.ts +6 -1
  10. package/front_end/core/sdk/Target.ts +4 -2
  11. package/front_end/entrypoint_template.html +5 -1
  12. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +31 -40
  13. package/front_end/entrypoints/greendev_floaty/floaty.css +41 -1
  14. package/front_end/entrypoints/greendev_floaty/floaty.html +8 -1
  15. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +5 -5
  16. package/front_end/entrypoints/node_app/app/NodeMain.ts +19 -1
  17. package/front_end/entrypoints/node_app/node_app.ts +34 -0
  18. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  19. package/front_end/generated/SupportedCSSProperties.js +2 -0
  20. package/front_end/generated/protocol.ts +0 -6
  21. package/front_end/models/ai_assistance/AiConversation.ts +10 -0
  22. package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -0
  23. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +26 -4
  24. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +85 -7
  25. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +2 -6
  26. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +66 -2
  27. package/front_end/models/computed_style/ComputedStyleModel.ts +26 -0
  28. package/front_end/models/greendev/Prototypes.ts +1 -10
  29. package/front_end/models/issues_manager/ConnectionAllowlistIssue.ts +75 -0
  30. package/front_end/models/issues_manager/CookieIssue.ts +0 -28
  31. package/front_end/models/issues_manager/FederatedAuthRequestIssue.ts +0 -30
  32. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  33. package/front_end/models/issues_manager/descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
  34. package/front_end/models/issues_manager/descriptions/connectionAllowlistInvalidHeader.md +12 -0
  35. package/front_end/models/issues_manager/descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
  36. package/front_end/models/issues_manager/descriptions/connectionAllowlistItemNotInnerList.md +12 -0
  37. package/front_end/models/issues_manager/descriptions/connectionAllowlistMoreThanOneList.md +7 -0
  38. package/front_end/models/issues_manager/descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
  39. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  40. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +93 -6
  41. package/front_end/panels/ai_assistance/components/ChatInput.ts +8 -4
  42. package/front_end/panels/application/ApplicationPanelSidebar.ts +13 -11
  43. package/front_end/panels/application/DOMStorageModel.ts +1 -1
  44. package/front_end/panels/application/ResourcesPanel.ts +10 -5
  45. package/front_end/panels/application/preloading/PreloadingView.ts +8 -1
  46. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +4 -1
  47. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +2 -1
  48. package/front_end/panels/application/preloading/components/PreloadingString.ts +12 -3
  49. package/front_end/panels/application/preloading/helper/PreloadingForward.ts +14 -0
  50. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +37 -3
  51. package/front_end/panels/changes/ChangesSidebar.ts +2 -6
  52. package/front_end/panels/common/AiCodeCompletionTeaser.ts +13 -3
  53. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -0
  54. package/front_end/panels/console/ConsoleSidebar.ts +3 -11
  55. package/front_end/panels/console_counters/WarningErrorCounter.ts +16 -10
  56. package/front_end/panels/elements/ComputedStyleWidget.ts +55 -37
  57. package/front_end/panels/elements/PlatformFontsWidget.ts +23 -10
  58. package/front_end/panels/greendev/GreenDevPanel.css +42 -1
  59. package/front_end/panels/greendev/GreenDevPanel.ts +30 -1
  60. package/front_end/panels/lighthouse/LighthouseStartView.ts +3 -5
  61. package/front_end/panels/lighthouse/lighthouseStartView.css +6 -0
  62. package/front_end/panels/network/NetworkLogView.ts +6 -6
  63. package/front_end/panels/network/RequestInitiatorView.ts +27 -19
  64. package/front_end/panels/network/RequestTimingView.ts +1 -1
  65. package/front_end/panels/settings/AISettingsTab.ts +1 -5
  66. package/front_end/panels/settings/KeybindsSettingsTab.ts +4 -3
  67. package/front_end/panels/settings/SettingsScreen.ts +0 -51
  68. package/front_end/panels/sources/OutlineQuickOpen.ts +19 -0
  69. package/front_end/panels/timeline/AnimationsTrackAppender.ts +4 -1
  70. package/front_end/panels/timeline/InteractionsTrackAppender.ts +1 -1
  71. package/front_end/panels/timeline/TimelinePanel.ts +25 -0
  72. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -16
  73. package/front_end/third_party/chromium/README.chromium +1 -1
  74. package/front_end/third_party/lighthouse/README.chromium +2 -2
  75. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +145 -144
  76. package/front_end/third_party/lighthouse/report/bundle.js +12 -5
  77. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  78. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +5 -1
  79. package/front_end/ui/legacy/ListControl.ts +28 -1
  80. package/front_end/ui/legacy/Toolbar.ts +4 -4
  81. package/front_end/ui/legacy/Treeoutline.ts +5 -5
  82. package/front_end/ui/legacy/UIUtils.ts +26 -10
  83. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +10 -13
  84. package/front_end/ui/legacy/components/utils/Linkifier.ts +4 -7
  85. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  86. package/inspector_overlay/main.ts +18 -3
  87. package/inspector_overlay/tool_green_dev_anchors.css +54 -0
  88. package/inspector_overlay/tool_green_dev_anchors.ts +164 -0
  89. package/inspector_overlay/tool_persistent.ts +14 -0
  90. package/package.json +1 -1
  91. package/docs/contributing/design.md +0 -166
  92. package/docs/design_guidelines.md +0 -1
  93. package/front_end/models/issues_manager/descriptions/federatedAuthRequestClientMetadataHttpNotFound.md +0 -1
  94. package/front_end/models/issues_manager/descriptions/federatedAuthRequestClientMetadataInvalidResponse.md +0 -1
  95. package/front_end/models/issues_manager/descriptions/federatedAuthRequestClientMetadataNoResponse.md +0 -1
@@ -15,6 +15,7 @@ import * as Annotations from '../../models/annotations/annotations.js';
15
15
  import * as Badges from '../../models/badges/badges.js';
16
16
  import * as GreenDev from '../../models/greendev/greendev.js';
17
17
  import * as TextUtils from '../../models/text_utils/text_utils.js';
18
+ import type * as Trace from '../../models/trace/trace.js';
18
19
  import * as Workspace from '../../models/workspace/workspace.js';
19
20
  import * as Buttons from '../../ui/components/buttons/buttons.js';
20
21
  import * as Snackbars from '../../ui/components/snackbars/snackbars.js';
@@ -503,6 +504,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
503
504
  #selectedRequest: AiAssistanceModel.NetworkAgent.RequestContext|null = null;
504
505
  // Messages displayed in the `ChatView` component.
505
506
  #messages: Message[] = [];
507
+ #isContextAutoSelectionSuspended = false;
506
508
 
507
509
  // Whether the UI should show loading or not.
508
510
  #isLoading = false;
@@ -672,6 +674,10 @@ export class AiAssistancePanel extends UI.Panel.Panel {
672
674
  this.requestUpdate();
673
675
  }
674
676
 
677
+ async #handlePerformanceRecordAndReload(): Promise<Trace.TraceModel.ParsedTrace> {
678
+ return await TimelinePanel.TimelinePanel.TimelinePanel.executeRecordAndReload();
679
+ }
680
+
675
681
  #getDefaultConversationType(): AiAssistanceModel.AiHistoryStorage.ConversationType|undefined {
676
682
  const {hostConfig} = Root.Runtime;
677
683
  const viewManager = UI.ViewManager.ViewManager.instance();
@@ -715,7 +721,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
715
721
  }
716
722
  const conversation = targetConversationType ?
717
723
  new AiAssistanceModel.AiConversation.AiConversation(
718
- targetConversationType, [], undefined, false, this.#aidaClient, this.#changeManager) :
724
+ targetConversationType, [], undefined, false, this.#aidaClient, this.#changeManager, false,
725
+ this.#handlePerformanceRecordAndReload.bind(this), this.#handleInspectElement.bind(this)) :
719
726
  undefined;
720
727
 
721
728
  this.#updateConversationState(conversation);
@@ -735,16 +742,28 @@ export class AiAssistancePanel extends UI.Panel.Panel {
735
742
  const conversationType = this.#getDefaultConversationType();
736
743
  if (conversationType) {
737
744
  conversation = new AiAssistanceModel.AiConversation.AiConversation(
738
- conversationType, [], undefined, false, this.#aidaClient, this.#changeManager);
745
+ conversationType,
746
+ [],
747
+ undefined,
748
+ false,
749
+ this.#aidaClient,
750
+ this.#changeManager,
751
+ false,
752
+ this.#handlePerformanceRecordAndReload.bind(this),
753
+ this.#handleInspectElement.bind(this),
754
+ );
739
755
  }
740
756
  }
741
757
 
742
758
  this.#conversation = conversation;
759
+ this.#isContextAutoSelectionSuspended = false;
743
760
  }
744
761
 
745
- this.#conversation?.setContext(this.#getConversationContext(
746
- isAiAssistanceContextSelectionAgentEnabled() ? this.#getDefaultConversationType() :
747
- (this.#conversation?.type ?? null)));
762
+ if (!this.#isContextAutoSelectionSuspended) {
763
+ this.#conversation?.setContext(this.#getConversationContext(
764
+ isAiAssistanceContextSelectionAgentEnabled() ? this.#getDefaultConversationType() :
765
+ (this.#conversation?.type ?? null)));
766
+ }
748
767
 
749
768
  this.requestUpdate();
750
769
  }
@@ -1116,10 +1135,12 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1116
1135
 
1117
1136
  #handleContextRemoved(): void {
1118
1137
  this.#conversation?.setContext(null);
1138
+ this.#isContextAutoSelectionSuspended = true;
1119
1139
  this.requestUpdate();
1120
1140
  }
1121
1141
 
1122
1142
  #handleContextAdd(): void {
1143
+ this.#isContextAutoSelectionSuspended = false;
1123
1144
  this.#conversation?.setContext(this.#getConversationContext(this.#getDefaultConversationType()));
1124
1145
  this.requestUpdate();
1125
1146
  }
@@ -1141,6 +1162,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1141
1162
  return;
1142
1163
  }
1143
1164
 
1165
+ this.#isContextAutoSelectionSuspended = false;
1144
1166
  let targetConversationType: AiAssistanceModel.AiHistoryStorage.ConversationType|undefined;
1145
1167
  switch (actionId) {
1146
1168
  case 'freestyler.elements-floating-button': {
@@ -1187,7 +1209,16 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1187
1209
  let conversation = this.#conversation;
1188
1210
  if (!this.#conversation || this.#conversation.type !== targetConversationType || this.#conversation.isEmpty) {
1189
1211
  conversation = new AiAssistanceModel.AiConversation.AiConversation(
1190
- targetConversationType, [], undefined, false, this.#aidaClient, this.#changeManager);
1212
+ targetConversationType,
1213
+ [],
1214
+ undefined,
1215
+ false,
1216
+ this.#aidaClient,
1217
+ this.#changeManager,
1218
+ false,
1219
+ this.#handlePerformanceRecordAndReload.bind(this),
1220
+ this.#handleInspectElement.bind(this),
1221
+ );
1191
1222
  }
1192
1223
  this.#updateConversationState(conversation);
1193
1224
  const predefinedPrompt = opts?.['prompt'];
@@ -1334,11 +1365,67 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1334
1365
  this.#conversation?.setContext(context);
1335
1366
  }
1336
1367
 
1368
+ this.#isContextAutoSelectionSuspended = false;
1369
+
1337
1370
  void VisualLogging.logFunctionCall(`context-change-${this.#conversation?.type}`);
1338
1371
 
1339
1372
  this.requestUpdate();
1340
1373
  };
1341
1374
 
1375
+ async #handleInspectElement(): Promise<SDK.DOMModel.DOMNode|null> {
1376
+ if (!this.#toggleSearchElementAction) {
1377
+ return null;
1378
+ }
1379
+
1380
+ const result = new Promise<SDK.DOMModel.DOMNode|null>(resolve => {
1381
+ // Track the new flavor change for dom node.
1382
+ const handleDOMNodeFlavorChange = (ev: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode>): void => {
1383
+ if (!ev.data) {
1384
+ return;
1385
+ }
1386
+ resolve(selectedElementFilter(ev.data));
1387
+ removeListeners();
1388
+ };
1389
+
1390
+ // If the inspect mode is toggled, we want to resolve null.
1391
+ const handleInspectModeToggled = (ev: Common.EventTarget.EventTargetEvent<boolean>): void => {
1392
+ if (!ev.data) {
1393
+ // The inspect element is toggled off
1394
+ // before the flavor change event fires
1395
+ // so we need to wait a bit to see if the flavor changed.
1396
+ window.setTimeout(() => {
1397
+ resolve((selectedElementFilter(UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode))));
1398
+ removeListeners();
1399
+ }, 50);
1400
+ }
1401
+ };
1402
+
1403
+ const removeListeners = (): void => {
1404
+ UI.Context.Context.instance().removeFlavorChangeListener(SDK.DOMModel.DOMNode, handleDOMNodeFlavorChange);
1405
+ this.#toggleSearchElementAction?.removeEventListener(
1406
+ UI.ActionRegistration.Events.TOGGLED, handleInspectModeToggled);
1407
+ };
1408
+
1409
+ UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, handleDOMNodeFlavorChange);
1410
+ this.#toggleSearchElementAction?.addEventListener(UI.ActionRegistration.Events.TOGGLED, handleInspectModeToggled);
1411
+
1412
+ // Clean-up listeners in case of abort.
1413
+ this.#runAbortController.signal.addEventListener('abort', () => {
1414
+ resolve(null);
1415
+ removeListeners();
1416
+ }, {once: true});
1417
+ });
1418
+
1419
+ void this.#toggleSearchElementAction.execute();
1420
+ try {
1421
+ return await result;
1422
+ } finally {
1423
+ if (this.#toggleSearchElementAction.toggled()) {
1424
+ void this.#toggleSearchElementAction.execute();
1425
+ }
1426
+ }
1427
+ }
1428
+
1342
1429
  async #startConversation(
1343
1430
  text: string,
1344
1431
  imageInput?: Host.AidaClient.Part,
@@ -42,7 +42,11 @@ const UIStrings = {
42
42
  /**
43
43
  * @description Label added to the button that remove the currently selected context in AI Assistance panel.
44
44
  */
45
- removeContext: 'Remove selected context',
45
+ removeContext: 'Remove from context',
46
+ /**
47
+ * @description Label added to the button that add selected context from the current panel in AI Assistance panel.
48
+ */
49
+ addContext: 'Add selected item as context',
46
50
  } as const;
47
51
 
48
52
  /*
@@ -387,12 +391,12 @@ export const
387
391
  :
388
392
  input.onContextAdd ? html`
389
393
  <devtools-button
390
- title=${i18nString(UIStrings.removeContext)}
391
- aria-label=${i18nString(UIStrings.removeContext)}
394
+ title=${i18nString(UIStrings.addContext)}
395
+ aria-label=${i18nString(UIStrings.addContext)}
392
396
  class="add-context"
393
397
  .iconName=${'plus'}
394
398
  .size=${Buttons.Button.Size.SMALL}
395
- .jslogContext=${'context-add'}
399
+ .jslogContext=${'context-added'}
396
400
  .variant=${Buttons.Button.Variant.ICON}
397
401
  @click=${input.onContextAdd}></devtools-button>` : Lit.nothing}
398
402
  </div>
@@ -1916,17 +1916,19 @@ export class ResourcesSection implements SDK.TargetManager.Observer {
1916
1916
  frameManager.addEventListener(
1917
1917
  SDK.FrameManager.Events.RESOURCE_ADDED, event => this.resourceAdded(event.data.resource), this);
1918
1918
 
1919
- SDK.TargetManager.TargetManager.instance().addModelListener(
1920
- SDK.ChildTargetManager.ChildTargetManager, SDK.ChildTargetManager.Events.TARGET_CREATED, this.windowOpened,
1921
- this, {scoped: true});
1922
- SDK.TargetManager.TargetManager.instance().addModelListener(
1923
- SDK.ChildTargetManager.ChildTargetManager, SDK.ChildTargetManager.Events.TARGET_INFO_CHANGED,
1924
- this.windowChanged, this, {scoped: true});
1925
- SDK.TargetManager.TargetManager.instance().addModelListener(
1926
- SDK.ChildTargetManager.ChildTargetManager, SDK.ChildTargetManager.Events.TARGET_DESTROYED, this.windowDestroyed,
1927
- this, {scoped: true});
1928
-
1929
- SDK.TargetManager.TargetManager.instance().observeTargets(this, {scoped: true});
1919
+ if (this.panel.mode !== 'node') {
1920
+ SDK.TargetManager.TargetManager.instance().addModelListener(
1921
+ SDK.ChildTargetManager.ChildTargetManager, SDK.ChildTargetManager.Events.TARGET_CREATED, this.windowOpened,
1922
+ this, {scoped: true});
1923
+ SDK.TargetManager.TargetManager.instance().addModelListener(
1924
+ SDK.ChildTargetManager.ChildTargetManager, SDK.ChildTargetManager.Events.TARGET_INFO_CHANGED,
1925
+ this.windowChanged, this, {scoped: true});
1926
+ SDK.TargetManager.TargetManager.instance().addModelListener(
1927
+ SDK.ChildTargetManager.ChildTargetManager, SDK.ChildTargetManager.Events.TARGET_DESTROYED,
1928
+ this.windowDestroyed, this, {scoped: true});
1929
+
1930
+ SDK.TargetManager.TargetManager.instance().observeTargets(this, {scoped: true});
1931
+ }
1930
1932
  }
1931
1933
 
1932
1934
  private initialize(): void {
@@ -250,7 +250,7 @@ export class DOMStorageModel extends SDK.SDKModel.SDKModel<EventTypes> {
250
250
  }
251
251
  }
252
252
 
253
- SDK.SDKModel.SDKModel.register(DOMStorageModel, {capabilities: SDK.Target.Capability.DOM, autostart: false});
253
+ SDK.SDKModel.SDKModel.register(DOMStorageModel, {capabilities: SDK.Target.Capability.DOM_STORAGE, autostart: false});
254
254
 
255
255
  export const enum Events {
256
256
  DOM_STORAGE_ADDED = 'DOMStorageAdded',
@@ -37,9 +37,13 @@ export class ResourcesPanel extends UI.Panel.PanelWithSidebar {
37
37
  private cookieView: CookieItemsView|null;
38
38
  private deviceBoundSessionsView: DeviceBoundSessionsView|null;
39
39
  private readonly sidebar: ApplicationPanelSidebar;
40
+ mode: 'default'|'node' = 'default';
40
41
 
41
- private constructor() {
42
+ private constructor(
43
+ mode: 'default'|'node' = 'default',
44
+ ) {
42
45
  super('resources');
46
+ this.mode = mode;
43
47
  this.registerRequiredCSS(resourcesPanelStyles);
44
48
 
45
49
  this.resourcesLastSelectedItemSetting =
@@ -68,11 +72,12 @@ export class ResourcesPanel extends UI.Panel.PanelWithSidebar {
68
72
  }
69
73
 
70
74
  static instance(opts: {
71
- forceNew: boolean|null,
72
- } = {forceNew: null}): ResourcesPanel {
73
- const {forceNew} = opts;
75
+ forceNew?: boolean|null,
76
+ mode?: 'default'|'node',
77
+ } = {forceNew: null, mode: 'default'}): ResourcesPanel {
78
+ const {forceNew, mode} = opts;
74
79
  if (!resourcesPanelInstance || forceNew) {
75
- resourcesPanelInstance = new ResourcesPanel();
80
+ resourcesPanelInstance = new ResourcesPanel(mode);
76
81
  }
77
82
 
78
83
  return resourcesPanelInstance;
@@ -22,7 +22,7 @@ import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
22
22
 
23
23
  import * as PreloadingComponents from './components/components.js';
24
24
  import {ruleSetTagOrLocationShort} from './components/PreloadingString.js';
25
- import type * as PreloadingHelper from './helper/helper.js';
25
+ import * as PreloadingHelper from './helper/helper.js';
26
26
  import preloadingViewStyles from './preloadingView.css.js';
27
27
  import preloadingViewDropDownStyles from './preloadingViewDropDown.css.js';
28
28
 
@@ -501,10 +501,17 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
501
501
  const ruleSet = this.model.getRuleSetById(id);
502
502
  return ruleSet === null ? [] : [ruleSet];
503
503
  });
504
+
505
+ // Lookup status code for prefetch attempts
506
+ const statusCode = attempt.action === Protocol.Preload.SpeculationAction.Prefetch ?
507
+ PreloadingHelper.PreloadingForward.prefetchStatusCode(attempt.requestId) :
508
+ undefined;
509
+
504
510
  return {
505
511
  id,
506
512
  pipeline,
507
513
  ruleSets,
514
+ statusCode,
508
515
  };
509
516
  });
510
517
  this.preloadingGrid.rows = rows;
@@ -349,7 +349,10 @@ export class PreloadingDetailsReportView extends LegacyWrapper.LegacyWrapper.Wra
349
349
  return Lit.nothing;
350
350
  }
351
351
 
352
- const failureDescription = prefetchFailureReason(attempt);
352
+ // Lookup status code for Non2XX failures
353
+ const statusCode = PreloadingHelper.PreloadingForward.prefetchStatusCode(attempt.requestId);
354
+
355
+ const failureDescription = prefetchFailureReason(attempt, statusCode);
353
356
  if (failureDescription === null) {
354
357
  return Lit.nothing;
355
358
  }
@@ -51,6 +51,7 @@ export interface PreloadingGridRow {
51
51
  id: string;
52
52
  pipeline: SDK.PreloadingModel.PreloadPipeline;
53
53
  ruleSets: Protocol.Preload.RuleSet[];
54
+ statusCode?: number;
54
55
  }
55
56
 
56
57
  export interface ViewInput {
@@ -107,7 +108,7 @@ export const PRELOADING_GRID_DEFAULT_VIEW: View = (input, _output, target): void
107
108
  'vertical-align': 'sub',
108
109
  })}
109
110
  ></devtools-icon>` : ''}
110
- ${hasWarning ? i18nString(UIStrings.prefetchFallbackReady) : composedStatus(attempt)}
111
+ ${hasWarning ? i18nString(UIStrings.prefetchFallbackReady) : composedStatus(attempt, row.statusCode)}
111
112
  </div>
112
113
  </td>
113
114
  </tr>`;
@@ -31,6 +31,11 @@ const UIStrings = {
31
31
  * @description Description text for Prefetch status PrefetchFailedNon2XX.
32
32
  */
33
33
  PrefetchFailedNon2XX: 'The prefetch failed because of a non-2xx HTTP response status code.',
34
+ /**
35
+ * @description Description text for Prefetch status PrefetchFailedNon2XX when the HTTP status code is known.
36
+ * @example {404} PH1
37
+ */
38
+ PrefetchFailedNon2XXWithStatusCode: 'The prefetch failed because of a non-2xx HTTP response status code ({PH1}).',
34
39
  /**
35
40
  * @description Description text for Prefetch status PrefetchIneligibleRetryAfter.
36
41
  */
@@ -446,7 +451,8 @@ export const PrefetchReasonDescription: Record<string, {name: () => Platform.UIS
446
451
  };
447
452
 
448
453
  /** Decoding PrefetchFinalStatus prefetchAttempt to failure description. **/
449
- export function prefetchFailureReason({prefetchStatus}: SDK.PreloadingModel.PrefetchAttempt): string|null {
454
+ export function prefetchFailureReason(
455
+ {prefetchStatus}: SDK.PreloadingModel.PrefetchAttempt, statusCode?: number): string|null {
450
456
  // If you face an error on rolling CDP changes, see
451
457
  // https://docs.google.com/document/d/1PnrfowsZMt62PX1EvvTp2Nqs3ji1zrklrAEe1JYbkTk
452
458
  switch (prefetchStatus) {
@@ -478,6 +484,9 @@ export function prefetchFailureReason({prefetchStatus}: SDK.PreloadingModel.Pref
478
484
  case Protocol.Preload.PrefetchStatus.PrefetchFailedNetError:
479
485
  return PrefetchReasonDescription['PrefetchFailedNetError'].name();
480
486
  case Protocol.Preload.PrefetchStatus.PrefetchFailedNon2XX:
487
+ if (statusCode !== undefined) {
488
+ return i18nString(UIStrings.PrefetchFailedNon2XXWithStatusCode, {PH1: String(statusCode)});
489
+ }
481
490
  return PrefetchReasonDescription['PrefetchFailedNon2XX'].name();
482
491
  case Protocol.Preload.PrefetchStatus.PrefetchIneligibleRetryAfter:
483
492
  return PrefetchReasonDescription['PrefetchIneligibleRetryAfter'].name();
@@ -795,7 +804,7 @@ export function status(status: SDK.PreloadingModel.PreloadingStatus): string {
795
804
  }
796
805
  }
797
806
 
798
- export function composedStatus(attempt: SDK.PreloadingModel.PreloadingAttempt): string {
807
+ export function composedStatus(attempt: SDK.PreloadingModel.PreloadingAttempt, statusCode?: number): string {
799
808
  const short = status(attempt.status);
800
809
 
801
810
  if (attempt.status !== SDK.PreloadingModel.PreloadingStatus.FAILURE) {
@@ -804,7 +813,7 @@ export function composedStatus(attempt: SDK.PreloadingModel.PreloadingAttempt):
804
813
 
805
814
  switch (attempt.action) {
806
815
  case Protocol.Preload.SpeculationAction.Prefetch: {
807
- const detail = prefetchFailureReason(attempt) ?? i18n.i18n.lockedString('Internal error');
816
+ const detail = prefetchFailureReason(attempt, statusCode) ?? i18n.i18n.lockedString('Internal error');
808
817
  return short + ' - ' + detail;
809
818
  }
810
819
  case Protocol.Preload.SpeculationAction.Prerender:
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import type * as Protocol from '../../../../generated/protocol.js';
6
+ import * as Logs from '../../../../models/logs/logs.js';
6
7
 
7
8
  export class RuleSetView {
8
9
  readonly ruleSetId: Protocol.Preload.RuleSetId|null;
@@ -19,3 +20,16 @@ export class AttemptViewWithFilter {
19
20
  this.ruleSetId = ruleSetId;
20
21
  }
21
22
  }
23
+
24
+ /**
25
+ * Retrieves the HTTP status code for a prefetch attempt by looking up its
26
+ * network request in the network log.
27
+ */
28
+ export function prefetchStatusCode(requestId: Protocol.Network.RequestId): number|undefined {
29
+ const networkLog = Logs.NetworkLog.NetworkLog.instance();
30
+ const requests = networkLog.requestsForId(requestId);
31
+ if (requests.length > 0) {
32
+ return requests[requests.length - 1].statusCode;
33
+ }
34
+ return undefined;
35
+ }
@@ -134,6 +134,9 @@ interface ViewOutput {
134
134
  interface ViewInput {
135
135
  onFilterChanged: (filterText: string|null) => void;
136
136
  onBreakpointChange: (breakpoint: SDK.CategorizedBreakpoint.CategorizedBreakpoint, enabled: boolean) => void;
137
+ onItemSelected: (item: SDK.CategorizedBreakpoint.Category|SDK.CategorizedBreakpoint.CategorizedBreakpoint|
138
+ null) => void;
139
+ onSpaceKeyDown: () => void;
137
140
 
138
141
  filterText: string|null;
139
142
  userExpandedCategories: Set<SDK.CategorizedBreakpoint.Category>;
@@ -200,6 +203,13 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
200
203
  }
201
204
  };
202
205
 
206
+ const onKeyDown = (e: KeyboardEvent): void => {
207
+ if (e.key === ' ') {
208
+ input.onSpaceKeyDown();
209
+ e.preventDefault();
210
+ }
211
+ };
212
+
203
213
  render(
204
214
  // clang-format off
205
215
  html`
@@ -210,10 +220,11 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
210
220
  style="flex: 1;"
211
221
  ></devtools-toolbar-input>
212
222
  </devtools-toolbar>
213
- <devtools-tree autofocus .template=${html`
223
+ <devtools-tree autofocus @keydown=${onKeyDown} .template=${html`
214
224
  <ul role="tree">
215
225
  ${filteredCategories.map(([category, breakpoints]) => html`
216
- <li @expand=${(e: UI.TreeOutline.TreeViewElement.ExpandEvent) => onExpand(category, e)}
226
+ <li @select=${() => input.onItemSelected(category)}
227
+ @expand=${(e: UI.TreeOutline.TreeViewElement.ExpandEvent) => onExpand(category, e)}
217
228
  role="treeitem"
218
229
  jslog-context=${category}
219
230
  aria-checked=${breakpoints.some(breakpoint => breakpoint.enabled())
@@ -233,7 +244,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
233
244
  role="group"
234
245
  ?hidden=${!shouldExpandCategory(breakpoints) && !input.userExpandedCategories.has(category)}>
235
246
  ${breakpoints.map(breakpoint => html`
236
- <li
247
+ <li @select=${() => input.onItemSelected(breakpoint)}
237
248
  role="treeitem"
238
249
  aria-checked=${breakpoint.enabled()}
239
250
  jslog-context=${Platform.StringUtilities.toKebabCase(breakpoint.name)}>
@@ -265,6 +276,7 @@ export abstract class CategorizedBreakpointsSidebarPane extends UI.Widget.VBox {
265
276
  #filterText: string|null = null;
266
277
  #view: View;
267
278
  #userExpandedCategories = new Set<SDK.CategorizedBreakpoint.Category>();
279
+ #selectedItem: SDK.CategorizedBreakpoint.Category|SDK.CategorizedBreakpoint.CategorizedBreakpoint|null = null;
268
280
  constructor(
269
281
  breakpoints: SDK.CategorizedBreakpoint.CategorizedBreakpoint[], jslog: string, viewId: string,
270
282
  view = DEFAULT_VIEW) {
@@ -321,6 +333,26 @@ export abstract class CategorizedBreakpointsSidebarPane extends UI.Widget.VBox {
321
333
  this.requestUpdate();
322
334
  }
323
335
 
336
+ #onItemSelected(item: SDK.CategorizedBreakpoint.Category|SDK.CategorizedBreakpoint.CategorizedBreakpoint|null): void {
337
+ this.#selectedItem = item;
338
+ }
339
+
340
+ #onSpaceKeyDown(): void {
341
+ const selected = this.#selectedItem;
342
+ if (!selected) {
343
+ return;
344
+ }
345
+ if (selected instanceof SDK.CategorizedBreakpoint.CategorizedBreakpoint) {
346
+ this.onBreakpointChanged(selected, !selected.enabled());
347
+ } else {
348
+ const breakpoints = this.categories.get(selected);
349
+ if (breakpoints) {
350
+ const newEnabled = breakpoints.some(bp => !bp.enabled());
351
+ breakpoints.forEach(bp => this.onBreakpointChanged(bp, newEnabled));
352
+ }
353
+ }
354
+ }
355
+
324
356
  protected onBreakpointChanged(breakpoint: SDK.CategorizedBreakpoint.CategorizedBreakpoint, enabled: boolean): void {
325
357
  breakpoint.setEnabled(enabled);
326
358
  this.requestUpdate();
@@ -331,6 +363,8 @@ export abstract class CategorizedBreakpointsSidebarPane extends UI.Widget.VBox {
331
363
  filterText: this.#filterText,
332
364
  onFilterChanged: this.#onFilterChanged.bind(this),
333
365
  onBreakpointChange: this.onBreakpointChanged.bind(this),
366
+ onItemSelected: this.#onItemSelected.bind(this),
367
+ onSpaceKeyDown: this.#onSpaceKeyDown.bind(this),
334
368
  sortedCategoryNames: this.#sortedCategories,
335
369
  categories: this.categories,
336
370
  highlightedItem: this.#highlightedItem,
@@ -24,7 +24,7 @@ const UIStrings = {
24
24
  } as const;
25
25
  const str_ = i18n.i18n.registerUIStrings('panels/changes/ChangesSidebar.ts', UIStrings);
26
26
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
27
- const {render, html, Directives: {ref}} = Lit;
27
+ const {render, html} = Lit;
28
28
  interface ViewInput {
29
29
  selectedSourceCode: Workspace.UISourceCode.UISourceCode|null;
30
30
  onSelect: (uiSourceCode: Workspace.UISourceCode.UISourceCode|null) => void;
@@ -38,20 +38,16 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
38
38
  uiSourceCode.url();
39
39
  const icon = (uiSourceCode: Workspace.UISourceCode.UISourceCode): string =>
40
40
  Snippets.ScriptSnippetFileSystem.isSnippetsUISourceCode(uiSourceCode) ? 'snippet' : 'document';
41
- const configElements = new WeakMap<HTMLLIElement, Workspace.UISourceCode.UISourceCode>();
42
- const onSelect = (e: UI.TreeOutline.TreeViewElement.SelectEvent): void =>
43
- input.onSelect(configElements.get(e.detail) ?? null);
44
41
  render(
45
42
  // clang-format off
46
43
  html`<devtools-tree
47
- @selected=${onSelect}
48
44
  navigation-variant
49
45
  hide-overflow .template=${html`
50
46
  <ul role="tree">
51
47
  ${input.sourceCodes.values().map(uiSourceCode => html`
52
48
  <li
53
49
  role="treeitem"
54
- ${ref(e => e instanceof HTMLLIElement && configElements.set(e, uiSourceCode))}
50
+ @select=${() => input.onSelect(uiSourceCode)}
55
51
  ?selected=${uiSourceCode === input.selectedSourceCode}>
56
52
  <style>${changesSidebarStyles}</style>
57
53
  <div class=${'navigator-' + uiSourceCode.contentType().name() + '-tree-item'}>
@@ -73,12 +73,12 @@ const UIStringsNotTranslate = {
73
73
  * @description Code generation disclaimer item text for the fre dialog.
74
74
  */
75
75
  freDisclaimerDescribeCodeInComment:
76
- 'In Console or Sources, describe the code you need in a comment, then press Ctrl+I to generate it.',
76
+ 'In Console or Sources, describe the code you need in a comment, then press ctrl+i to generate it.',
77
77
  /**
78
78
  * @description Code generation disclaimer item text for the fre dialog.
79
79
  */
80
80
  freDisclaimerDescribeCodeInCommentForMacOs:
81
- 'In Console or Sources, describe the code you need in a comment, then press Cmd+I to generate it.',
81
+ 'In Console or Sources, describe the code you need in a comment, then press cmd+i to generate it.',
82
82
  /**
83
83
  * @description Privacy disclaimer item text for the fre dialog.
84
84
  */
@@ -140,7 +140,17 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
140
140
  <span>${lockedString(UIStringsNotTranslate.i)}</span>
141
141
  </span>
142
142
  </span>&nbsp;${lockedString(UIStringsNotTranslate.toTurnOnCodeSuggestions)}&nbsp;
143
- <span role="button" class="ai-code-completion-teaser-dismiss" @click=${input.onDismiss}
143
+ <span role="button" class="ai-code-completion-teaser-dismiss"
144
+ tabindex="0"
145
+ @click=${input.onDismiss}
146
+ @keydown=${(e: KeyboardEvent) => {
147
+ // Handle Enter and Space events to make dismiss button accessible for only keyboard users.
148
+ if (e.key === 'Enter' || e.key === ' ') {
149
+ input.onDismiss(e);
150
+ e.stopPropagation();
151
+ e.preventDefault();
152
+ }
153
+ }}
144
154
  jslog=${VisualLogging.action('ai-code-completion-teaser.dismiss').track({click: true})}>
145
155
  ${lockedString(UIStringsNotTranslate.dontShowAgain)}
146
156
  </span>
@@ -26,6 +26,12 @@
26
26
  .ai-code-completion-teaser-dismiss {
27
27
  text-decoration: underline;
28
28
  cursor: pointer;
29
+
30
+ &:focus-visible {
31
+ border-radius: var(--sys-shape-corner-extra-small);
32
+ outline: var(--sys-size-2) solid var(--sys-color-state-focus-ring);
33
+ outline-offset: 0;
34
+ }
29
35
  }
30
36
 
31
37
  .ai-code-completion-teaser-action {
@@ -50,7 +50,7 @@ const UIStrings = {
50
50
  } as const;
51
51
  const str_ = i18n.i18n.registerUIStrings('panels/console/ConsoleSidebar.ts', UIStrings);
52
52
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
53
- const {render, html, nothing, Directives} = Lit;
53
+ const {render, html, nothing} = Lit;
54
54
 
55
55
  export const enum GroupName {
56
56
  CONSOLE_API = 'user message',
@@ -78,18 +78,10 @@ interface ViewInput {
78
78
 
79
79
  export type View = (input: ViewInput, output: object, target: HTMLElement) => void;
80
80
  export const DEFAULT_VIEW: View = (input, output, target) => {
81
- const nodeFilterMap = new WeakMap<Element, ConsoleFilter>();
82
- const onSelectionChanged = (event: UI.TreeOutline.TreeViewElement.SelectEvent): void => {
83
- const filter = nodeFilterMap.get(event.detail);
84
- if (filter) {
85
- input.onSelectionChanged(filter);
86
- }
87
- };
88
81
  render(
89
82
  html`<devtools-tree
90
83
  navigation-variant
91
84
  hide-overflow
92
- @select=${onSelectionChanged}
93
85
  .template=${
94
86
  html`
95
87
  <ul role="tree">
@@ -98,7 +90,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
98
90
  group => html`
99
91
  <li
100
92
  role="treeitem"
101
- ${Directives.ref(element => element && nodeFilterMap.set(element, group.filter))}
93
+ @select=${() => input.onSelectionChanged(group.filter)}
102
94
  ?selected=${group.filter === input.selectedFilter}>
103
95
  <style>${consoleSidebarStyles}</style>
104
96
  <devtools-icon name=${GROUP_ICONS[group.name].icon}></devtools-icon>
@@ -112,7 +104,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
112
104
  <ul role="group" hidden>
113
105
  ${group.urlGroups.values().map(urlGroup => html`
114
106
  <li
115
- ${Directives.ref(element => element && nodeFilterMap.set(element, urlGroup.filter))}
107
+ @select=${() => input.onSelectionChanged(urlGroup.filter)}
116
108
  role="treeitem"
117
109
  ?selected=${urlGroup.filter === input.selectedFilter}
118
110
  title=${urlGroup.url ?? ''}>