chrome-devtools-frontend 1.0.1699061 → 1.0.1700561

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 (120) hide show
  1. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -2
  2. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +3 -0
  3. package/front_end/core/sdk/StorageBucketsModel.ts +2 -0
  4. package/front_end/entrypoints/main/GlobalAiButton.ts +2 -6
  5. package/front_end/entrypoints/main/MainImpl.ts +8 -1
  6. package/front_end/generated/InspectorBackendCommands.ts +12 -2
  7. package/front_end/generated/SupportedCSSProperties.ts +14 -0
  8. package/front_end/generated/protocol-mapping.d.ts +48 -1
  9. package/front_end/generated/protocol-proxy-api.d.ts +37 -1
  10. package/front_end/generated/protocol.ts +131 -0
  11. package/front_end/models/bindings/NetworkProject.ts +30 -0
  12. package/front_end/models/bindings/ResourceScriptMapping.ts +62 -0
  13. package/front_end/models/change_tracker/ChangeTracker.ts +1 -1
  14. package/front_end/models/comment_manager/CD4ABridge.ts +2 -2
  15. package/front_end/models/comment_manager/CommentManager.ts +18 -0
  16. package/front_end/models/extensions/ExtensionAPI.ts +4 -2
  17. package/front_end/models/issues_manager/CookieIssue.ts +0 -2
  18. package/front_end/models/persistence/NetworkPersistenceManager.ts +29 -4
  19. package/front_end/models/source_map_scopes/NamesResolver.ts +23 -1
  20. package/front_end/models/source_map_scopes/ScopeChainModel.ts +21 -1
  21. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +5 -8
  22. package/front_end/panels/application/AppManifestView.ts +128 -134
  23. package/front_end/panels/application/CrashReportContextView.ts +5 -5
  24. package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +17 -17
  25. package/front_end/panels/application/DeviceBoundSessionsView.ts +136 -137
  26. package/front_end/panels/application/IndexedDBModel.ts +3 -0
  27. package/front_end/panels/application/OriginTrialTreeView.ts +18 -23
  28. package/front_end/panels/application/ReportingApiTreeElement.ts +3 -3
  29. package/front_end/panels/application/ReportingApiView.ts +6 -14
  30. package/front_end/panels/application/application-meta.ts +7 -7
  31. package/front_end/panels/common/CommentThreadWidget.ts +19 -6
  32. package/front_end/panels/common/CommentsOverlayWidget.ts +115 -9
  33. package/front_end/panels/common/CommentsStatusBarPill.ts +13 -1
  34. package/front_end/panels/common/ExtensionServer.ts +43 -34
  35. package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
  36. package/front_end/panels/common/commentThreadWidget.css +18 -0
  37. package/front_end/panels/common/common-meta.ts +4 -1
  38. package/front_end/panels/elements/ElementsTreeElement.ts +2 -0
  39. package/front_end/panels/elements/ElementsTreeOutline.ts +11 -7
  40. package/front_end/panels/elements/elementsTreeOutline.css +4 -0
  41. package/front_end/panels/event_listeners/EventListenersView.ts +9 -4
  42. package/front_end/panels/issues/IssuesPane.ts +34 -22
  43. package/front_end/panels/network/RequestPayloadView.ts +17 -15
  44. package/front_end/panels/recorder/RecordingView.ts +32 -53
  45. package/front_end/panels/recorder/controlButton.css +6 -0
  46. package/front_end/panels/recorder/recordingView.css +11 -2
  47. package/front_end/panels/sources/DebuggerPlugin.ts +136 -55
  48. package/front_end/panels/sources/ScopeChainSidebarPane.ts +17 -15
  49. package/front_end/panels/timeline/components/IgnoreListSetting.ts +0 -2
  50. package/front_end/panels/timeline/components/LiveMetricsView.ts +59 -64
  51. package/front_end/panels/timeline/components/liveMetricsView.css +1 -0
  52. package/front_end/third_party/chromium/README.chromium +1 -1
  53. package/front_end/third_party/lighthouse/README.chromium +2 -2
  54. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1580 -1524
  55. package/front_end/third_party/lighthouse/locales/ar-XB.json +111 -72
  56. package/front_end/third_party/lighthouse/locales/ar.json +113 -74
  57. package/front_end/third_party/lighthouse/locales/bg.json +112 -73
  58. package/front_end/third_party/lighthouse/locales/ca.json +112 -73
  59. package/front_end/third_party/lighthouse/locales/cs.json +112 -73
  60. package/front_end/third_party/lighthouse/locales/da.json +112 -73
  61. package/front_end/third_party/lighthouse/locales/de.json +112 -73
  62. package/front_end/third_party/lighthouse/locales/el.json +112 -73
  63. package/front_end/third_party/lighthouse/locales/en-GB.json +111 -72
  64. package/front_end/third_party/lighthouse/locales/en-US.json +102 -66
  65. package/front_end/third_party/lighthouse/locales/en-XA.json +111 -72
  66. package/front_end/third_party/lighthouse/locales/en-XL.json +102 -66
  67. package/front_end/third_party/lighthouse/locales/es-419.json +112 -73
  68. package/front_end/third_party/lighthouse/locales/es.json +112 -73
  69. package/front_end/third_party/lighthouse/locales/fi.json +112 -73
  70. package/front_end/third_party/lighthouse/locales/fil.json +112 -73
  71. package/front_end/third_party/lighthouse/locales/fr.json +112 -73
  72. package/front_end/third_party/lighthouse/locales/he.json +112 -73
  73. package/front_end/third_party/lighthouse/locales/hi.json +112 -73
  74. package/front_end/third_party/lighthouse/locales/hr.json +112 -73
  75. package/front_end/third_party/lighthouse/locales/hu.json +112 -73
  76. package/front_end/third_party/lighthouse/locales/id.json +112 -73
  77. package/front_end/third_party/lighthouse/locales/it.json +113 -74
  78. package/front_end/third_party/lighthouse/locales/ja.json +112 -73
  79. package/front_end/third_party/lighthouse/locales/ko.json +112 -73
  80. package/front_end/third_party/lighthouse/locales/lt.json +112 -73
  81. package/front_end/third_party/lighthouse/locales/lv.json +112 -73
  82. package/front_end/third_party/lighthouse/locales/nl.json +112 -73
  83. package/front_end/third_party/lighthouse/locales/no.json +112 -73
  84. package/front_end/third_party/lighthouse/locales/pl.json +112 -73
  85. package/front_end/third_party/lighthouse/locales/pt-PT.json +112 -73
  86. package/front_end/third_party/lighthouse/locales/pt.json +112 -73
  87. package/front_end/third_party/lighthouse/locales/ro.json +112 -73
  88. package/front_end/third_party/lighthouse/locales/ru.json +112 -73
  89. package/front_end/third_party/lighthouse/locales/sk.json +112 -73
  90. package/front_end/third_party/lighthouse/locales/sl.json +112 -73
  91. package/front_end/third_party/lighthouse/locales/sr-Latn.json +112 -73
  92. package/front_end/third_party/lighthouse/locales/sr.json +112 -73
  93. package/front_end/third_party/lighthouse/locales/sv.json +124 -85
  94. package/front_end/third_party/lighthouse/locales/ta.json +112 -73
  95. package/front_end/third_party/lighthouse/locales/te.json +112 -73
  96. package/front_end/third_party/lighthouse/locales/th.json +112 -73
  97. package/front_end/third_party/lighthouse/locales/tr.json +113 -74
  98. package/front_end/third_party/lighthouse/locales/uk.json +112 -73
  99. package/front_end/third_party/lighthouse/locales/vi.json +112 -73
  100. package/front_end/third_party/lighthouse/locales/zh-HK.json +112 -73
  101. package/front_end/third_party/lighthouse/locales/zh-TW.json +113 -74
  102. package/front_end/third_party/lighthouse/locales/zh.json +113 -74
  103. package/front_end/third_party/lighthouse/report/bundle.d.ts +4 -4
  104. package/front_end/third_party/lighthouse/report/bundle.js +10 -3
  105. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  106. package/front_end/third_party/lit/lit.ts +3 -0
  107. package/front_end/third_party/lit/package.json +3 -0
  108. package/front_end/third_party/lit/rebuild.sh +3 -2
  109. package/front_end/ui/comments/CommentAnchorResolver.ts +8 -2
  110. package/front_end/ui/comments/CommentOverlayManager.ts +2 -1
  111. package/front_end/ui/legacy/InspectorView.ts +4 -3
  112. package/front_end/ui/legacy/TabbedPane.ts +121 -80
  113. package/front_end/ui/legacy/Treeoutline.ts +35 -6
  114. package/front_end/ui/legacy/UIUtils.ts +7 -0
  115. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +135 -146
  116. package/front_end/ui/lit/lit.ts +1 -1
  117. package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
  118. package/inspector_overlay/highlight_anchor.ts +197 -0
  119. package/inspector_overlay/tool_highlight.ts +8 -0
  120. package/package.json +1 -1
