chrome-devtools-frontend 1.0.1565595 → 1.0.1567721

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 (201) hide show
  1. package/docs/contributing/performance.md +24 -0
  2. package/front_end/Images/src/lock-person.svg +1 -0
  3. package/front_end/core/common/Base64.ts +21 -2
  4. package/front_end/core/common/ResourceType.ts +7 -0
  5. package/front_end/core/host/InspectorFrontendHostStub.ts +3 -0
  6. package/front_end/core/host/UserMetrics.ts +20 -0
  7. package/front_end/core/root/Runtime.ts +5 -0
  8. package/front_end/core/sdk/DOMModel.ts +32 -2
  9. package/front_end/core/sdk/NetworkManager.ts +16 -3
  10. package/front_end/core/sdk/ResourceTreeModel.ts +11 -6
  11. package/front_end/entrypoints/main/MainImpl.ts +1 -7
  12. package/front_end/generated/Deprecation.ts +0 -8
  13. package/front_end/generated/InspectorBackendCommands.ts +10 -1
  14. package/front_end/generated/SupportedCSSProperties.js +48 -62
  15. package/front_end/generated/protocol-mapping.d.ts +4 -0
  16. package/front_end/generated/protocol-proxy-api.d.ts +5 -0
  17. package/front_end/generated/protocol.ts +203 -0
  18. package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -1
  19. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +98 -105
  20. package/front_end/models/emulation/DeviceModeModel.ts +1 -1
  21. package/front_end/models/formatter/FormatterWorkerPool.ts +8 -6
  22. package/front_end/models/javascript_metadata/NativeFunctions.js +4 -9
  23. package/front_end/models/persistence/PersistenceImpl.ts +8 -0
  24. package/front_end/models/text_utils/TextUtils.snapshot.txt +83 -0
  25. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +0 -1
  26. package/front_end/panels/ai_assistance/components/ArtifactsViewer.ts +28 -4
  27. package/front_end/panels/ai_assistance/components/ChatView.ts +52 -169
  28. package/front_end/panels/ai_assistance/components/CollapsibleAssistanceContentWidget.ts +27 -8
  29. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +19 -4
  30. package/front_end/panels/application/ApplicationPanelSidebar.ts +29 -0
  31. package/front_end/panels/application/ApplicationPanelTreeElement.ts +12 -0
  32. package/front_end/panels/application/DeviceBoundSessionsModel.ts +169 -0
  33. package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +219 -0
  34. package/front_end/panels/application/DeviceBoundSessionsView.ts +1096 -0
  35. package/front_end/panels/application/ResourcesPanel.ts +19 -0
  36. package/front_end/panels/application/application.ts +6 -0
  37. package/front_end/panels/application/deviceBoundSessionsView.css +27 -0
  38. package/front_end/panels/common/AiCodeGenerationUpgradeDialog.ts +115 -0
  39. package/front_end/panels/common/aiCodeGenerationUpgradeDialog.css +79 -0
  40. package/front_end/panels/common/common.ts +1 -0
  41. package/front_end/panels/console/ConsoleInsightTeaser.ts +41 -8
  42. package/front_end/panels/console/ConsoleViewMessage.ts +20 -13
  43. package/front_end/panels/elements/ElementsTreeOutline.ts +13 -0
  44. package/front_end/panels/elements/MetricsSidebarPane.ts +3 -1
  45. package/front_end/panels/issues/AffectedSourcesView.ts +0 -1
  46. package/front_end/panels/issues/IssuesPane.ts +0 -4
  47. package/front_end/panels/network/RequestTimingView.ts +5 -20
  48. package/front_end/panels/network/networkTimingTable.css +5 -5
  49. package/front_end/panels/recorder/components/StepEditor.ts +15 -8
  50. package/front_end/panels/security/SecurityPanelSidebar.ts +0 -1
  51. package/front_end/panels/settings/AISettingsTab.ts +31 -2
  52. package/front_end/panels/sources/InplaceFormatterEditorAction.ts +8 -6
  53. package/front_end/panels/sources/UISourceCodeFrame.ts +9 -3
  54. package/front_end/panels/timeline/TimelinePanel.ts +12 -4
  55. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +1 -1
  56. package/front_end/third_party/chromium/README.chromium +1 -1
  57. package/front_end/third_party/puppeteer/README.chromium +2 -2
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts +9 -2
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts.map +1 -1
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.js.map +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +8 -0
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js +8 -0
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.d.ts +2 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.d.ts.map +1 -1
  67. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.js +2 -2
  68. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Browser.js.map +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BrowserContext.d.ts.map +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BrowserContext.js +3 -1
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BrowserContext.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.d.ts.map +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.js +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Frame.js.map +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js +3 -3
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/UserContext.d.ts +1 -0
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/UserContext.d.ts.map +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/UserContext.js +1 -0
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/UserContext.js.map +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +1 -0
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +33 -31
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebWorker.d.ts +2 -2
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebWorker.d.ts.map +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebWorker.js +1 -4
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/WebWorker.js.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/ConsoleMessage.d.ts +7 -1
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/ConsoleMessage.d.ts.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/ConsoleMessage.js +11 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/ConsoleMessage.js.map +1 -1
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/Cookie.d.ts +2 -2
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  99. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  100. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  101. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +13 -4
  104. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +62 -42
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts +9 -2
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts.map +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.js.map +1 -1
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +8 -0
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js +8 -0
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.d.ts +2 -1
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.d.ts.map +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.js +2 -2
  115. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Browser.js.map +1 -1
  116. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BrowserContext.d.ts.map +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BrowserContext.js +3 -1
  118. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BrowserContext.js.map +1 -1
  119. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.d.ts.map +1 -1
  120. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.js +1 -1
  121. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Frame.js.map +1 -1
  122. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
  123. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js +3 -3
  124. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
  125. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/UserContext.d.ts +1 -0
  126. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/UserContext.d.ts.map +1 -1
  127. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/UserContext.js +1 -0
  128. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/UserContext.js.map +1 -1
  129. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +1 -0
  131. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  132. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  133. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +33 -31
  134. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  135. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebWorker.d.ts +2 -2
  136. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebWorker.d.ts.map +1 -1
  137. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebWorker.js +1 -4
  138. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/WebWorker.js.map +1 -1
  139. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/ConsoleMessage.d.ts +7 -1
  140. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/ConsoleMessage.d.ts.map +1 -1
  141. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/ConsoleMessage.js +11 -1
  142. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/ConsoleMessage.js.map +1 -1
  143. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/Cookie.d.ts +2 -2
  144. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  145. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  146. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  147. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  148. package/front_end/third_party/puppeteer/package/lib/types.d.ts +13 -4
  149. package/front_end/third_party/puppeteer/package/package.json +3 -3
  150. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +11 -3
  151. package/front_end/third_party/puppeteer/package/src/api/Page.ts +9 -0
  152. package/front_end/third_party/puppeteer/package/src/bidi/Browser.ts +3 -3
  153. package/front_end/third_party/puppeteer/package/src/bidi/BrowserContext.ts +3 -1
  154. package/front_end/third_party/puppeteer/package/src/bidi/Frame.ts +1 -0
  155. package/front_end/third_party/puppeteer/package/src/bidi/core/BrowsingContext.ts +3 -5
  156. package/front_end/third_party/puppeteer/package/src/bidi/core/UserContext.ts +2 -0
  157. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +1 -0
  158. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +43 -45
  159. package/front_end/third_party/puppeteer/package/src/cdp/WebWorker.ts +3 -11
  160. package/front_end/third_party/puppeteer/package/src/common/ConsoleMessage.ts +12 -0
  161. package/front_end/third_party/puppeteer/package/src/common/Cookie.ts +2 -2
  162. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  163. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  164. package/front_end/third_party/source-map-scopes-codec/README.chromium +2 -5
  165. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/builder.d.ts +1 -1
  166. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/builder.d.ts.map +1 -1
  167. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/safe_builder.d.ts +1 -1
  168. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/safe_builder.d.ts.map +1 -1
  169. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts +1 -1
  170. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts.map +1 -1
  171. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/encode/encode.d.ts +1 -1
  172. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/encode/encode.d.ts.map +1 -1
  173. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/mod.d.ts +1 -1
  174. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/mod.d.ts.map +1 -1
  175. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/scopes.d.ts +130 -0
  176. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/scopes.d.ts.map +1 -0
  177. package/front_end/third_party/source-map-scopes-codec/package/deno.json +1 -1
  178. package/front_end/third_party/source-map-scopes-codec/package/package.json +1 -1
  179. package/front_end/third_party/source-map-scopes-codec/package/src/builder/builder.ts +1 -1
  180. package/front_end/third_party/source-map-scopes-codec/package/src/builder/safe_builder.ts +1 -1
  181. package/front_end/third_party/source-map-scopes-codec/package/src/codec.js +4 -0
  182. package/front_end/third_party/source-map-scopes-codec/package/src/codec.js.map +1 -1
  183. package/front_end/third_party/source-map-scopes-codec/package/src/codec.ts +4 -0
  184. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +30 -13
  185. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js.map +1 -1
  186. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.ts +35 -17
  187. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encode.ts +1 -1
  188. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
  189. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js.map +1 -1
  190. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.ts +2 -2
  191. package/front_end/third_party/source-map-scopes-codec/package/src/mod.ts +1 -1
  192. package/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +7 -0
  193. package/front_end/third_party/source-map-scopes-codec/package/src/scopes.js.map +1 -0
  194. package/front_end/third_party/source-map-scopes-codec/package/src/util.ts +1 -1
  195. package/front_end/ui/legacy/components/color_picker/FormatPickerContextMenu.ts +8 -1
  196. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +4 -0
  197. package/front_end/ui/visual_logging/KnownContextValues.ts +13 -0
  198. package/mcp/mcp.ts +1 -0
  199. package/package.json +1 -2
  200. package/front_end/third_party/source-map-scopes-codec/package/src/scopes-tsconfig.json +0 -8
  201. /package/front_end/third_party/source-map-scopes-codec/package/src/{scopes.d.ts → scopes.ts} +0 -0
