chrome-devtools-frontend 1.0.1611390 → 1.0.1613465

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 (53) hide show
  1. package/front_end/Images/src/flowsheet.svg +1 -0
  2. package/front_end/core/common/Color.ts +3 -2
  3. package/front_end/core/host/AidaClientTypes.ts +5 -0
  4. package/front_end/core/host/AidaGcaTranslation.ts +1 -0
  5. package/front_end/core/platform/StringUtilities.ts +5 -0
  6. package/front_end/core/sdk/NetworkManager.ts +7 -0
  7. package/front_end/core/sdk/ResourceTreeModel.ts +2 -1
  8. package/front_end/core/sdk/Target.ts +1 -3
  9. package/front_end/generated/InspectorBackendCommands.ts +3 -3
  10. package/front_end/generated/SupportedCSSProperties.js +4 -0
  11. package/front_end/generated/protocol.ts +12 -10
  12. package/front_end/models/greendev/Prototypes.ts +7 -4
  13. package/front_end/models/har/HARFormat.ts +30 -0
  14. package/front_end/models/har/Importer.ts +12 -1
  15. package/front_end/models/har/Log.ts +28 -0
  16. package/front_end/models/web_mcp/WebMCPModel.ts +73 -18
  17. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +64 -25
  18. package/front_end/panels/ai_assistance/PatchWidget.ts +7 -23
  19. package/front_end/panels/ai_assistance/SelectWorkspaceDialog.ts +3 -3
  20. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +7 -0
  21. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  22. package/front_end/panels/ai_assistance/components/ChatMessage.ts +21 -11
  23. package/front_end/panels/ai_assistance/components/ChatView.ts +4 -0
  24. package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +20 -10
  25. package/front_end/panels/ai_assistance/components/OptInChangeDialog.ts +179 -0
  26. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +6 -2
  27. package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +1 -1
  28. package/front_end/panels/ai_assistance/components/optInChangeDialog.css +95 -0
  29. package/front_end/panels/ai_assistance/components/walkthroughView.css +19 -18
  30. package/front_end/panels/application/CookieItemsView.ts +3 -2
  31. package/front_end/panels/application/WebMCPView.ts +97 -6
  32. package/front_end/panels/application/webMCPView.css +7 -1
  33. package/front_end/panels/changes/ChangesView.ts +0 -12
  34. package/front_end/panels/common/common.ts +0 -1
  35. package/front_end/panels/console/ConsolePrompt.ts +2 -2
  36. package/front_end/panels/elements/StylePropertyTreeElement.ts +2 -1
  37. package/front_end/panels/elements/StylesSidebarPane.ts +3 -1
  38. package/front_end/panels/elements/elements-meta.ts +14 -0
  39. package/front_end/panels/network/NetworkLogView.ts +58 -28
  40. package/front_end/panels/recorder/RecorderController.ts +4 -4
  41. package/front_end/panels/settings/AISettingsTab.ts +39 -3
  42. package/front_end/panels/settings/SettingsScreen.ts +2 -2
  43. package/front_end/panels/sources/BreakpointEditDialog.ts +6 -0
  44. package/front_end/third_party/chromium/README.chromium +1 -1
  45. package/front_end/ui/components/markdown_view/CodeBlock.ts +6 -0
  46. package/front_end/ui/legacy/SplitWidget.ts +9 -6
  47. package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +3 -3
  48. package/front_end/ui/legacy/components/source_frame/FontView.ts +101 -88
  49. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +4 -4
  50. package/front_end/ui/visual_logging/KnownContextValues.ts +10 -0
  51. package/mcp/mcp.ts +1 -1
  52. package/package.json +1 -1
  53. package/front_end/panels/common/CopyChangesToPrompt.ts +0 -233
@@ -45,6 +45,10 @@ const UIStrings = {
45
45
  * @description Title for the button that hides the walkthrough when there are widgets in the walkthrough.
46
46
  */
47
47
  hideAgentWalkthrough: 'Hide agent walkthrough',
48
+ /**
49
+ * @description Aria label for the spinner to be read by screen reader when a step is in progress.
50
+ */
51
+ inProgress: 'In progress',
48
52
  } as const;
49
53
  const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/components/WalkthroughView.ts', UIStrings);
50
54
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -118,7 +122,7 @@ function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate,
118
122
  <div class="inline-wrapper" ?data-open=${input.isExpanded}>
119
123
  <span class="inline-icon">