@@ -428,6 +428,9 @@ export class IndexedDBModel extends SDK.SDKModel.SDKModel<EventTypes> implements
428
428
 
429
429
  storageBucketDeleted(_event: Protocol.Storage.StorageBucketDeletedEvent): void {
430
430
  }
431
+
432
+ privateVerificationTokensUpdated(): void {
433
+ }
431
434
  }
432
435
 
433
436
  SDK.SDKModel.SDKModel.register(IndexedDBModel, {capabilities: SDK.Target.Capability.STORAGE, autostart: false});
@@ -20,53 +20,48 @@ const {widget} = UI.Widget;
20
20
 
21
21
  const UIStrings = {
22
22
  /**
23
- * @description Label for the 'origin' field in a parsed Origin Trial Token.
23
+ * @description Label for the origin field in an origin trial token.
24
24
  */
25
25
  origin: 'Origin',
26
26
  /**
27
- * @description Label for `trialName` field in a parsed Origin Trial Token.
28
- * This field is only shown when token has unknown trial name as the token
29
- * will be put into 'UNKNOWN' group.
27
+ * @description Label for the trial name field in an origin trial token.
30
28
  */
31
- trialName: 'Trial Name',
29
+ trialName: 'Trial name',
32
30
  /**
33
- * @description Label for `expiryTime` field in a parsed Origin Trial Token.
31
+ * @description Label for the expiry time field in an origin trial token.
34
32
  */
35
- expiryTime: 'Expiry Time',
33
+ expiryTime: 'Expiry time',
36
34
  /**
37
- * @description Label for `usageRestriction` field in a parsed Origin Trial Token.
35
+ * @description Label for the usage restriction field in an origin trial token.
38
36
  */
39
- usageRestriction: 'Usage Restriction',
37
+ usageRestriction: 'Usage restriction',
40
38
  /**
41
- * @description Label for `isThirdParty` field in a parsed Origin Trial Token.
39
+ * @description Label for the third party field in an origin trial token.
42
40
  */
43
- isThirdParty: 'Third Party',
41
+ isThirdParty: 'Third party',
44
42
  /**
45
- * @description Label for a field containing info about an Origin Trial Token's `matchSubDomains` field.
46
- *An Origin Trial Token contains an origin URL. The `matchSubDomains` field describes whether the token
47
- *only applies to the origin URL or to all subdomains of the origin URL as well.
48
- *The field contains either 'true' or 'false'.
43
+ * @description Label for the subdomain matching field in an origin trial token.
49
44
  */
50
- matchSubDomains: 'Subdomain Matching',
45
+ matchSubDomains: 'Subdomain matching',
51
46
  /**
52
- * @description Label for the raw(= encoded / not human-readable) Origin Trial Token.
47
+ * @description Label for the raw token text in an origin trial token.
53
48
  */
54
- rawTokenText: 'Raw Token',
49
+ rawTokenText: 'Raw token',
55
50
  /**
56
- * @description Label for `status` field in an Origin Trial Token.
51
+ * @description Label for the token status field in an origin trial token.
57
52
  */
58
- status: 'Token Status',
53
+ status: 'Token status',
59
54
  /**
60
- * @description Label for tokenWithStatus node.
55
+ * @description Label for a token node in the origin trials tree view.
61
56
  */
62
57
  token: 'Token',
63
58
  /**
64
- * @description Label for a badge showing the number of Origin Trial Tokens. This number is always greater than 1.
59
+ * @description Label for a badge showing the number of origin trial tokens.
65
60
  * @example {2} PH1
66
61
  */
67
62
  tokens: '{PH1} tokens',
68
63
  /**
69
- * @description Label shown when there are no Origin Trial Tokens in the Frame view of the Application panel.
64
+ * @description Label shown when there are no origin trial tokens in the frame details view of the Application panel.
70
65
  */
71
66
  noTrialTokens: 'No trial tokens',
72
67
  } as const;