@@ -208,7 +208,6 @@ export class ChatView extends HTMLElement {
208
208
  };
209
209
 
210
210
  #render(): void {
211
-
212
211
  const inputWidgetClasses = Lit.Directives.classMap({
213
212
  'chat-input-widget': true,
214
213
  sticky: !this.#props.isReadOnly,
@@ -219,22 +218,58 @@ export class ChatView extends HTMLElement {
219
218
  <style>${chatViewStyles}</style>
220
219
  <div class="chat-ui">
221
220
  <main @scroll=${this.#handleScroll} ${ref(this.#mainElementRef)}>
222
- ${renderMainContents({
223
- messages: this.#props.messages,
224
- isLoading: this.#props.isLoading,
225
- isReadOnly: this.#props.isReadOnly,
226
- canShowFeedbackForm: this.#props.canShowFeedbackForm,
227
- isTextInputDisabled: this.#props.isTextInputDisabled,
228
- suggestions: this.#props.emptyStateSuggestions,
229
- userInfo: this.#props.userInfo,
230
- markdownRenderer: this.#props.markdownRenderer,
231
- changeSummary: this.#props.changeSummary,
232
- changeManager: this.#props.changeManager,
233
- onSuggestionClick: this.#handleSuggestionClick,
234
- onFeedbackSubmit: this.#props.onFeedbackSubmit,
235
- onMessageContainerRef: this.#handleMessageContainerRef,
236
- onCopyResponseClick: this.#props.onCopyResponseClick,
237
- })}
221
+ ${this.#props.messages.length > 0 ? html`
222
+ <div class="messages-container" ${ref(this.#handleMessageContainerRef)}>
223
+ ${repeat(this.#props.messages, message =>
224
+ html`<devtools-widget .widgetConfig=${UI.Widget.widgetConfig(ChatMessage, {
225
+ message,
226
+ isLoading: this.#props.isLoading,
227
+ isReadOnly: this.#props.isReadOnly,
228
+ canShowFeedbackForm: this.#props.canShowFeedbackForm,
229
+ userInfo: this.#props.userInfo,
230
+ markdownRenderer: this.#props.markdownRenderer,
231
+ isLastMessage: this.#props.messages.at(-1) === message,
232
+ onSuggestionClick: this.#handleSuggestionClick,
233
+ onFeedbackSubmit: this.#props.onFeedbackSubmit,
234
+ onCopyResponseClick: this.#props.onCopyResponseClick,
235
+ })}></devtools-widget>`
236
+ )}
237
+ ${this.#props.isLoading ? Lit.nothing : html`<devtools-widget
238
+ .widgetConfig=${UI.Widget.widgetConfig(PatchWidget, {
239
+ changeSummary: this.#props.changeSummary ?? '',
240
+ changeManager: this.#props.changeManager,
241
+ })}
242
+ ></devtools-widget>`}
243
+ </div>
244
+ ` : html`
245
+ <div class="empty-state-container">
246
+ <div class="header">
247
+ <div class="icon">
248
+ <devtools-icon
249
+ name="smart-assistant"
250
+ ></devtools-icon>
251
+ </div>
252
+ <h1>${lockedString(UIStringsNotTranslate.emptyStateText)}</h1>
253
+ </div>
254
+ <div class="empty-state-content">
255
+ ${this.#props.emptyStateSuggestions.map(({title, jslogContext}) => {
256
+ return html`<devtools-button
257
+ class="suggestion"
258
+ @click=${() => this.#handleSuggestionClick(title)}
259
+ .data=${
260
+ {
261
+ variant: Buttons.Button.Variant.OUTLINED,
262
+ size: Buttons.Button.Size.REGULAR,
263
+ title,
264
+ jslogContext: jslogContext ?? 'suggestion',
265
+ disabled: this.#props.isTextInputDisabled,
266
+ } as Buttons.Button.ButtonData
267
+ }
268
+ >${title}</devtools-button>`;
269
+ })}
270
+ </div>
271
+ </div>
272
+ `}
238
273
  <devtools-widget class=${inputWidgetClasses} .widgetConfig=${UI.Widget.widgetConfig(ChatInput, {
239
274
  isLoading: this.#props.isLoading,
240
275
  blockedByCrossOrigin: this.#props.blockedByCrossOrigin,
@@ -266,158 +301,6 @@ export class ChatView extends HTMLElement {
266
301
  }
267
302
  }
268
303
 
269
- function renderMainContents({
270
- messages,
271
- isLoading,
272
- isReadOnly,
273
- canShowFeedbackForm,
274
- isTextInputDisabled,
275
- suggestions,
276
- userInfo,
277
- markdownRenderer,
278
- changeSummary,
279
- changeManager,
280
- onSuggestionClick,
281
- onFeedbackSubmit,
282
- onCopyResponseClick,
283
- onMessageContainerRef,
284
- }: {
285
- messages: Message[],
286
- isLoading: boolean,
287
- isReadOnly: boolean,
288
- canShowFeedbackForm: boolean,
289
- isTextInputDisabled: boolean,
290
- suggestions: AiAssistanceModel.AiAgent.ConversationSuggestion[],
291
- userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'>,
292
- markdownRenderer: MarkdownLitRenderer,
293
- changeManager: AiAssistanceModel.ChangeManager.ChangeManager,
294
- onSuggestionClick: (suggestion: string) => void,
295
- onFeedbackSubmit: (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void,
296
- onCopyResponseClick: (message: ModelChatMessage) => void,
297
- onMessageContainerRef: (el: Element|undefined) => void,
298
- changeSummary?: string,
299
- }): Lit.LitTemplate {
300
- if (messages.length > 0) {
301
- return renderMessages({
302
- messages,
303
- isLoading,
304
- isReadOnly,
305
- canShowFeedbackForm,
306
- userInfo,
307
- markdownRenderer,
308
- changeSummary,
309
- changeManager,
310
- onSuggestionClick,
311
- onFeedbackSubmit,
312
- onMessageContainerRef,
313
- onCopyResponseClick
314
- });
315
- }
316
-
317
- return renderEmptyState({isTextInputDisabled, suggestions, onSuggestionClick});
318
- }
319
-
320
- function renderMessages({
321
- messages,
322
- isLoading,
323
- isReadOnly,
324
- canShowFeedbackForm,
325
- userInfo,
326
- markdownRenderer,
327
- changeSummary,
328
- changeManager,
329
- onSuggestionClick,
330
- onFeedbackSubmit,
331
- onCopyResponseClick,
332
- onMessageContainerRef,
333
- }: {
334
- messages: Message[],
335
- isLoading: boolean,
336
- isReadOnly: boolean,
337
- canShowFeedbackForm: boolean,
338
- userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'>,
339
- markdownRenderer: MarkdownLitRenderer,
340
- onSuggestionClick: (suggestion: string) => void,
341
- onFeedbackSubmit: (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void,
342
- onCopyResponseClick: (message: ModelChatMessage) => void,
343
- onMessageContainerRef: (el: Element|undefined) => void,
344
- changeSummary?: string,
345
- changeManager?: AiAssistanceModel.ChangeManager.ChangeManager,
346
- }): Lit.TemplateResult {
347
- function renderPatchWidget(): Lit.LitTemplate {
348
- if (isLoading) {
349
- return Lit.nothing;
350
- }
351
-
352
- // clang-format off
353
- return html`<devtools-widget
354
- .widgetConfig=${UI.Widget.widgetConfig(PatchWidget, {
355
- changeSummary: changeSummary ?? '',
356
- changeManager,
357
- })}
358
- ></devtools-widget>`;
359
- // clang-format on
360
- }
361
-
362
- // clang-format off
363
- return html`
364
- <div class="messages-container" ${ref(onMessageContainerRef)}>
365
- ${repeat(messages, message =>
366
- html`<devtools-widget .widgetConfig=${UI.Widget.widgetConfig(ChatMessage, {
367
- message,
368
- isLoading,
369
- isReadOnly,
370
- canShowFeedbackForm,
371
- userInfo,
372
- markdownRenderer,
373
- isLastMessage: messages.at(-1) === message,
374
- onSuggestionClick,
375
- onFeedbackSubmit,
376
- onCopyResponseClick,
377
- })}></devtools-widget>`
378
- )}
379
- ${renderPatchWidget()}
380
- </div>
381
- `;
382
- // clang-format on
383
- }
384
-
385
- function renderEmptyState({isTextInputDisabled, suggestions, onSuggestionClick}: {
386
- isTextInputDisabled: boolean,
387
- suggestions: AiAssistanceModel.AiAgent.ConversationSuggestion[],
388
- onSuggestionClick: (suggestion: string) => void,
389
- }): Lit.TemplateResult {
390
- // clang-format off
391
- return html`<div class="empty-state-container">
392
- <div class="header">
393
- <div class="icon">
394
- <devtools-icon
395
- name="smart-assistant"
396
- ></devtools-icon>
397
- </div>
398
- <h1>${lockedString(UIStringsNotTranslate.emptyStateText)}</h1>
399
- </div>
400
- <div class="empty-state-content">
401
- ${suggestions.map(({title, jslogContext}) => {
402
- return html`<devtools-button
403
- class="suggestion"
404
- @click=${() => onSuggestionClick(title)}
405
- .data=${
406
- {
407
- variant: Buttons.Button.Variant.OUTLINED,
408
- size: Buttons.Button.Size.REGULAR,
409
- title,
410
- jslogContext: jslogContext ?? 'suggestion',
411
- disabled: isTextInputDisabled,
412
- } as Buttons.Button.ButtonData
413
- }
414
- >${title}</devtools-button>`;
415
- })}
416
- </div>
417
- </div>`;
418
- // clang-format on
419
- }
420
-
421
304
  declare global {
422
305
  interface HTMLElementTagNameMap {
423
306
  'devtools-ai-chat-view': ChatView;
@@ -12,15 +12,18 @@ const {render, html} = Lit;
12
12
 
13
13
  export interface CollapsibleAssistanceContentWidgetData {
14
14
  headerText: string;
15
+ onReveal?: () => void;
15
16
  }
16
17
 
17
18
  export class CollapsibleAssistanceContentWidget extends HTMLElement {
18
19
  readonly #shadow = this.attachShadow({mode: 'open'});
19
20
  #isCollapsed = false;
20
21
  #headerText = 'Details';
22
+ #onReveal?: () => void;
21
23
 
22
24
  set data(data: CollapsibleAssistanceContentWidgetData) {
23
25
  this.#headerText = data.headerText;
26
+ this.#onReveal = data.onReveal;
24
27
  this.#render();
25
28
  }
26
29
 
@@ -43,14 +46,30 @@ export class CollapsibleAssistanceContentWidget extends HTMLElement {
43
46
  this.#toggleCollapse();
44
47
  }}>
45
48
  ${this.#headerText}
46
- <devtools-button .data=${{
47
- variant: Buttons.Button.Variant.ICON,
48
- iconName: this.#isCollapsed ? 'triangle-right' : 'triangle-down',
49
- color: 'var(--sys-color-on-surface)',
50
- width: '14px',
51
- height: '14px',
52
- } as Buttons.Button.ButtonData}
53
- ></devtools-button>
49
+ <div>
50
+ <devtools-button .data=${{
51
+ variant: Buttons.Button.Variant.ICON,
52
+ iconName: 'select-element',
53
+ color: 'var(--sys-color-on-surface)',
54
+ width: '14px',
55
+ height: '14px',
56
+ title: 'reveal',
57
+ } as Buttons.Button.ButtonData}
58
+ @click=${(event: Event) => {
59
+ event.stopPropagation();
60
+ this.#onReveal?.();
61
+ }}
62
+ ></devtools-button>
63
+ <devtools-button .data=${{
64
+ variant: Buttons.Button.Variant.ICON,
65
+ iconName: this.#isCollapsed ? 'triangle-right' : 'triangle-down',
66
+ color: 'var(--sys-color-on-surface)',
67
+ width: '14px',
68
+ height: '14px',
69
+ title: 'expand',
70
+ } as Buttons.Button.ButtonData}
71
+ ></devtools-button>
72
+ </div>
54
73
  </summary>
55
74
  <div class="content">
56
75
  <slot></slot>
@@ -2,10 +2,10 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import './CollapsibleAssistanceContentWidget.js';
6
5
  import '../../../models/trace/insights/insights.js';
7
6
  import '../../../panels/timeline/components/components.js';
8
7
  import './PerformanceAgentFlameChart.js';
8
+ import './CollapsibleAssistanceContentWidget.js';
9
9
 
10
10
  import * as Common from '../../../core/common/common.js';
11
11
  import * as SDK from '../../../core/sdk/sdk.js';
@@ -19,9 +19,11 @@ import type * as Marked from '../../../third_party/marked/marked.js';
19
19
  import * as UI from '../../../ui/legacy/legacy.js';
20
20
  import * as Lit from '../../../ui/lit/lit.js';
21
21
  import * as PanelsCommon from '../../common/common.js';
22
+ import * as NetworkForward from '../../network/forward/forward.js';
22
23
  import * as Network from '../../network/network.js';
23
24
  import * as TimelineComponents from '../../timeline/components/components.js';
24
25
  import * as Insights from '../../timeline/components/insights/insights.js';
26
+ import * as Timeline from '../../timeline/timeline.js';
25
27
 
26
28
  import {MarkdownRendererWithCodeBlock} from './MarkdownRendererWithCodeBlock.js';
27
29
  import type * as PerformanceAgentFlameChart from './PerformanceAgentFlameChart.js';
@@ -86,7 +88,11 @@ export class PerformanceAgentMarkdownRenderer extends MarkdownRendererWithCodeBl
86
88
  }
87
89
 
88
90
  return html`<devtools-collapsible-assistance-content-widget .data=${{
89
- headerText: `Insight - ${componentName}`
91
+ headerText: `Insight - ${componentName}`, onReveal: () => {
92
+ void UI.InspectorView.InspectorView.instance().showPanel('timeline').then(() => {
93
+ Timeline.TimelinePanel.TimelinePanel.instance().revealInsight(insightM);
94
+ });
95
+ },
90
96
  }
91
97
  }>
92
98
  ${this.#insightRenderer.renderInsightToWidgetElement(this.parsedTrace, insightSet, insightM, componentName, {
@@ -115,7 +121,13 @@ export class PerformanceAgentMarkdownRenderer extends MarkdownRendererWithCodeBl
115
121
  return html`<devtools-collapsible-assistance-content-widget
116
122
  .data=${{
117
123
  headerText: `Network Request: ${
118
- networkRequest.url().length > 80 ? networkRequest.url().slice(0, 80) + '...' : networkRequest.url()}`
124
+ networkRequest.url().length > 80 ? networkRequest.url().slice(0, 80) + '...' : networkRequest.url()}`,
125
+ onReveal: () => {
126
+ void UI.InspectorView.InspectorView.instance().showPanel('network').then(() => {
127
+ void Common.Revealer.reveal(NetworkForward.UIRequestLocation.UIRequestLocation.tab(
128
+ networkRequest, NetworkForward.UIRequestLocation.UIRequestTabs.TIMING));
129
+ });
130
+ },
119
131
  }
120
132
  }>
121
133
  <devtools-widget class="actions" .widgetConfig=${
@@ -142,7 +154,10 @@ export class PerformanceAgentMarkdownRenderer extends MarkdownRendererWithCodeBl
142
154
 
143
155
  return html`<devtools-collapsible-assistance-content-widget
144
156
  .data=${{
145
- headerText: 'Network Request'
157
+ headerText: 'Network Request', onReveal: () => {
158
+ // eslint-disable-next-line no-console
159
+ console.log('Reveal network request', value);
160
+ },
146
161
  }
147
162
  }>
148
163
  ${networkTooltip}
@@ -37,6 +37,7 @@ import * as Common from '../../core/common/common.js';
37
37
  import * as Host from '../../core/host/host.js';
38
38
  import * as i18n from '../../core/i18n/i18n.js';
39
39
  import * as Platform from '../../core/platform/platform.js';
40
+ import * as Root from '../../core/root/root.js';
40
41
  import * as SDK from '../../core/sdk/sdk.js';
41
42
  import * as Protocol from '../../generated/protocol.js';
42
43
  import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
@@ -51,6 +52,8 @@ import {BackForwardCacheTreeElement} from './BackForwardCacheTreeElement.js';
51
52
  import {BackgroundServiceModel} from './BackgroundServiceModel.js';
52
53
  import {BackgroundServiceView} from './BackgroundServiceView.js';
53
54
  import {BounceTrackingMitigationsTreeElement} from './BounceTrackingMitigationsTreeElement.js';
55
+ import {DeviceBoundSessionsModel} from './DeviceBoundSessionsModel.js';
56
+ import {RootTreeElement as DeviceBoundSessionsRootTreeElement} from './DeviceBoundSessionsTreeElement.js';
54
57
  import {type DOMStorage, DOMStorageModel, Events as DOMStorageModelEvents} from './DOMStorageModel.js';
55
58
  import {
56
59
  Events as ExtensionStorageModelEvents,
@@ -343,6 +346,8 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
343
346
  periodicBackgroundSyncTreeElement: BackgroundServiceTreeElement;
344
347
  pushMessagingTreeElement: BackgroundServiceTreeElement;
345
348
  reportingApiTreeElement: ReportingApiTreeElement;
349
+ deviceBoundSessionsRootTreeElement: DeviceBoundSessionsRootTreeElement|undefined;
350
+ deviceBoundSessionsModel: DeviceBoundSessionsModel|undefined;
346
351
  preloadingSummaryTreeElement: PreloadingSummaryTreeElement|undefined;
347
352
  private readonly resourcesSection: ResourcesSection;
348
353
  private domStorageTreeElements: Map<DOMStorage, DOMStorageTreeElement>;
@@ -481,6 +486,13 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
481
486
  this.reportingApiTreeElement = new ReportingApiTreeElement(panel);
482
487
  backgroundServiceTreeElement.appendChild(this.reportingApiTreeElement);
483
488
 
489
+ if (Root.Runtime.hostConfig.deviceBoundSessionsDebugging?.enabled) {
490
+ this.deviceBoundSessionsModel = new DeviceBoundSessionsModel();
491
+ this.deviceBoundSessionsRootTreeElement =
492
+ new DeviceBoundSessionsRootTreeElement(panel, this.deviceBoundSessionsModel);
493
+ backgroundServiceTreeElement.appendChild(this.deviceBoundSessionsRootTreeElement);
494
+ }
495
+
484
496
  const resourcesSectionTitle = i18nString(UIStrings.frames);
485
497
  const resourcesTreeElement = this.addSidebarSection(resourcesSectionTitle, 'frames');
486
498
  this.resourcesSection = new ResourcesSection(panel, resourcesTreeElement);
@@ -760,6 +772,8 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
760
772
  this.domains = {};
761
773
  this.cookieListTreeElement.removeChildren();
762
774
  this.interestGroupTreeElement.clearEvents();
775
+ this.deviceBoundSessionsModel?.clearVisibleSites();
776
+ this.deviceBoundSessionsModel?.clearEvents();
763
777
  }
764
778
 
765
779
  private frameNavigated(event: Common.EventTarget.EventTargetEvent<SDK.ResourceTreeModel.ResourceTreeFrame>): void {
@@ -791,6 +805,21 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
791
805
  this.domains[domain] = true;
792
806
  const cookieDomainTreeElement = new CookieTreeElement(this.panel, frame, parsedURL);
793
807
  this.cookieListTreeElement.appendChild(cookieDomainTreeElement);
808
+
809
+ // Update device bound session visibility for new cookie domain.
810
+ if (this.deviceBoundSessionsModel) {
811
+ const target = frame.resourceTreeModel().target();
812
+ const networkAgent = target.networkAgent();
813
+ void networkAgent.invoke_fetchSchemefulSite({origin: domain}).then(response => {
814
+ if (response.getError() || !this.deviceBoundSessionsModel) {
815
+ return;
816
+ }
817
+ // Confirm the domain is still present first.
818
+ if (this.domains[domain]) {
819
+ this.deviceBoundSessionsModel.addVisibleSite(response.schemefulSite);
820
+ }
821
+ });
822
+ }
794
823
  }
795
824
  }
796
825
 
@@ -10,6 +10,7 @@ import type {ResourcesPanel} from './ResourcesPanel.js';
10
10
 
11
11
  export class ApplicationPanelTreeElement extends UI.TreeOutline.TreeElement {
12
12
  protected readonly resourcesPanel: ResourcesPanel;
13
+ private customItemURL?: Platform.DevToolsPath.UrlString;
13
14
 
14
15
  constructor(resourcesPanel: ResourcesPanel, title: string, expandable: boolean, jslogContext: string) {
15
16
  super(title, expandable, jslogContext);
@@ -24,9 +25,16 @@ export class ApplicationPanelTreeElement extends UI.TreeOutline.TreeElement {
24
25
  }
25
26
 
26
27
  get itemURL(): Platform.DevToolsPath.UrlString {
28
+ if (this.customItemURL) {
29
+ return this.customItemURL;
30
+ }
27
31
  throw new Error('Unimplemented Method');
28
32
  }
29
33
 
34
+ set itemURL(value: Platform.DevToolsPath.UrlString) {
35
+ this.customItemURL = value;
36
+ }
37
+
30
38
  override onselect(selectedByUser: boolean|undefined): boolean {
31
39
  if (!selectedByUser) {
32
40
  return false;
@@ -76,6 +84,10 @@ export class ExpandableApplicationPanelTreeElement extends ApplicationPanelTreeE
76
84
  return 'category://' + this.categoryName as Platform.DevToolsPath.UrlString;
77
85
  }
78
86
 
87
+ override set itemURL(value: Platform.DevToolsPath.UrlString) {
88
+ super.itemURL = value;
89
+ }
90
+
79
91
  setLink(link: Platform.DevToolsPath.UrlString): void {
80
92
  this.categoryLink = link;
81
93
  }
@@ -0,0 +1,169 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Common from '../../core/common/common.js';
6
+ import * as SDK from '../../core/sdk/sdk.js';
7
+ import type * as Protocol from '../../generated/protocol.js';
8
+
9
+ interface EventWithTimestamp {
10
+ event: Protocol.Network.DeviceBoundSessionEventOccurredEvent;
11
+ timestamp: Date;
12
+ }
13
+ export interface SessionAndEvents {
14
+ session?: Protocol.Network.DeviceBoundSession;
15
+ eventsById: Map<string, EventWithTimestamp>;
16
+ }
17
+ type SessionIdToSessionMap = Map<string|undefined, SessionAndEvents>;
18
+
19
+ export class DeviceBoundSessionsModel extends Common.ObjectWrapper.ObjectWrapper<DeviceBoundSessionModelEventTypes>
20
+ implements SDK.TargetManager.SDKModelObserver<SDK.NetworkManager.NetworkManager> {
21
+ #siteSessions = new Map<string, SessionIdToSessionMap>();
22
+ #visibleSites = new Set<string>();
23
+
24
+ constructor() {
25
+ super();
26
+ SDK.TargetManager.TargetManager.instance().observeModels(SDK.NetworkManager.NetworkManager, this, {scoped: true});
27
+ }
28
+
29
+ modelAdded(networkManager: SDK.NetworkManager.NetworkManager): void {
30
+ networkManager.addEventListener(SDK.NetworkManager.Events.DeviceBoundSessionsAdded, this.#onSessionsSet, this);
31
+ networkManager.addEventListener(
32
+ SDK.NetworkManager.Events.DeviceBoundSessionEventOccurred, this.#onEventOccurred, this);
33
+ void networkManager.enableDeviceBoundSessions();
34
+ }
35
+
36
+ modelRemoved(networkManager: SDK.NetworkManager.NetworkManager): void {
37
+ networkManager.removeEventListener(SDK.NetworkManager.Events.DeviceBoundSessionsAdded, this.#onSessionsSet, this);
38
+ networkManager.removeEventListener(
39
+ SDK.NetworkManager.Events.DeviceBoundSessionEventOccurred, this.#onEventOccurred, this);
40
+ }
41
+
42
+ addVisibleSite(site: string): void {
43
+ if (this.#visibleSites.has(site)) {
44
+ return;
45
+ }
46
+ this.#visibleSites.add(site);
47
+ this.dispatchEventToListeners(DeviceBoundSessionModelEvents.ADD_VISIBLE_SITE, {site});
48
+ }
49
+
50
+ clearVisibleSites(): void {
51
+ if (this.getPreserveLogSetting().get()) {
52
+ return;
53
+ }
54
+ this.#visibleSites.clear();
55
+ this.dispatchEventToListeners(DeviceBoundSessionModelEvents.CLEAR_VISIBLE_SITES);
56
+ }
57
+
58
+ clearEvents(): void {
59
+ if (this.getPreserveLogSetting().get()) {
60
+ return;
61
+ }
62
+ const emptySessions = new Map<string, Array<string|undefined>>();
63
+ const emptySites = new Set<string>();
64
+ for (const [site, sessionIdToSessionMap] of [...this.#siteSessions]) {
65
+ let emptySessionsSiteEntry = emptySessions.get(site);
66
+ for (const [sessionId, sessionAndEvents] of sessionIdToSessionMap) {
67
+ sessionAndEvents.eventsById.clear();
68
+ if (sessionAndEvents.session) {
69
+ continue;
70
+ }
71
+ // Remove empty sessions.
72
+ sessionIdToSessionMap.delete(sessionId);
73
+ if (!emptySessionsSiteEntry) {
74
+ emptySessionsSiteEntry = [];
75
+ emptySessions.set(site, emptySessionsSiteEntry);
76
+ }
77
+ emptySessionsSiteEntry.push(sessionId);
78
+ }
79
+
80
+ // Remove empty sites.
81
+ if (sessionIdToSessionMap.size === 0) {
82
+ this.#siteSessions.delete(site);
83
+ emptySites.add(site);
84
+ }
85
+ }
86
+
87
+ this.dispatchEventToListeners(DeviceBoundSessionModelEvents.CLEAR_EVENTS, {emptySessions, emptySites});
88
+ }
89
+
90
+ isSiteVisible(site: string): boolean {
91
+ return this.#visibleSites.has(site);
92
+ }
93
+
94
+ getSession(site: string, sessionId?: string): SessionAndEvents|undefined {
95
+ return this.#siteSessions.get(site)?.get(sessionId);
96
+ }
97
+
98
+ getPreserveLogSetting(): Common.Settings.Setting<boolean> {
99
+ return Common.Settings.Settings.instance().createSetting('device-bound-sessions-preserve-log', false);
100
+ }
101
+
102
+ #onSessionsSet({data: sessions}: {data: Protocol.Network.DeviceBoundSession[]}): void {
103
+ for (const session of sessions) {
104
+ const sessionAndEvents = this.#ensureSiteAndSessionInitialized(session.key.site, session.key.id);
105
+ sessionAndEvents.session = session;
106
+ }
107
+ this.dispatchEventToListeners(DeviceBoundSessionModelEvents.INITIALIZE_SESSIONS, {sessions});
108
+ }
109
+
110
+ #ensureSiteAndSessionInitialized(site: string, sessionId?: string): SessionAndEvents {
111
+ let sessionIdToSessionMap = this.#siteSessions.get(site);
112
+ if (!sessionIdToSessionMap) {
113
+ sessionIdToSessionMap = new Map();
114
+ this.#siteSessions.set(site, sessionIdToSessionMap);
115
+ }
116
+
117
+ let sessionAndEvent = sessionIdToSessionMap.get(sessionId);
118
+ if (!sessionAndEvent) {
119
+ sessionAndEvent = {session: undefined, eventsById: new Map<string, EventWithTimestamp>()};
120
+ sessionIdToSessionMap.set(sessionId, sessionAndEvent);
121
+ }
122
+ return sessionAndEvent;
123
+ }
124
+
125
+ #onEventOccurred({data: event}: {data: Protocol.Network.DeviceBoundSessionEventOccurredEvent}): void {
126
+ const sessionAndEvent = this.#ensureSiteAndSessionInitialized(event.site, event.sessionId);
127
+
128
+ // If this eventId has already been tracked, quit early.
129
+ if (sessionAndEvent.eventsById.has(event.eventId)) {
130
+ return;
131
+ }
132
+
133
+ // Add the new event.
134
+ const eventWithTimestamp = {event, timestamp: new Date()};
135
+ sessionAndEvent.eventsById.set(event.eventId, eventWithTimestamp);
136
+
137
+ // Add the new session if there is one.
138
+ const newSession = event.creationEventDetails?.newSession || event.refreshEventDetails?.newSession;
139
+ if (newSession) {
140
+ sessionAndEvent.session = newSession;
141
+ }
142
+
143
+ // Add the new challenge onto the session if there is one.
144
+ if (event.succeeded && sessionAndEvent.session && event.challengeEventDetails) {
145
+ sessionAndEvent.session.cachedChallenge = event.challengeEventDetails.challenge;
146
+ }
147
+
148
+ this.dispatchEventToListeners(
149
+ DeviceBoundSessionModelEvents.EVENT_OCCURRED,
150
+ {site: eventWithTimestamp.event.site, sessionId: eventWithTimestamp.event.sessionId});
151
+ }
152
+ }
153
+
154
+ export const enum DeviceBoundSessionModelEvents {
155
+ INITIALIZE_SESSIONS = 'INITIALIZE_SESSIONS',
156
+ ADD_VISIBLE_SITE = 'ADD_VISIBLE_SITE',
157
+ CLEAR_VISIBLE_SITES = 'CLEAR_VISIBLE_SITES',
158
+ EVENT_OCCURRED = 'EVENT_OCCURRED',
159
+ CLEAR_EVENTS = 'CLEAR_EVENTS',
160
+ }
161
+
162
+ export interface DeviceBoundSessionModelEventTypes {
163
+ [DeviceBoundSessionModelEvents.INITIALIZE_SESSIONS]: {sessions: Protocol.Network.DeviceBoundSession[]};
164
+ [DeviceBoundSessionModelEvents.ADD_VISIBLE_SITE]: {site: string};
165
+ [DeviceBoundSessionModelEvents.CLEAR_VISIBLE_SITES]: void;
166
+ [DeviceBoundSessionModelEvents.EVENT_OCCURRED]: {site: string, sessionId?: string};
167
+ [DeviceBoundSessionModelEvents.CLEAR_EVENTS]:
168
+ {emptySessions: Map<string, Array<string|undefined>>, emptySites: Set<string>};
169
+ }