120
124
  ${input.isLoading ?
121
- html`<devtools-spinner></devtools-spinner>` :
125
+ html`<devtools-spinner aria-label=${lockedString(UIStrings.inProgress)}></devtools-spinner>` :
122
126
  html`<devtools-icon name=${icon}></devtools-icon>`
123
127
  }
124
128
  </span>
@@ -149,7 +153,7 @@ function renderSidebarWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate
149
153
  return html`
150
154
  <div class="walkthrough-view">
151
155
  <div class="walkthrough-header">
152
- <div class="walkthrough-title">${i18nString(UIStrings.title)}</div>
156
+ <h2 class="walkthrough-title">${i18nString(UIStrings.title)}</h2>
153
157
  <devtools-button
154
158
  .data=${{
155
159
  variant: Buttons.Button.Variant.TOOLBAR,
@@ -21,7 +21,7 @@
21
21
  .export-for-agents-dialog header {
22
22
  margin-bottom: var(--sys-size-6);
23
23
 
24
- h2 {
24
+ h1 {
25
25
  font: var(--sys-typescale-headline5);
26
26
  margin: 0;
27
27
  color: var(--sys-color-on-surface);
@@ -0,0 +1,95 @@
1
+ /*
2
+ * Copyright 2026 The Chromium Authors
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ @scope to (devtools-widget > *) {
8
+ :scope {
9
+ width: 100%;
10
+ box-shadow: none;
11
+ padding: var(--sys-size-8);
12
+ background-color: var(--sys-color-surface);
13
+ border-radius: var(--sys-shape-corner-medium);
14
+ }
15
+
16
+ .opt-in-change-dialog {
17
+ width: var(--sys-size-33);
18
+ max-width: 100%;
19
+ }
20
+
21
+ header {
22
+ display: flex;
23
+ flex-direction: row;
24
+ align-items: center;
25
+ gap: var(--sys-size-8);
26
+ margin-bottom: var(--sys-size-8);
27
+
28
+ h2 {
29
+ margin: 0;
30
+ color: var(--sys-color-on-surface);
31
+ font: var(--sys-typescale-headline5);
32
+ }
33
+
34
+ .header-icon-container {
35
+ background: linear-gradient(
36
+ 135deg,
37
+ var(--sys-color-gradient-primary),
38
+ var(--sys-color-gradient-tertiary)
39
+ );
40
+ border-radius: var(--sys-size-4);
41
+ height: var(--sys-size-14);
42
+ width: var(--sys-size-14);
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+
47
+ devtools-icon {
48
+ width: var(--sys-size-9);
49
+ height: var(--sys-size-9);
50
+ color: var(--sys-color-on-primary);
51
+ }
52
+ }
53
+ }
54
+
55
+ main {
56
+ background-color: var(--sys-color-surface4);
57
+ border-radius: var(--sys-shape-corner-medium-small);
58
+ padding: var(--sys-size-8);
59
+ display: flex;
60
+ flex-direction: column;
61
+ gap: var(--sys-size-6);
62
+ margin-bottom: var(--sys-size-8);
63
+
64
+ .item {
65
+ display: flex;
66
+ flex-direction: row;
67
+ align-items: center;
68
+ gap: var(--sys-size-8);
69
+
70
+ devtools-icon {
71
+ width: var(--sys-size-8);
72
+ height: var(--sys-size-8);
73
+ flex-shrink: 0;
74
+ color: var(--sys-color-on-surface-subtle);
75
+ }
76
+
77
+ .text {
78
+ font: var(--sys-typescale-body4);
79
+ color: var(--sys-color-on-surface);
80
+ }
81
+ }
82
+ }
83
+
84
+ footer {
85
+ display: flex;
86
+ flex-direction: row;
87
+ align-items: center;
88
+ justify-content: flex-end;
89
+
90
+ .right-buttons {
91
+ display: flex;
92
+ gap: var(--sys-size-5);
93
+ }
94
+ }
95
+ }
@@ -99,7 +99,7 @@
99
99
 
100
100
  .inline-wrapper {
101
101
  display: flex;
102
- align-items: center;
102
+ align-items: flex-start;
103
103
  gap: var(--sys-size-2);
104
104
  justify-content: flex-start;
105
105
 
@@ -118,21 +118,9 @@
118
118
  &[open] {
119
119
  border-radius: var(--sys-size-5);
120
120
  width: auto;
121
- background-color: var(--sys-color-surface1);
121
+ background-color: var(--sys-color-surface2);
122
122
  margin-left: calc(var(--sys-size-6) / 2);
123
123
  flex-grow: 1;
124
-
125
- > summary {
126
- border-radius: var(--sys-shape-corner-medium-small);
127
- border-bottom-right-radius: 0;
128
- border-bottom-left-radius: 0;
129
- background: var(--sys-color-surface5);
130
- color: var(--sys-color-on-surface);
131
-
132
- &[data-has-widgets] {
133
- margin-left: 0;
134
- }
135
- }
136
124
  }
137
125
  }
138
126
 
@@ -177,6 +165,23 @@
177
165
  }
178
166
  }
179
167
 
168
+ .walkthrough-inline[open] > summary {
169
+ border-radius: var(--sys-shape-corner-medium-small);
170
+ border-bottom-right-radius: 0;
171
+ border-bottom-left-radius: 0;
172
+ background: var(--sys-color-surface5);
173
+ color: var(--sys-color-on-surface);
174
+
175
+ &[data-has-widgets] {
176
+ margin-left: 0;
177
+ }
178
+
179
+ > devtools-icon[name='chevron-right'] {
180
+ transform: rotate(270deg);
181
+ }
182
+
183
+ }
184
+
180
185
  .walkthrough-inline > summary::-webkit-details-marker {
181
186
  display: none;
182
187
  }
@@ -201,8 +206,4 @@
201
206
  .walkthrough-inline .step {
202
207
  background-color: var(--sys-color-surface5);
203
208
  }
204
-
205
- .walkthrough-inline[open] > summary > devtools-icon[name='chevron-right'] {
206
- transform: rotate(270deg);
207
- }
208
209
  }
@@ -375,9 +375,10 @@ export class CookieItemsView extends UI.Widget.VBox {
375
375
  }
376
376
 
377
377
  deleteSelectedItem(): void {
378
- if (this.selectedCookie) {
378
+ const cookie = this.selectedCookie;
379
+ if (cookie) {
379
380
  this.showPreview(null);
380
- void this.model.deleteCookie(this.selectedCookie);
381
+ void this.model.deleteCookie(cookie);
381
382
  }
382
383
  }
383
384
 
@@ -433,19 +433,23 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
433
433
  @click=${() => input.onCallSelect(null)}
434
434
  ></devtools-button>
435
435
  <devtools-widget
436
- id="details"
436
+ id="webmcp.tool-details"
437
437
  title=${i18nString(UIStrings.toolDetails)}
438
438
  ${widget(ToolDetailsWidget, {tool: input.selectedCall?.tool})}>
439
439
  </devtools-widget>
440
440
  <devtools-widget
441
- id="inputs"
441
+ id="webmcp.call-inputs"
442
442
  title=${i18nString(UIStrings.input)}
443
443
  ${widget(PayloadWidget, parsePayload(input.selectedCall?.input))}>
444
444
  </devtools-widget>
445
445
  <devtools-widget
446
- id="outputs"
446
+ id="webmcp.call-outputs"
447
447
  title=${i18nString(UIStrings.output)}
448
- ${widget(PayloadWidget, parsePayload(input.selectedCall?.result?.output))}>
448
+ ${widget(PayloadWidget, {
449
+ valueObject: input.selectedCall?.result?.output,
450
+ errorText: input.selectedCall?.result?.errorText,
451
+ exceptionDetails: input.selectedCall?.result?.exceptionDetails,
452
+ })}>
449
453
  </devtools-widget>
450
454
  </devtools-tabbed-pane>
451
455
  </div>
@@ -685,10 +689,12 @@ export class WebMCPView extends UI.Widget.VBox {
685
689
  export interface PayloadViewInput {
686
690
  valueObject?: unknown;
687
691
  valueString?: string;
692
+ errorText?: string;
693
+ exceptionDetails?: WebMCP.WebMCPModel.ExceptionDetails;
688
694
  }
689
695
 
690
696
  export const PAYLOAD_DEFAULT_VIEW = (input: PayloadViewInput, output: object, target: HTMLElement): void => {
691
- if (input.valueObject === undefined && input.valueString === undefined) {
697
+ if (!input.valueObject && !input.valueString && !input.errorText && !input.exceptionDetails) {
692
698
  render(nothing, target);
693
699
  return;
694
700
  }
@@ -715,6 +721,52 @@ export const PAYLOAD_DEFAULT_VIEW = (input: PayloadViewInput, output: object, ta
715
721
  };
716
722
 
717
723
  const createSourceText = (text: string): TemplateResult => html`<div class="payload-value source-code">${text}</div>`;
724
+ const createErrorText = (text: string): TemplateResult =>
725
+ html`<div class="payload-value source-code error-text">${text}</div>`;
726
+
727
+ const createException = (
728
+ details: WebMCP.WebMCPModel.ExceptionDetails,
729
+ linkifier: Components.Linkifier.Linkifier = new Components.Linkifier.Linkifier(),
730
+ ): TemplateResult => {
731
+ const renderFrame = (
732
+ frame: StackTrace.ErrorStackParser.ParsedErrorFrame,
733
+ index: number,
734
+ array: StackTrace.ErrorStackParser.ParsedErrorFrame[],
735
+ ): TemplateResult => {
736
+ const newline = index < array.length - 1 ? '\n' : '';
737
+ const {line, link, isCallFrame} = frame;
738
+
739
+ if (!isCallFrame) {
740
+ return html`<span>${line}${newline}</span>`;
741
+ }
742
+
743
+ if (!link) {
744
+ return html`<span class="formatted-builtin-stack-frame">${line}${newline}</span>`;
745
+ }
746
+
747
+ const scriptLocationLink = linkifier.linkifyScriptLocation(
748
+ details.error.runtimeModel().target(),
749
+ link.scriptId || null,
750
+ link.url,
751
+ link.lineNumber,
752
+ {
753
+ columnNumber: link.columnNumber,
754
+ inlineFrameIndex: 0,
755
+ showColumnNumber: true,
756
+ },
757
+ );
758
+ scriptLocationLink.tabIndex = -1;
759
+
760
+ return html`<span class="formatted-stack-frame">${link.prefix}${scriptLocationLink}${link.suffix}${
761
+ newline}</span>`;
762
+ };
763
+
764
+ return html`
765
+ <div class="payload-value source-code error-text">
766
+ ${details.frames.length === 0 && details.description ? html`<span>${details.description}\n</span>` : nothing}
767
+ <div>${details.frames.map(renderFrame)}</div>
768
+ ${details.cause ? html`\nCaused by:\n${createException(details.cause, linkifier)}` : nothing}</div>`;
769
+ };
718
770
 
719
771
  render(
720
772
  html`