@@ -14,13 +14,13 @@ import type {ResourcesPanel} from './ResourcesPanel.js';
14
14
 
15
15
  const UIStrings = {
16
16
  /**
17
- * @description Label for an item in the Application Panel Sidebar of the Application panel
17
+ * @description Label for an item in the sidebar of the Application panel.
18
18
  */
19
19
  reportingApi: 'Reporting API',
20
20
  /**
21
- * @description Label for the Crash Report Context child item in the Reporting API section.
21
+ * @description Label for the crash report context item in the sidebar of the Application panel.
22
22
  */
23
- crashReportContext: 'Crash Report Context',
23
+ crashReportContext: 'Crash report context',
24
24
  } as const;
25
25
  const str_ = i18n.i18n.registerUIStrings('panels/application/ReportingApiTreeElement.ts', UIStrings);
26
26
  export const i18nString: i18n.LocalizeString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -17,29 +17,21 @@ const {widget} = UI.Widget;
17
17
 
18
18
  const UIStrings = {
19
19
  /**
20
- * @description Placeholder text that shows if no report or endpoint was detected.
21
- * A report contains information on issues or events that were encountered by a web browser.
22
- * An endpoint is a URL where the report is sent to.
23
- * (https://developer.chrome.com/docs/capabilities/web-apis/reporting-api)
20
+ * @description Header text in an empty state view when no report or endpoint has been detected in the reporting API view of the Application panel.
24
21
  */
25
22
  noReportOrEndpoint: 'No report or endpoint',
26
23
  /**
27
- * @description Placeholder text that shows if no report or endpoint was detected.
28
- * A report contains information on issues or events that were encountered by a web browser.
29
- * An endpoint is a URL where the report is sent to.
30
- * (https://developer.chrome.com/docs/capabilities/web-apis/reporting-api)
24
+ * @description Explanatory text in an empty state view describing the reporting API view of the Application panel.
31
25
  */
32
- reportingApiDescription: 'On this page you will be able to inspect `Reporting API` reports and endpoints',
26
+ reportingApiDescription: 'Inspect `Reporting API` reports and endpoints',
33
27
  /**
34
- * @description Placeholder text that shows if no report was selected for viewing
35
- *report body (https://developers.google.com/web/updates/2018/09/reportingapi#sending).
28
+ * @description Header text in an empty state view when no report is selected in the reporting API view of the Application panel.
36
29
  */
37
30
  noReportSelected: 'No report selected',
38
31
  /**
39
- * @description Placeholder text instructing the user how to display a Reporting API
40
- *report body (https://developers.google.com/web/updates/2018/09/reportingapi#sending).
32
+ * @description Explanatory text instructing the user how to display a report body in the reporting API view of the Application panel.
41
33
  */
42
- clickToDisplayBody: 'Click on any report to display its body',
34
+ clickToDisplayBody: 'Click any report to display its body',
43
35
  } as const;
44
36
  const str_ = i18n.i18n.registerUIStrings('panels/application/ReportingApiView.ts', UIStrings);
45
37
  export const i18nString: i18n.LocalizeString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -12,31 +12,31 @@ import * as PreloadingHelper from './preloading/helper/helper.js';
12
12
 
13
13
  const UIStrings = {
14
14
  /**
15
- * @description Text in Application Panel Sidebar of the Application panel
15
+ * @description Title of the Application panel.
16
16
  */
17
17
  application: 'Application',
18
18
  /**
19
- * @description Command for showing the 'Application' tool
19
+ * @description Command in the command menu for showing the Application panel.
20
20
  */
21
21
  showApplication: 'Show Application',
22
22
  /**
23
- * @description A tag of Application Panel that can be searched in the command menu
23
+ * @description Search tag for the Application panel in the command menu.
24
24
  */
25
25
  pwa: 'pwa',
26
26
  /**
27
- * @description Text of button in Clear Storage View of the Application panel
27
+ * @description Title of an action in the Application panel to clear site data.
28
28
  */
29
29
  clearSiteData: 'Clear site data',
30
30
  /**
31
- * @description Title of an action that clears all site data including 3rd party cookies
31
+ * @description Title of an action in the Application panel to clear all site data including third-party cookies.
32
32
  */
33
33
  clearSiteDataIncludingThirdparty: 'Clear site data (including third-party cookies)',
34
34
  /**
35
- * @description Title of an action under the Background Services category that can be invoked through the Command Menu
35
+ * @description Title of an action under the background services category that can be invoked through the command menu.
36
36
  */
37
37
  startRecordingEvents: 'Start recording events',
38
38
  /**
39
- * @description Title of an action under the Background Services category that can be invoked through the Command Menu
39
+ * @description Title of an action under the background services category that can be invoked through the command menu.
40
40
  */
41
41
  stopRecordingEvents: 'Stop recording events',
42
42
  } as const;
@@ -5,6 +5,7 @@
5
5
  import '../../ui/components/tooltips/tooltips.js';
6
6
 
7
7
  import * as i18n from '../../core/i18n/i18n.js';
8
+ import type * as SDK from '../../core/sdk/sdk.js';
8
9
  import type * as CommentManager from '../../models/comment_manager/comment_manager.js';
9
10
  import * as Input from '../../ui/components/input/input.js';
10
11
  import * as MarkdownView from '../../ui/components/markdown_view/markdown_view.js';
@@ -12,8 +13,10 @@ import * as UI from '../../ui/legacy/legacy.js';
12
13
  import * as Lit from '../../ui/lit/lit.js';
13
14
 
14
15
  import commentThreadWidgetStyles from './commentThreadWidget.css.js';
16
+ import {DOMNodeLink} from './DOMLinkifier.js';
15
17
 
16
18
  const {html, render, Directives: {createRef, ref}} = Lit;
19
+ const {widget} = UI.Widget;
17
20
 