@@ -723,7 +775,10 @@ export const PAYLOAD_DEFAULT_VIEW = (input: PayloadViewInput, output: object, ta
723
775
  <div class="call-payload-content">
724
776
  ${
725
777
  isParsable ? createPayload(input.valueObject) :
726
- (input.valueString !== undefined ? createSourceText(input.valueString) : nothing)}
778
+ (input.valueString !== undefined ?
779
+ createSourceText(input.valueString) :
780
+ (input.exceptionDetails ? createException(input.exceptionDetails) :
781
+ (input.errorText ? createErrorText(input.errorText) : nothing)))}
727
782
  </div>
728
783
  </div>
729
784
  `,
@@ -733,6 +788,9 @@ export const PAYLOAD_DEFAULT_VIEW = (input: PayloadViewInput, output: object, ta
733
788
  export class PayloadWidget extends UI.Widget.Widget {
734
789
  #valueObject?: unknown;
735
790
  #valueString?: string;
791
+ #errorText?: string;
792
+ #exceptionDetailsPromise?: Promise<WebMCP.WebMCPModel.ExceptionDetails|undefined>;
793
+ #exceptionDetails?: WebMCP.WebMCPModel.ExceptionDetails;
736
794
  #view: typeof PAYLOAD_DEFAULT_VIEW;
737
795
 
738
796
  constructor(element?: HTMLElement, view = PAYLOAD_DEFAULT_VIEW) {
@@ -758,6 +816,37 @@ export class PayloadWidget extends UI.Widget.Widget {
758
816
  return this.#valueString;
759
817
  }
760
818
 
819
+ set errorText(errorText: string|undefined) {
820
+ this.#errorText = errorText;
821
+ this.requestUpdate();
822
+ }
823
+
824
+ get errorText(): string|undefined {
825
+ return this.#errorText;
826
+ }
827
+
828
+ async #updateExceptionDetails(
829
+ exceptionDetailsPromise: Promise<WebMCP.WebMCPModel.ExceptionDetails|undefined>|undefined): Promise<void> {
830
+ if (this.#exceptionDetailsPromise === exceptionDetailsPromise) {
831
+ return;
832
+ }
833
+ this.#exceptionDetailsPromise = exceptionDetailsPromise;
834
+ this.#exceptionDetails = undefined;
835
+ this.requestUpdate();
836
+ const exceptionDetails = await exceptionDetailsPromise;
837
+ if (this.#exceptionDetailsPromise === exceptionDetailsPromise) {
838
+ this.#exceptionDetails = exceptionDetails;
839
+ this.requestUpdate();
840
+ }
841
+ }
842
+
843
+ set exceptionDetails(exceptionDetailsPromise: Promise<WebMCP.WebMCPModel.ExceptionDetails|undefined>|undefined) {
844
+ void this.#updateExceptionDetails(exceptionDetailsPromise);
845
+ }
846
+
847
+ get exceptionDetails(): Promise<WebMCP.WebMCPModel.ExceptionDetails|undefined>|undefined {
848
+ return this.#exceptionDetailsPromise;
849
+ }
761
850
  override wasShown(): void {
762
851
  super.wasShown();
763
852
  this.requestUpdate();
@@ -767,6 +856,8 @@ export class PayloadWidget extends UI.Widget.Widget {
767
856
  const input: PayloadViewInput = {
768
857
  valueObject: this.#valueObject,
769
858
  valueString: this.#valueString,
859
+ errorText: this.#errorText,
860
+ exceptionDetails: this.#exceptionDetails,
770
861
  };
771
862
  this.#view(input, {}, this.contentElement);
772
863
  }
@@ -48,7 +48,8 @@
48
48
  background-color: var(--sys-color-tonal-container);
49
49
  }
50
50
 
51
- tr.selected.status-error {
51
+ tbody tr.selected.status-error,
52
+ tbody tr.selected.status-error.revealed {
52
53
  background-color: var(--sys-color-error-container);
53
54
  color: var(--sys-color-error);
54
55
  }
@@ -210,4 +211,9 @@
210
211
  flex: auto;
211
212
  overflow: auto;
212
213
  }
214
+
215
+ .payload-value.error-text {
216
+ color: var(--sys-color-error);
217
+ white-space: pre-wrap;
218
+ }
213
219
  }
@@ -6,13 +6,11 @@ import '../../ui/legacy/legacy.js';
6
6
 
7
7
  import * as i18n from '../../core/i18n/i18n.js';
8
8
  import type * as Platform from '../../core/platform/platform.js';
9
- import * as GreenDev from '../../models/greendev/greendev.js';
10
9
  import type * as Workspace from '../../models/workspace/workspace.js';
11
10
  import * as WorkspaceDiff from '../../models/workspace_diff/workspace_diff.js';
12
11
  import * as UI from '../../ui/legacy/legacy.js';
13
12
  import * as Lit from '../../ui/lit/lit.js';
14
13
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
15
- import * as PanelsCommon from '../common/common.js';
16
14
 
17
15
  import {ChangesSidebar, Events} from './ChangesSidebar.js';
18
16
  import changesViewStyles from './changesView.css.js';
@@ -48,8 +46,6 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
48
46
  Events.SELECTED_UI_SOURCE_CODE_CHANGED, () => input.onSelect(sidebar.selectedUISourceCode()));
49
47
  };
50
48
 
51
- const hasCopyToPrompt = GreenDev.Prototypes.instance().isEnabled('copyToGemini');
52
-
53
49
  render(
54
50
  // clang-format off
55
51
  html`