18
21
  const UIStrings = {
19
22
  /**
@@ -33,7 +36,7 @@ const UIStrings = {
33
36
  /**
34
37
  * @description Label for the aria-label of the add comment button.
35
38
  */
36
- addCommentButton: 'Add comment',
39
+ sendToAgent: 'Send to agent',
37
40
  /**
38
41
  * @description aria-label for the comment text area.
39
42
  */
@@ -52,8 +55,16 @@ const str_ = i18n.i18n.registerUIStrings('panels/common/CommentThreadWidget.ts',
52
55
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
53
56
  const lockedString = i18n.i18n.lockedString;
54
57
 
58
+ /**
59
+ * Either the DOM node the thread is anchored to, or a plain text label for anchors that are not
60
+ * DOM nodes (e.g. network requests).
61
+ */
62
+ export type Title = {
63
+ node: SDK.DOMModel.DOMNode,
64
+ }|{text: string};
65
+
55
66
  export interface ViewInput {
56
- title: string;
67
+ title: Title;
57
68
  comments: CommentManager.CommentManager.Comment[];
58
69
  commentText: string;
59
70
  textAreaRef: Lit.Directives.Ref<HTMLTextAreaElement>;
@@ -72,7 +83,9 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
72
83
  <div class="comment-thread-widget ${hasComment ? 'submitted' : ''}">
73
84
  <div class="header">
74
85
  <span class="selected-item">
75
- <span class="selected-item-text">${input.title}</span>
86
+ ${'node' in input.title ?
87
+ widget(DOMNodeLink, {node: input.title.node}) :
88
+ html`<span class="selected-item-text">${input.title.text}</span>`}
76
89
  </span>
77
90
  ${hasComment ? html`
78
91
  <div class="sent-status">
@@ -137,10 +150,10 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
137
150
  </div>
138
151
  </devtools-tooltip>
139
152
  <devtools-button
140
- aria-label=${i18nString(UIStrings.addCommentButton)}
153
+ aria-label=${i18nString(UIStrings.sendToAgent)}
141
154
  .disabled=${!input.commentText.trim()}
142
155
  @click=${() => input.onAddComment(input.commentText)}>
143
- ${i18nString(UIStrings.addCommentButton)}
156
+ ${i18nString(UIStrings.sendToAgent)}
144
157
  </devtools-button>
145
158
  </div>
146
159
  ` : Lit.nothing}
@@ -152,7 +165,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
152
165
  type View = typeof DEFAULT_VIEW;
153
166
 
154
167
  export class CommentThreadWidget extends UI.Widget.Widget {
155
- title = 'Comment Thread';
168
+ title: Title = {text: ''};
156
169
  #comments: CommentManager.CommentManager.Comment[] = [];
157
170
  #commentText = '';
158
171
  #textAreaRef = createRef<HTMLTextAreaElement>();
@@ -4,13 +4,15 @@
4
4
 
5
5
  import type * as Common from '../../core/common/common.js';
6
6
  import * as Root from '../../core/root/root.js';
7
+ import * as SDK from '../../core/sdk/sdk.js';
8
+ import type * as Protocol from '../../generated/protocol.js';
7
9
  import * as CommentManager from '../../models/comment_manager/comment_manager.js';
8
10
  import * as Comments from '../../ui/comments/comments.js';
9
11
  import * as UI from '../../ui/legacy/legacy.js';
10
12
  import * as Lit from '../../ui/lit/lit.js';
11
13
 
12
14
  import commentsOverlayStyles from './commentsOverlay.css.js';
13
- import {CommentThreadWidget} from './CommentThreadWidget.js';
15
+ import {CommentThreadWidget, type Title} from './CommentThreadWidget.js';
14
16
 
15
17
  const {
16
18
  html,
@@ -27,6 +29,7 @@ export interface ViewInput {
27
29
  onPinClick: (threadId: string) => void;
28
30
  activeThread: CommentManager.CommentManager.CommentThread|null;
29
31
  activePin: Comments.CommentOverlayManager.PinPositionData|null;
32
+ title: Title;
30
33
  onAddComment: (text: string) => void;
31
34
  }
32
35
 
@@ -96,6 +99,7 @@ const DEFAULT_VIEW: View = (input: ViewInput, _output: undefined, target: HTMLEl
96
99
  )}px`,
97
100
  })}>
98
101
  ${UI.Widget.widget(CommentThreadWidget, {
102
+ title: input.title,
99
103
  comments: [...item.thread.comments],
100
104
  onAddComment: input.onAddComment,
101
105
  })}
@@ -115,6 +119,8 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
115
119
  readonly #commentManager: CommentManager.CommentManager.CommentManager;
116
120
  #commentOverlayManager: Comments.CommentOverlayManager.CommentOverlayManager;
117
121
  #activeThreadId: string|null = null;
122
+ #cachedTitle: Title = {text: ''};
123
+ #cachedTitleAnchor: CommentManager.CommentManager.CommentAnchorSignature|null = null;
118
124
 
119
125
  constructor(
120
126
  element: HTMLElement|undefined,
@@ -157,6 +163,11 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
157
163
  this.#onCommentModeChanged,
158
164
  this,
159
165
  );
166
+ this.#commentManager.addEventListener(
167
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
168
+ this.#onAgentAttachedChanged,
169
+ this,
170
+ );
160
171
 
161
172
  this.requestUpdate();
162
173
  }
@@ -184,10 +195,25 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
184
195
  this.#onCommentModeChanged,
185
196
  this,
186
197
  );
198
+ this.#commentManager.removeEventListener(
199
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
200
+ this.#onAgentAttachedChanged,
201
+ this,
202
+ );
187
203
 
188
204
  super.willHide();
189
205
  }
190
206
 
207
+ #onAgentAttachedChanged(
208
+ event: Common.EventTarget.EventTargetEvent<
209
+ CommentManager.CommentManager.EventTypes[CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED]>,
210
+ ): void {
211
+ if (!event.data) {
212
+ this.#activeThreadId = null;
213
+ }
214
+ this.requestUpdate();
215
+ }
216
+
191
217
  #onCommentModeChanged(
192
218
  event: Common.EventTarget.EventTargetEvent<
193
219
  CommentManager.CommentManager.EventTypes[CommentManager.CommentManager.Events.COMMENT_MODE_CHANGED]>,
@@ -213,6 +239,43 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
213
239
  this.requestUpdate();
214
240
  }
215
241
 
242
+ async #getOrComputeTitle(anchor: CommentManager.CommentManager.CommentAnchorSignature|null): Promise<Title> {
243
+ if (anchor === this.#cachedTitleAnchor) {
244
+ return this.#cachedTitle;
245
+ }
246
+ const title = anchor ? await this.#computeTitle(anchor) : {text: ''};
247
+ this.#cachedTitleAnchor = anchor;
248
+ this.#cachedTitle = title;
249
+ return title;
250
+ }
251
+
252
+ async #computeTitle(anchor: CommentManager.CommentManager.CommentAnchorSignature): Promise<Title> {
253
+ if (anchor.node) {
254
+ const target = SDK.TargetManager.TargetManager.instance().targetById(anchor.node.targetId);
255
+ if (target) {
256
+ const deferredNode = new SDK.DOMModel.DeferredDOMNode(
257
+ target,
258
+ anchor.node.backendNodeId as Protocol.DOM.BackendNodeId,
259
+ );
260
+ const node = await deferredNode.resolvePromise();
261
+ if (node) {
262
+ return {node};
263
+ }
264
+ }
265
+ return {text: ''};
266
+ }
267
+
268
+ if (anchor.networkRequestId) {
269
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
270
+ const request = target?.model(SDK.NetworkManager.NetworkManager)?.requestForId(anchor.networkRequestId);
271
+ if (request) {
272
+ return {text: request.name()};
273
+ }
274
+ }
275
+
276
+ return {text: anchor.textSignature || ''};
277
+ }
278
+
216
279
  #handlePinClick = (threadId: string): void => {
217
280
  const thread = this.#commentManager.getCommentThread(threadId);
218
281
  if (this.#activeThreadId === threadId) {
@@ -227,16 +290,34 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
227
290
  this.requestUpdate();
228
291
  };
229
292
 
230
- override performUpdate(): void {
293
+ override async performUpdate(signal?: AbortSignal): Promise<void> {
294
+ if (!this.#commentManager.isAgentAttached()) {
295
+ this.#view(
296
+ {
297
+ pins: [],
298
+ highlights: [],
299
+ hoverHighlight: null,
300
+ commentMode: false,
301
+ onPinClick: this.#handlePinClick,
302
+ activeThread: null,
303
+ activePin: null,
304
+ title: {text: ''},
305
+ onAddComment: () => {},
306
+ },
307
+ undefined,
308
+ this.contentElement,
309
+ );
310
+ return;
311
+ }
312
+
313
+ const activeThread =
314
+ this.#activeThreadId ? this.#commentManager.getCommentThread(this.#activeThreadId) ?? null : null;
315
+ const title = await this.#getOrComputeTitle(activeThread?.anchor ?? null);
316
+ signal?.throwIfAborted();
317
+
231
318
  const pins = this.#commentOverlayManager.getPinPositions();
232
319
  const highlights = this.#commentOverlayManager.getHighlightRects();
233
-
234
- let activePin: Comments.CommentOverlayManager.PinPositionData|null = null;
235
- let activeThread: CommentManager.CommentManager.CommentThread|null = null;
236
- if (this.#activeThreadId) {
237
- activePin = pins.find(p => p.id === this.#activeThreadId) ?? null;
238
- activeThread = this.#commentManager.getCommentThread(this.#activeThreadId) ?? null;
239
- }
320
+ const activePin = this.#activeThreadId ? pins.find(p => p.id === this.#activeThreadId) ?? null : null;
240
321
 
241
322
  const viewInput: ViewInput = {
242
323
  pins,
@@ -246,6 +327,7 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
246
327
  onPinClick: this.#handlePinClick,
247
328
  activeThread,
248
329
  activePin,
330
+ title,
249
331
  onAddComment: (text: string) => {
250
332
  activeThread?.save(text);
251
333
  },
@@ -291,3 +373,27 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
291
373
  }
292
374
  }
293
375
  }
376
+
377
+ export class ButtonProvider implements UI.Toolbar.Provider {
378
+ readonly #button: UI.Toolbar.ToolbarButton;
379
+ readonly #commentManager: CommentManager.CommentManager.CommentManager;
380
+
381
+ constructor(commentManager?: CommentManager.CommentManager.CommentManager) {
382
+ this.#commentManager = commentManager ??
383
+ Root.DevToolsContext.globalInstance().get(
384
+ CommentManager.CommentManager.CommentManager,
385
+ );
386
+ this.#button = UI.Toolbar.Toolbar.createActionButton('comments.toggle-comment-mode');
387
+ this.#button.setVisible(this.#commentManager.isAgentAttached());
388
+ this.#commentManager.addEventListener(
389
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
390
+ event => {
391
+ this.#button.setVisible(event.data);
392
+ },
393
+ );
394
+ }
395
+
396
+ item(): UI.Toolbar.ToolbarItem|null {
397
+ return this.#button;
398
+ }
399
+ }
@@ -72,6 +72,11 @@ export class CommentsStatusBarPill extends UI.Widget.Widget {
72
72
  this.#onThreadsChanged,
73
73
  this,
74
74
  );
75
+ this.#commentManager.addEventListener(
76
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
77
+ this.#onThreadsChanged,
78
+ this,
79
+ );
75
80
  this.requestUpdate();
76
81
  }
77
82
 
@@ -81,12 +86,19 @@ export class CommentsStatusBarPill extends UI.Widget.Widget {
81
86
  this.#onThreadsChanged,
82
87
  this,
83
88
  );
89
+ this.#commentManager.removeEventListener(
90
+ CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED,
91
+ this.#onThreadsChanged,
92
+ this,
93
+ );
84
94
  super.willHide();
85
95
  }
86
96
 
87
97
  override performUpdate(): void {
88
98
  const viewInput: ViewInput = {
89
- threads: this.#commentManager.getCommentThreads().filter(thread => thread.status !== 'DRAFT'),
99
+ threads: this.#commentManager.isAgentAttached() ?
100
+ this.#commentManager.getCommentThreads().filter(thread => thread.status !== 'DRAFT') :
101
+ [],
90
102
  onPillClick: this.#handlePillClick,
91
103
  };
92
104
  this.#view(viewInput, undefined, this.contentElement);
@@ -584,7 +584,8 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
584
584
  return this.subscribers.has(type);
585
585
  }
586
586
 
587
- private postNotification(type: string, args: unknown[], filter?: (extension: RegisteredExtension) => boolean): void {
587
+ private postNotification(type: string, args: unknown[], filter?: (extension: RegisteredExtension) => boolean,
588
+ argsForExtension?: (extension: RegisteredExtension) => unknown[]): void {
588
589
  if (!this.extensionsEnabled) {
589
590
  return;
590
591
  }
@@ -592,18 +593,22 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
592
593
  if (!subscribers) {
593
594
  return;
594
595
  }
595
- const message = {command: 'notify-' + type, arguments: args};
596
596
  for (const subscriber of subscribers) {
597
597
  if (!this.extensionEnabled(subscriber)) {
598
598
  continue;
599
599
  }
600
- if (filter) {
600
+ let extension: RegisteredExtension|undefined;
601
+ if (filter || argsForExtension) {
601
602
  const origin = extensionOrigins.get(subscriber);
602
- const extension = origin && this.registeredExtensions.get(origin);
603
- if (!extension || !filter(extension)) {
603
+ extension = origin && this.registeredExtensions.get(origin);
604
+ if (!extension || (filter && !filter(extension))) {
604
605
  continue;
605
606
  }
606
607
  }
608
+ const message = {
609
+ command: 'notify-' + type,
610
+ arguments: extension && argsForExtension ? argsForExtension(extension) : args,
611
+ };
607
612
  subscriber.postMessage(message);
608
613
  }
609
614
  }
@@ -1104,10 +1109,9 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1104
1109
  return this.evaluate(expression, true, true, evaluateOptions, this.getExtensionOrigin(port), callback.bind(this));
1105
1110
  }
1106
1111
 
1107
- private harEntryReferencesBlockedURL(entry: HAR.Log.EntryDTO, extension: RegisteredExtension): boolean {
1112
+ private sanitizeHarEntry(entry: HAR.Log.EntryDTO, extension: RegisteredExtension): HAR.Log.EntryDTO {
1108
1113
  const baseURL = entry.request.url;
1109
1114
 
1110
- // Helper to cleanly resolve and check permission
1111
1115
  const isBlocked = (url: string|undefined): boolean => {
1112
1116
  if (!url) {
1113
1117
  return false;
@@ -1116,44 +1120,49 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1116
1120
  const absoluteURL = new URL(url, baseURL).toString() as Platform.DevToolsPath.UrlString;
1117
1121
  return !extension.isAllowedOnTarget(absoluteURL);
1118
1122
  } catch {
1119
- return true; // Fallback safely: treat unparsable URLs as blocked
1123
+ return true;
1120
1124
  }
1121
1125
  };
1122
1126
 
1123
- if (isBlocked(entry.response.redirectURL)) {
1124
- return true;
1125
- }
1127
+ let initiator = entry._initiator;
1126
1128
  if (entry._initiator) {
1127
1129
  if (isBlocked(entry._initiator.url)) {
1128
- return true;
1129
- }
1130
- let stack = entry._initiator.stack;
1131
- while (stack) {
1132
- if (stack.callFrames.some(f => isBlocked(f.url))) {
1133
- return true;
1130
+ initiator = null;
1131
+ } else {
1132
+ let stack = entry._initiator.stack;
1133
+ while (stack) {
1134
+ if (stack.callFrames.some(f => isBlocked(f.url))) {
1135
+ initiator = null;
1136
+ break;
1137
+ }
1138
+ stack = stack.parent;
1134
1139
  }
1135
- stack = stack.parent;
1136
1140
  }
1137
1141
  }
1138
- for (const header of entry.response.headers) {
1142
+
1143
+ const headerReferencesBlockedURL = (header: {name: string, value: string}): boolean => {
1139
1144
  const name = header.name.toLowerCase();
1140
1145
  if (name === 'location' || name === 'content-location') {
1141
- if (isBlocked(header.value)) {
1142
- return true;
1143
- }
1144
- } else if (name === 'refresh') {
1146
+ return isBlocked(header.value);
1147
+ }
1148
+ if (name === 'refresh') {
1145
1149
  const match = header.value.match(/;\s*url\s*=\s*(.+)$/i);
1146
- if (isBlocked(match?.[1]?.trim())) {
1147
- return true;
1148
- }
1149
- } else if (name === 'link') {
1150
+ return isBlocked(match?.[1]?.trim());
1151
+ }
1152
+ if (name === 'link') {
1150
1153
  const urls = [...header.value.matchAll(/<([^>]+)>/g)].map(m => m[1]);
1151
- if (urls.some(url => isBlocked(url))) {
1152
- return true;
1153
- }
1154
+ return urls.some(url => isBlocked(url));
1154
1155
  }
1156
+ return false;
1157
+ };
1158
+
1159
+ const headers = entry.response.headers.filter(header => !headerReferencesBlockedURL(header));
1160
+ const redirectURL = isBlocked(entry.response.redirectURL) ? '' : entry.response.redirectURL;
1161
+ if (initiator === entry._initiator && headers.length === entry.response.headers.length &&
1162
+ redirectURL === entry.response.redirectURL) {
1163
+ return entry;
1155
1164
  }
1156
- return false;
1165
+ return {...entry, _initiator: initiator, response: {...entry.response, headers, redirectURL}};
1157
1166
  }
1158
1167
 
1159
1168
  private async onGetHAR(message: Extensions.ExtensionAPI.PrivateAPI.ExtensionServerRequestMessage, port: MessagePort):
@@ -1173,7 +1182,7 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1173
1182
  // @ts-expect-error
1174
1183
  harLog.entries[i]._requestId = this.requestId(requests[i]);
1175
1184
  }
1176
- harLog.entries = harLog.entries.filter(entry => !this.harEntryReferencesBlockedURL(entry, extension));
1185
+ harLog.entries = harLog.entries.map(entry => this.sanitizeHarEntry(entry, extension));
1177
1186
  return harLog;
1178
1187
  }
1179
1188
 
@@ -1513,8 +1522,8 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
1513
1522
  this.postNotification(
1514
1523
  Extensions.ExtensionAPI.PrivateAPI.Events.NetworkRequestFinished, [this.requestId(request), entry],
1515
1524
  extension => extension.isAllowedOnTarget(entry.request.url as Platform.DevToolsPath.UrlString) &&
1516
- (!targetUrl || extension.isAllowedOnTarget(targetUrl)) &&
1517
- !this.harEntryReferencesBlockedURL(entry, extension));
1525
+ (!targetUrl || extension.isAllowedOnTarget(targetUrl)),
1526
+ extension => [this.requestId(request), this.sanitizeHarEntry(entry, extension)]);
1518
1527
  }
1519
1528
 
1520
1529
  private notifyElementsSelectionChanged(): void {
@@ -26,7 +26,7 @@ const UIStrings = {
26
26
  /**
27
27
  * @description ARIA label for the dialog.
28
28
  */
29
- dialogAriaLabel: 'Gemini 3 Flash in DevTools',
29
+ dialogAriaLabel: '`Gemini 3 Flash` in `DevTools`',
30
30
  /**
31
31
  * @description Button text for dismissing the dialog.
32
32
  */