@@ -70,14 +66,6 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
70
66
  workspaceDiff: input.workspaceDiff
71
67
  })}
72
68
  </div>
73
- ${hasCopyToPrompt ? html`
74
- <devtools-widget class="copy-to-prompt"
75
- ${widget(PanelsCommon.CopyChangesToPrompt, {
76
- workspaceDiff: input.workspaceDiff,
77
- patchAgentCSSChange: null,
78
- })}
79
- ></devtools-widget>
80
- ` : Lit.nothing}
81
69
  </div>
82
70
  <devtools-widget slot="sidebar" ${widget(ChangesSidebar, {workspaceDiff: input.workspaceDiff})}
83
71
  ${UI.Widget.widgetRef(ChangesSidebar, onSidebar)}>
@@ -111,4 +111,3 @@ export * as PersistenceUtils from './PersistenceUtils.js';
111
111
  export * as DOMLinkifier from './DOMLinkifier.js';
112
112
  export * as ThrottlingUtils from './ThrottlingUtils.js';
113
113
  export * as ExtensionIframe from './ExtensionView.js';
114
- export {CopyChangesToPrompt} from './CopyChangesToPrompt.js';
@@ -29,11 +29,11 @@ const UIStrings = {
29
29
  */
30
30
  consolePrompt: 'Console prompt',
31
31
  /**
32
- * @description Warning shown to users when pasting text into the DevTools console.
32
+ * @description Warning shown to users when pasting text into the DevTools console. IMPORTANT: keep double quotes around PH1 and do not use single quotes.
33
33
  * @example {allow pasting} PH1
34
34
  */
35
35
  selfXssWarning:
36
- 'Warning: Don’t paste code into the DevTools Console that you don’t understand or haven’t reviewed yourself. This could allow attackers to steal your identity or take control of your computer. Please type {PH1} below and press Enter to allow pasting.',
36
+ 'Warning: Don’t paste code into the DevTools Console that you don’t understand or haven’t reviewed yourself. This could allow attackers to steal your identity or take control of your computer. Please type {PH1} below and press Enter to allow pasting.',
37
37
  /**
38
38
  * @description Text a user needs to type in order to confirm that they are aware of the danger of pasting code into the DevTools console.
39
39
  */
@@ -2782,7 +2782,8 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
2782
2782
  existingElement?.remove();
2783
2783
  }
2784
2784
 
2785
- if (!this.overriddenByAnimation() || UI.ViewManager.ViewManager.instance().isViewVisible('animations')) {
2785
+ if (!this.overriddenByAnimation() || UI.ViewManager.ViewManager.instance().isViewVisible('animations') ||
2786
+ !Common.Settings.Settings.instance().moduleSetting('css-animations-only-when-animations-tab-open').get()) {
2786
2787
  return;
2787
2788
  }
2788
2789
 
@@ -1096,10 +1096,12 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1096
1096
  // the matched styles we reenable the button.
1097
1097
  LayersWidget.ButtonProvider.instance().item().setVisible(false);
1098
1098
  const animationsPanelVisible = UI.ViewManager.ViewManager.instance().isViewVisible('animations');
1099
+ const cssAnimationsOnlyWhenAnimationsTabOpen =
1100
+ Common.Settings.Settings.instance().moduleSetting('css-animations-only-when-animations-tab-open').get();
1099
1101
  for (const style of matchedStyles.nodeStyles()) {
1100
1102
  const isTransitionOrAnimationStyle = style.type === SDK.CSSStyleDeclaration.Type.Transition ||
1101
1103
  style.type === SDK.CSSStyleDeclaration.Type.Animation;
1102
- if (isTransitionOrAnimationStyle && !animationsPanelVisible) {
1104
+ if (isTransitionOrAnimationStyle && cssAnimationsOnlyWhenAnimationsTabOpen && !animationsPanelVisible) {
1103
1105
  continue;
1104
1106
  }
1105
1107
 
@@ -163,6 +163,10 @@ const UIStrings = {
163
163
  * @description Command for toggling the eye dropper when the color picker is open
164
164
  */
165
165
  toggleEyeDropper: 'Toggle eye dropper',
166
+ /**
167
+ * @description Title of a setting under the Elements category.
168
+ */
169
+ cssAnimationsOnlyWhenAnimationsTabOpen: 'Show animation styles only when the Animations tab is open',
166
170
  } as const;
167
171
  const str_ = i18n.i18n.registerUIStrings('panels/elements/elements-meta.ts', UIStrings);
168
172
  const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
@@ -569,6 +573,16 @@ Common.Settings.registerSettingExtension({
569
573
  defaultValue: true,
570
574
  });
571
575
 
576
+ Common.Settings.registerSettingExtension({
577
+ category: Common.Settings.SettingCategory.ELEMENTS,
578
+ storageType: Common.Settings.SettingStorageType.SYNCED,
579
+ order: 6,
580
+ title: i18nLazyString(UIStrings.cssAnimationsOnlyWhenAnimationsTabOpen),
581
+ settingName: 'css-animations-only-when-animations-tab-open',
582
+ settingType: Common.Settings.SettingType.BOOLEAN,
583
+ defaultValue: true,
584
+ });
585
+
572
586
  Common.Settings.registerSettingExtension({
573
587
  settingName: 'show-event-listeners-for-ancestors',
574
588
  settingType: Common.Settings.SettingType.BOOLEAN,
@@ -1880,10 +1880,30 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
1880
1880
  const throttlingMenu =
1881
1881
  contextMenu.debugSection().appendSubMenuItem(i18nString(UIStrings.throttleRequests), /* disabled=*/ true);
1882
1882
 
1883
- const urlWithoutScheme = request.parsedURL.urlWithoutScheme();
1884
- const urlPattern = urlWithoutScheme &&
1883
+ const parsed = request.parsedURL;
1884
+ let urlPatternString = '';
1885
+ if (parsed.isValid) {
1886
+ // We cannot escape parsed.urlWithoutScheme() directly. If we escaped the first '?'
1887
+ // URLPattern would treat the entire string as the pathname and fail to match the query string.
1888
+ // Thus, we must escape the components individually and reconstruct the pattern string.
1889
+ urlPatternString = '*://' + Platform.StringUtilities.escapeForURLPattern(parsed.host);
1890
+ if (parsed.port) {
1891
+ urlPatternString += ':' + Platform.StringUtilities.escapeForURLPattern(parsed.port);
1892
+ }
1893
+ urlPatternString += Platform.StringUtilities.escapeForURLPattern(parsed.path);
1894
+ if (parsed.queryParams) {
1895
+ urlPatternString += '?' + Platform.StringUtilities.escapeForURLPattern(parsed.queryParams);
1896
+ }
1897
+ if (parsed.fragment) {
1898
+ urlPatternString += '#' + Platform.StringUtilities.escapeForURLPattern(parsed.fragment);
1899
+ }
1900
+ } else if (parsed.urlWithoutScheme()) {
1901
+ urlPatternString = '*://' + Platform.StringUtilities.escapeForURLPattern(parsed.urlWithoutScheme());
1902
+ }
1903
+
1904
+ const urlPattern = urlPatternString &&
1885
1905
  SDK.NetworkManager.RequestURLPattern.create(
1886
- `*://${urlWithoutScheme}` as SDK.NetworkManager.URLPatternConstructorString);
1906
+ urlPatternString as SDK.NetworkManager.URLPatternConstructorString);
1887
1907
  if (urlPattern) {
1888
1908
  throttlingMenu.setEnabled(true);
1889
1909
  blockingMenu.setEnabled(true);
@@ -1906,30 +1926,39 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
1906
1926
  {jslogContext: 'throttle-request-url'});
1907
1927
  }
1908
1928
 
1909
- const domain = request.parsedURL.domain();
1910
- const domainPattern = domain &&
1911
- SDK.NetworkManager.RequestURLPattern.create(
1912
- `*://${domain}` as SDK.NetworkManager.URLPatternConstructorString);
1913
- if (domainPattern) {
1914
- throttlingMenu.setEnabled(true);
1915
- blockingMenu.setEnabled(true);
1916
- const existingConditions = manager.requestConditions.findCondition(domainPattern.constructorString);
1917
- const isBlocking = existingConditions?.conditions === SDK.NetworkManager.BlockingConditions;
1918
- const isThrottling = existingConditions &&
1919
- existingConditions.conditions !== SDK.NetworkManager.BlockingConditions &&
1920
- existingConditions.conditions !== SDK.NetworkManager.NoThrottlingConditions;
1921
- const croppedURL = Platform.StringUtilities.trimMiddle(domainPattern.constructorString, maxBlockedURLLength);
1922
- blockingMenu.debugSection().appendItem(
1923
- isBlocking ? i18nString(UIStrings.unblockS, {PH1: croppedURL}) : i18nString(UIStrings.blockRequestDomain),
1924
- () => isBlocking ? removeRequestCondition(domainPattern) :
1925
- addRequestCondition(domainPattern, SDK.NetworkManager.BlockingConditions),
1926
- {jslogContext: 'block-request-domain'});
1927
- throttlingMenu.debugSection().appendItem(
1928
- isThrottling ? i18nString(UIStrings.unthrottleS, {PH1: croppedURL}) :
1929
- i18nString(UIStrings.throttleRequestDomain),
1930
- () => isThrottling ? removeRequestCondition(domainPattern) :
1931
- addRequestCondition(domainPattern, SDK.NetworkManager.Slow3GConditions),
1932
- {jslogContext: 'throttle-request-domain'});
1929
+ let domainPatternString = '';
1930
+ if (parsed.isValid) {
1931
+ domainPatternString = '*://' + Platform.StringUtilities.escapeForURLPattern(parsed.host);
1932
+ if (parsed.port) {
1933
+ domainPatternString += ':' + Platform.StringUtilities.escapeForURLPattern(parsed.port);
1934
+ }
1935
+ } else if (parsed.domain()) {
1936
+ domainPatternString = '*://' + Platform.StringUtilities.escapeForURLPattern(parsed.domain());
1937
+ }
1938
+
1939
+ const domainPattern = domainPatternString &&
1940
+ SDK.NetworkManager.RequestURLPattern.create(
1941
+ domainPatternString as SDK.NetworkManager.URLPatternConstructorString);
1942
+ if (domainPattern) {
1943
+ throttlingMenu.setEnabled(true);
1944
+ blockingMenu.setEnabled(true);
1945
+ const existingConditions = manager.requestConditions.findCondition(domainPattern.constructorString);
1946
+ const isBlocking = existingConditions?.conditions === SDK.NetworkManager.BlockingConditions;
1947
+ const isThrottling = existingConditions &&
1948
+ existingConditions.conditions !== SDK.NetworkManager.BlockingConditions &&
1949
+ existingConditions.conditions !== SDK.NetworkManager.NoThrottlingConditions;
1950
+ const croppedURL = Platform.StringUtilities.trimMiddle(domainPattern.constructorString, maxBlockedURLLength);
1951
+ blockingMenu.debugSection().appendItem(
1952
+ isBlocking ? i18nString(UIStrings.unblockS, {PH1: croppedURL}) : i18nString(UIStrings.blockRequestDomain),
1953
+ () => isBlocking ? removeRequestCondition(domainPattern) :
1954
+ addRequestCondition(domainPattern, SDK.NetworkManager.BlockingConditions),
1955
+ {jslogContext: 'block-request-domain'});
1956
+ throttlingMenu.debugSection().appendItem(
1957
+ isThrottling ? i18nString(UIStrings.unthrottleS, {PH1: croppedURL}) :
1958
+ i18nString(UIStrings.throttleRequestDomain),
1959
+ () => isThrottling ? removeRequestCondition(domainPattern) :
1960
+ addRequestCondition(domainPattern, SDK.NetworkManager.Slow3GConditions),
1961
+ {jslogContext: 'throttle-request-domain'});
1933
1962
  }
1934
1963
 
1935
1964
  if (SDK.NetworkManager.NetworkManager.canReplayRequest(request)) {
@@ -1944,7 +1973,8 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
1944
1973
  const requests = Logs.NetworkLog.NetworkLog.instance().requests().filter(request => this.applyFilter(request));
1945
1974
  return requests.filter(NetworkLogView.getHTTPRequestsFilter).filter(request => {
1946
1975
  return request.finished ||
1947
- (request.resourceType() === Common.ResourceType.resourceTypes.WebSocket && request.responseReceivedTime);
1976
+ (request.resourceType() === Common.ResourceType.resourceTypes.WebSocket && request.responseReceivedTime) ||
1977
+ Boolean(request.eventSourceMessages()?.length);
1948
1978
  });
1949
1979
  }
1950
1980
 
@@ -133,21 +133,21 @@ const UIStrings = {
133
133
  */
134
134
  doYouTrustThisCode: 'Do you trust this recording?',
135
135
  /**
136
- * @description Warning shown to users when imports code into DevTools Recorder.
136
+ * @description Warning shown to users when imports code into DevTools Recorder. IMPORTANT: keep double quotes around PH1 and do not use single quotes.
137
137
  * @example {allow importing} PH1
138
138
  */
139
139
  doNotImport:
140
- 'Don\'t import recordings you do not understand or have not reviewed yourself into DevTools. This could allow attackers to steal your identity or take control of your computer. Please type \'\'{PH1}\'\' below to allow importing.',
140
+ 'Don\'t import recordings you do not understand or have not reviewed yourself into DevTools. This could allow attackers to steal your identity or take control of your computer. Please type {PH1} below to allow importing.',
141
141
  /**
142
142
  * @description Text a user needs to type in order to confirm that they
143
143
  *are aware of the danger of import code into the DevTools Recorder.
144
144
  */
145
145
  allowImporting: 'allow importing',
146
146
  /**
147
- * @description Input box placeholder which instructs the user to type 'allow importing' into the input box.
147
+ * @description Input box placeholder which instructs the user to type 'allow importing' into the input box. IMPORTANT: keep double quotes around PH1 and do not use single quotes.
148
148
  * @example {allow importing} PH1
149
149
  */
150
- typeAllowImporting: 'Type \'\'{PH1}\'\'',
150
+ typeAllowImporting: 'Type {PH1}',
151
151
  } as const;
152
152
  const str_ = i18n.i18n.registerUIStrings('panels/recorder/RecorderController.ts', UIStrings);
153
153
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);