chrome-devtools-frontend 1.0.1575635 → 1.0.1576287

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 (44) hide show
  1. package/front_end/core/host/AidaClient.ts +21 -0
  2. package/front_end/core/sdk/NetworkManager.ts +3 -1
  3. package/front_end/core/sdk/NetworkRequest.ts +7 -0
  4. package/front_end/core/sdk/SourceMapScopesInfo.ts +2 -1
  5. package/front_end/generated/SupportedCSSProperties.js +7 -0
  6. package/front_end/models/ai_assistance/agents/AiAgent.ts +6 -0
  7. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +6 -0
  8. package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
  9. package/front_end/models/source_map_scopes/NamesResolver.ts +5 -8
  10. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -2
  11. package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +22 -1
  12. package/front_end/panels/application/DeviceBoundSessionsView.ts +44 -33
  13. package/front_end/panels/application/deviceBoundSessionsView.css +1 -1
  14. package/front_end/panels/common/AiCodeGenerationUpgradeDialog.ts +12 -4
  15. package/front_end/panels/common/GeminiRebrandPromoDialog.ts +5 -5
  16. package/front_end/panels/network/NetworkItemView.ts +32 -3
  17. package/front_end/panels/network/RequestDeviceBoundSessionsView.ts +160 -0
  18. package/front_end/panels/network/RequestPayloadView.ts +72 -97
  19. package/front_end/panels/network/forward/UIRequestLocation.ts +1 -0
  20. package/front_end/panels/network/network.ts +3 -0
  21. package/front_end/panels/network/requestDeviceBoundSessionsView.css +14 -0
  22. package/front_end/panels/settings/AISettingsTab.ts +2 -2
  23. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +7 -0
  24. package/front_end/panels/whats_new/resources/WNDT.md +1 -1
  25. package/front_end/third_party/chromium/README.chromium +1 -1
  26. package/front_end/third_party/source-map-scopes-codec/README.chromium +2 -2
  27. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts +2 -2
  28. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts.map +1 -1
  29. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/mod.d.ts +1 -1
  30. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/mod.d.ts.map +1 -1
  31. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/scopes.d.ts +9 -1
  32. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/scopes.d.ts.map +1 -1
  33. package/front_end/third_party/source-map-scopes-codec/package/deno.json +8 -1
  34. package/front_end/third_party/source-map-scopes-codec/package/package.json +1 -1
  35. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +20 -7
  36. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js.map +1 -1
  37. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.ts +29 -9
  38. package/front_end/third_party/source-map-scopes-codec/package/src/mod.js.map +1 -1
  39. package/front_end/third_party/source-map-scopes-codec/package/src/mod.ts +1 -0
  40. package/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +1 -1
  41. package/front_end/third_party/source-map-scopes-codec/package/src/scopes.ts +12 -1
  42. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +37 -5
  43. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  44. package/package.json +1 -1
@@ -656,6 +656,13 @@ export class AidaClient {
656
656
  }
657
657
 
658
658
  registerClientEvent(clientEvent: AidaRegisterClientEvent): Promise<AidaClientResult> {
659
+ // Disable logging for now.
660
+ // For context, see b/454563259#comment35.
661
+ // We should be able to remove this ~end of April.
662
+ if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
663
+ clientEvent.disable_user_content_logging = true;
664
+ }
665
+
659
666
  const {promise, resolve} = Promise.withResolvers<AidaClientResult>();
660
667
  InspectorFrontendHostInstance.registerAidaClientEvent(
661
668
  JSON.stringify({
@@ -673,6 +680,14 @@ export class AidaClient {
673
680
  if (!InspectorFrontendHostInstance.aidaCodeComplete) {
674
681
  throw new Error('aidaCodeComplete is not available');
675
682
  }
683
+
684
+ // Disable logging for now.
685
+ // For context, see b/454563259#comment35.
686
+ // We should be able to remove this ~end of April.
687
+ if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
688
+ request.metadata.disable_user_content_logging = true;
689
+ }
690
+
676
691
  const {promise, resolve} = Promise.withResolvers<AidaCodeCompleteResult>();
677
692
  InspectorFrontendHostInstance.aidaCodeComplete(JSON.stringify(request), resolve);
678
693
  const completeCodeResult = await promise;
@@ -718,6 +733,12 @@ export class AidaClient {
718
733
 
719
734
  async generateCode(request: GenerateCodeRequest, options?: {signal?: AbortSignal}):
720
735
  Promise<GenerateCodeResponse|null> {
736
+ // Disable logging for now.
737
+ // For context, see b/454563259#comment35.
738
+ // We should be able to remove this ~end of April.
739
+ if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
740
+ request.metadata.disable_user_content_logging = true;
741
+ }
721
742
  const response = await DispatchHttpRequestClient.makeHttpRequest<GenerateCodeResponse>(
722
743
  {
723
744
  service: SERVICE_NAME,
@@ -1022,6 +1022,7 @@ export class NetworkDispatcher implements ProtocolProxyApi.NetworkDispatcher {
1022
1022
  requestId,
1023
1023
  associatedCookies,
1024
1024
  headers,
1025
+ deviceBoundSessionUsages,
1025
1026
  clientSecurityState,
1026
1027
  connectTiming,
1027
1028
  siteHasCookieInOtherPartition,
@@ -1036,10 +1037,11 @@ export class NetworkDispatcher implements ProtocolProxyApi.NetworkDispatcher {
1036
1037
  blockedRequestCookies.push({blockedReasons, cookie: Cookie.fromProtocolCookie(cookie)});
1037
1038
  }
1038
1039
  }
1039
- const extraRequestInfo = {
1040
+ const extraRequestInfo: ExtraRequestInfo = {
1040
1041
  blockedRequestCookies,
1041
1042
  includedRequestCookies,
1042
1043
  requestHeaders: this.headersMapToHeadersArray(headers),
1044
+ deviceBoundSessionUsages,
1043
1045
  clientSecurityState,
1044
1046
  connectTiming,
1045
1047
  siteHasCookieInOtherPartition,
@@ -258,6 +258,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
258
258
  #exemptedResponseCookies: ExemptedSetCookieWithReason[] = [];
259
259
  #responseCookiesPartitionKey: Protocol.Network.CookiePartitionKey|null = null;
260
260
  #responseCookiesPartitionKeyOpaque: boolean|null = null;
261
+ #deviceBoundSessionUsages: Protocol.Network.DeviceBoundSessionWithUsage[] = [];
261
262
  #siteHasCookieInOtherPartition = false;
262
263
  localizedFailDescription: string|null = null;
263
264
  #url!: Platform.DevToolsPath.UrlString;
@@ -1612,6 +1613,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
1612
1613
  this.setRequestHeaders(extraRequestInfo.requestHeaders);
1613
1614
  this.#hasExtraRequestInfo = true;
1614
1615
  this.setRequestHeadersText(''); // Mark request headers as non-provisional
1616
+ this.#deviceBoundSessionUsages = extraRequestInfo.deviceBoundSessionUsages || [];
1615
1617
  this.#clientSecurityState = extraRequestInfo.clientSecurityState;
1616
1618
  this.#appliedNetworkConditionsId = extraRequestInfo.appliedNetworkConditionsId;
1617
1619
  if (extraRequestInfo.connectTiming) {
@@ -1630,6 +1632,10 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
1630
1632
  this.#appliedNetworkConditionsId = appliedNetworkConditionsId;
1631
1633
  }
1632
1634
 
1635
+ getDeviceBoundSessionUsages(): Protocol.Network.DeviceBoundSessionWithUsage[] {
1636
+ return this.#deviceBoundSessionUsages;
1637
+ }
1638
+
1633
1639
  hasExtraRequestInfo(): boolean {
1634
1640
  return this.#hasExtraRequestInfo;
1635
1641
  }
@@ -2151,6 +2157,7 @@ export interface ExtraRequestInfo {
2151
2157
  blockedRequestCookies: Array<{blockedReasons: Protocol.Network.CookieBlockedReason[], cookie: Cookie}>;
2152
2158
  requestHeaders: NameValue[];
2153
2159
  includedRequestCookies: IncludedCookieWithReason[];
2160
+ deviceBoundSessionUsages?: Protocol.Network.DeviceBoundSessionWithUsage[];
2154
2161
  clientSecurityState?: Protocol.Network.ClientSecurityState;
2155
2162
  connectTiming: Protocol.Network.ConnectTiming;
2156
2163
  siteHasCookieInOtherPartition?: boolean;
@@ -190,7 +190,8 @@ export class SourceMapScopesInfo {
190
190
  }
191
191
 
192
192
  isEmpty(): boolean {
193
- return !this.#originalScopes.length && !this.#generatedRanges.length;
193
+ const noScopes = this.#originalScopes.every(scope => scope === null);
194
+ return noScopes && !this.#generatedRanges.length;
194
195
  }
195
196
 
196
197
  addOriginalScopesAtIndex(sourceIdx: number, scope: ScopesCodec.OriginalScope): void {
@@ -3925,6 +3925,13 @@ export const generatedProperties = [
3925
3925
  ],
3926
3926
  "name": "rule-style"
3927
3927
  },
3928
+ {
3929
+ "longhands": [
3930
+ "column-rule-visibility-items",
3931
+ "row-rule-visibility-items"
3932
+ ],
3933
+ "name": "rule-visibility-items"
3934
+ },
3928
3935
  {
3929
3936
  "longhands": [
3930
3937
  "column-rule-width",
@@ -309,6 +309,12 @@ export abstract class AiAgent<T> {
309
309
  constructor(opts: AgentOptions) {
310
310
  this.#aidaClient = opts.aidaClient;
311
311
  this.#serverSideLoggingEnabled = opts.serverSideLoggingEnabled ?? false;
312
+ // Disable logging for now.
313
+ // For context, see b/454563259#comment35.
314
+ // We should be able to remove this ~end of April.
315
+ if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
316
+ this.#serverSideLoggingEnabled = false;
317
+ }
312
318
  this.#sessionId = opts.sessionId ?? crypto.randomUUID();
313
319
  this.confirmSideEffect = opts.confirmSideEffectForTest ?? (() => Promise.withResolvers());
314
320
  }
@@ -54,6 +54,12 @@ export class AiCodeGeneration {
54
54
  constructor(opts: Options) {
55
55
  this.#aidaClient = opts.aidaClient;
56
56
  this.#serverSideLoggingEnabled = opts.serverSideLoggingEnabled ?? false;
57
+ // Disable logging for now.
58
+ // For context, see b/454563259#comment35.
59
+ // We should be able to remove this ~end of April.
60
+ if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
61
+ this.#serverSideLoggingEnabled = false;
62
+ }
57
63
  }
58
64
 
59
65
  #buildRequest(
@@ -7189,6 +7189,10 @@ export const NativeFunctions = [
7189
7189
  name: "setMenuListOptionsBoundsInAXTree",
7190
7190
  signatures: [["options_bounds","children_updated"]]
7191
7191
  },
7192
+ {
7193
+ name: "debugLog",
7194
+ signatures: [["message"]]
7195
+ },
7192
7196
  {
7193
7197
  name: "allowsFeature",
7194
7198
  signatures: [["feature","?origin"]]
@@ -3,7 +3,6 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
- import * as Root from '../../core/root/root.js';
7
6
  import * as SDK from '../../core/sdk/sdk.js';
8
7
  import * as Protocol from '../../generated/protocol.js';
9
8
  import * as Bindings from '../bindings/bindings.js';
@@ -366,17 +365,15 @@ const resolveScope = async(script: SDK.Script.Script, scopeChain: Formatter.Form
366
365
  export const resolveScopeChain =
367
366
  async function(callFrame: SDK.DebuggerModel.CallFrame): Promise<SDK.DebuggerModel.ScopeChainEntry[]> {
368
367
  const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
369
- let scopeChain: SDK.DebuggerModel.ScopeChainEntry[]|null|undefined = await pluginManager.resolveScopeChain(callFrame);
368
+ const scopeChain: SDK.DebuggerModel.ScopeChainEntry[]|null|undefined =
369
+ await pluginManager.resolveScopeChain(callFrame);
370
370
  if (scopeChain) {
371
371
  return scopeChain;
372
372
  }
373
373
 
374
- scopeChain = Root.Runtime.experiments.isEnabled(Root.ExperimentNames.ExperimentName.USE_SOURCE_MAP_SCOPES) ?
375
- callFrame.script.sourceMap()?.resolveScopeChain(callFrame) :
376
- null;
377
- if (scopeChain) {
378
- return scopeChain;
379
- }
374
+ // TODO(crbug.com/465968290): Re-enable creating the scope chain from the source map once:
375
+ // 1) We have a flag indicating whether the source map contained variable/binding information.
376
+ // 2) We have a chrome feature flag.
380
377
 
381
378
  if (callFrame.script.isWasm()) {
382
379
  return callFrame.scopeChain();
@@ -204,7 +204,16 @@ const UIStringsNotTranslate = {
204
204
  */
205
205
  inputDisclaimerForPerformanceEnterpriseNoLogging:
206
206
  'Chat messages and data from your performance trace are sent to Google. The content you submit and that is generated by this feature will not be used to improve Google’s AI models. This is an experimental AI feature and won’t always get it right.',
207
-
207
+ /**
208
+ * @description Disclaimer text right after the chat input.
209
+ */
210
+ inputDisclaimerForNoContext:
211
+ 'Chat messages, any data the inspected page can see using Web APIs, and the items you select such as files, network requests, and performance traces are sent to Google and may be seen by human reviewers to improve this feature. This is an experimental AI feature and won’t always get it right.',
212
+ /**
213
+ * @description Disclaimer text right after the chat input.
214
+ */
215
+ inputDisclaimerForNoContextEnterpriseNoLogging:
216
+ 'Chat messages, any data the inspected page can see using Web APIs, and the items you select such as files, network requests, and performance traces are sent to Google. This data will not be used to improve Google’s AI models. This is an experimental AI feature and won’t always get it right.',
208
217
  } as const;
209
218
 
210
219
  const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/AiAssistancePanel.ts', UIStrings);
@@ -1037,7 +1046,10 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1037
1046
  return lockedString(UIStringsNotTranslate.inputDisclaimerForPerformance);
1038
1047
 
1039
1048
  case AiAssistanceModel.AiHistoryStorage.ConversationType.NONE:
1040
- return lockedString(UIStringsNotTranslate.inputDisclaimerForPerformance);
1049
+ if (noLogging) {
1050
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForNoContextEnterpriseNoLogging);
1051
+ }
1052
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForNoContext);
1041
1053
  }
1042
1054
  }
1043
1055
 
@@ -6,6 +6,7 @@ import type * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import type * as Platform from '../../core/platform/platform.js';
8
8
  import {createIcon} from '../../ui/kit/kit.js';
9
+ import * as UI from '../../ui/legacy/legacy.js';
9
10
 
10
11
  import {ApplicationPanelTreeElement} from './ApplicationPanelTreeElement.js';
11
12
  import {
@@ -46,6 +47,16 @@ const UIStrings = {
46
47
  * linked to a session.
47
48
  */
48
49
  noSession: 'No session',
50
+ /**
51
+ *@description Tooltip text for a terminated session.
52
+ *@example {session_1} sessionName
53
+ */
54
+ terminatedSession: '{sessionName}, Session terminated',
55
+ /**
56
+ *@description Tooltip text for a session with errors.
57
+ *@example {session_1} sessionName
58
+ */
59
+ sessionWithErrors: '{sessionName}, Session has errors',
49
60
  } as const;
50
61
 
51
62
  const str_ = i18n.i18n.registerUIStrings('panels/application/DeviceBoundSessionsTreeElement.ts', UIStrings);
@@ -122,13 +133,23 @@ export class RootTreeElement extends ApplicationPanelTreeElement {
122
133
 
123
134
  #updateElementIconAndStyling(
124
135
  sessionElement: ApplicationPanelTreeElement, isSessionTerminated: boolean, sessionHasErrors: boolean): void {
136
+ const title = sessionElement.title as string;
125
137
  if (isSessionTerminated) {
126
138
  sessionElement.listItemElement.classList.add('device-bound-session-terminated');
127
139
  sessionElement.setLeadingIcons([createIcon('database-off')]);
140
+ const terminatedTitle = i18nString(UIStrings.terminatedSession, {sessionName: title});
141
+ UI.ARIAUtils.setLabel(sessionElement.listItemElement, terminatedTitle);
128
142
  return;
129
143
  }
130
144
  sessionElement.listItemElement.classList.remove('device-bound-session-terminated');
131
- sessionElement.setLeadingIcons([createIcon(sessionHasErrors ? 'warning' : 'database')]);
145
+ if (sessionHasErrors) {
146
+ sessionElement.setLeadingIcons([createIcon('warning')]);
147
+ const errorTitle = i18nString(UIStrings.sessionWithErrors, {sessionName: title});
148
+ UI.ARIAUtils.setLabel(sessionElement.listItemElement, errorTitle);
149
+ } else {
150
+ sessionElement.setLeadingIcons([createIcon('database')]);
151
+ UI.ARIAUtils.setLabel(sessionElement.listItemElement, title);
152
+ }
132
153
  }
133
154
 
134
155
  #updateIconAndStyling(site: string, sessionId: string|undefined): void {
@@ -125,6 +125,10 @@ const UIStrings = {
125
125
  *@description Section header for details about an event.
126
126
  */
127
127
  eventDetails: 'Event details',
128
+ /**
129
+ *@description Accessible label for the main area containing session details.
130
+ */
131
+ sessionDetails: 'Session details',
128
132
  /**
129
133
  *@description Placeholder text when no row is selected in a table of events.
130
134
  */
@@ -576,7 +580,8 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
576
580
  const {key, inclusionRules, cookieCravings} = sessionAndEvents.session;
577
581
  sessionDetailsHtml = html`
578
582
  <devtools-report>
579
- <devtools-report-section-header>${i18nString(UIStrings.sessionConfig)}</devtools-report-section-header>
583
+ <devtools-report-section-header role="heading" aria-level="2">${
584
+ i18nString(UIStrings.sessionConfig)}</devtools-report-section-header>
580
585
  <devtools-report-key>${i18nString(UIStrings.keySite)}</devtools-report-key>
581
586
  <devtools-report-value>${key.site}</devtools-report-value>
582
587
  <devtools-report-key>${i18nString(UIStrings.keyId)}</devtools-report-key>
@@ -590,7 +595,8 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
590
595
  <devtools-report-value>${sessionAndEvents.session.cachedChallenge || ''}</devtools-report-value>
591
596
  <devtools-report-key>${i18nString(UIStrings.allowedRefreshInitiators)}</devtools-report-key>
592
597
  <devtools-report-value>${sessionAndEvents.session.allowedRefreshInitiators.join(', ')}</devtools-report-value>
593
- <devtools-report-section-header>${i18nString(UIStrings.scope)}</devtools-report-section-header>
598
+ <devtools-report-section-header role="heading" aria-level="2">${
599
+ i18nString(UIStrings.scope)}</devtools-report-section-header>
594
600
  <devtools-report-key>${i18nString(UIStrings.origin)}</devtools-report-key>
595
601
  <devtools-report-value>${inclusionRules.origin}</devtools-report-value>
596
602
  <devtools-report-key>${i18nString(UIStrings.includeSite)}</devtools-report-key>
@@ -599,13 +605,14 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
599
605
  ${
600
606
  inclusionRules.urlRules.length > 0 ? html`
601
607
  <div class="device-bound-session-grid-wrapper">
602
- <devtools-data-grid class="device-bound-session-url-rules-grid" striped inline>
608
+ <devtools-data-grid class="device-bound-session-url-rules-grid" striped inline name=${
609
+ i18nString(UIStrings.scope)}>
603
610
  <table>
604
611
  <thead>
605
612
  <tr>
606
- <th id="should-include" weight="1" sortable>${i18nString(UIStrings.ruleType)}</th>
607
- <th id="host-pattern" weight="2" sortable>${i18nString(UIStrings.ruleHostPattern)}</th>
608
- <th id="path-prefix" weight="2" sortable>${i18nString(UIStrings.rulePathPrefix)}</th>
613
+ <th id="should-include" sortable>${i18nString(UIStrings.ruleType)}</th>
614
+ <th id="host-pattern" sortable>${i18nString(UIStrings.ruleHostPattern)}</th>
615
+ <th id="path-prefix" sortable>${i18nString(UIStrings.rulePathPrefix)}</th>
609
616
  </tr>
610
617
  </thead>
611
618
  <tbody>
@@ -622,22 +629,22 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
622
629
  </div>
623
630
  ` :
624
631
  nothing}
625
- <devtools-report-section-header>${i18nString(UIStrings.cookieCravings)}</devtools-report-section-header>
632
+ <devtools-report-section-header role="heading" aria-level="2">${
633
+ i18nString(UIStrings.cookieCravings)}</devtools-report-section-header>
626
634
  ${
627
635
  cookieCravings.length > 0 ? html`
628
636
  <div class="device-bound-session-grid-wrapper">
629
- <devtools-data-grid class="device-bound-session-cookie-cravings-grid" striped inline>
637
+ <devtools-data-grid class="device-bound-session-cookie-cravings-grid" striped inline name=${
638
+ i18nString(UIStrings.cookieCravings)}>
630
639
  <table>
631
640
  <thead>
632
641
  <tr>
633
- <th id="name" weight="2" sortable>${i18nString(UIStrings.name)}</th>
634
- <th id="domain" weight="2" sortable>${i18n.i18n.lockedString('Domain')}</th>
635
- <th id="path" weight="2" sortable>${i18n.i18n.lockedString('Path')}</th>
636
- <th id="secure" type="boolean" align="center" weight="1" sortable>${
637
- i18n.i18n.lockedString('Secure')}</th>
638
- <th id="http-only" type="boolean" align="center" weight="1" sortable>${
639
- i18n.i18n.lockedString('HttpOnly')}</th>
640
- <th id="same-site" weight="1" sortable>${i18n.i18n.lockedString('SameSite')}</th>
642
+ <th id="name" sortable>${i18nString(UIStrings.name)}</th>
643
+ <th id="domain" sortable>${i18n.i18n.lockedString('Domain')}</th>
644
+ <th id="path" sortable>${i18n.i18n.lockedString('Path')}</th>
645
+ <th id="secure" type="boolean" align="center" sortable>${i18n.i18n.lockedString('Secure')}</th>
646
+ <th id="http-only" type="boolean" align="center" sortable>${i18n.i18n.lockedString('HttpOnly')}</th>
647
+ <th id="same-site" sortable>${i18n.i18n.lockedString('SameSite')}</th>
641
648
  </tr>
642
649
  </thead>
643
650
  <tbody>
@@ -660,27 +667,28 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
660
667
  }
661
668
  const events = [...sessionAndEvents.eventsById.values()];
662
669
  const eventsHtml = html`
663
- <devtools-report-section-header>${i18nString(UIStrings.events)}</devtools-report-section-header>
670
+ <devtools-report-section-header role="heading" aria-level="2">${
671
+ i18nString(UIStrings.events)}</devtools-report-section-header>
664
672
  ${
665
673
  events.length > 0 && onEventRowSelected ?
666
674
  html`
667
675
  <div class="device-bound-session-grid-wrapper">
668
- <devtools-data-grid class="device-bound-session-events-grid" striped inline ${
669
- Directives.ref((el?: Element) => {
670
- if (!el || !(el instanceof HTMLElement)) {
671
- return;
672
- }
673
- const grid = el as HTMLElement & {deselectRow(): void};
674
- if (!selectedEvent) {
675
- grid.deselectRow();
676
- }
677
- })}>
676
+ <devtools-data-grid class="device-bound-session-events-grid" striped inline name=${
677
+ i18nString(UIStrings.events)} ${Directives.ref((el?: Element) => {
678
+ if (!el || !(el instanceof HTMLElement)) {
679
+ return;
680
+ }
681
+ const grid = el as HTMLElement & {deselectRow(): void};
682
+ if (!selectedEvent) {
683
+ grid.deselectRow();
684
+ }
685
+ })}>
678
686
  <table>
679
687
  <thead>
680
688
  <tr>
681
- <th id="type" weight="1" sortable>${i18nString(UIStrings.type)}</th>
682
- <th id="timestamp" weight="2" sortable>${i18nString(UIStrings.timestamp)}</th>
683
- <th id="details" weight="2" sortable>${i18nString(UIStrings.result)}</th>
689
+ <th id="type" sortable>${i18nString(UIStrings.type)}</th>
690
+ <th id="timestamp" sortable>${i18nString(UIStrings.timestamp)}</th>
691
+ <th id="details" sortable>${i18nString(UIStrings.result)}</th>
684
692
  </tr>
685
693
  </thead>
686
694
  <tbody>${events.map(({event, timestamp}) => html`
@@ -764,7 +772,8 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
764
772
  ` :
765
773
  html`<div class="device-bound-session-no-event-details">${i18nString(UIStrings.selectEventToViewDetails)}</div>`;
766
774
  const eventDetailsHtml = html`
767
- <devtools-report-section-header>${i18nString(UIStrings.eventDetails)}</devtools-report-section-header>
775
+ <devtools-report-section-header role="heading" aria-level="2">${
776
+ i18nString(UIStrings.eventDetails)}</devtools-report-section-header>
768
777
  ${eventDetailsContentHtml}
769
778
  `;
770
779
 
@@ -774,11 +783,13 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
774
783
  <style>${deviceBoundSessionsViewStyles}</style>
775
784
  ${toolbarHtml}
776
785
  <devtools-split-view sidebar-position="second">
777
- <div slot="main" class="device-bound-session-view-wrapper">
786
+ <div slot="main" class="device-bound-session-view-wrapper" role="region" aria-label=${
787
+ i18nString(UIStrings.sessionDetails)}>
778
788
  ${sessionDetailsHtml || nothing}
779
789
  ${eventsHtml}
780
790
  </div>
781
- <div slot="sidebar" class="device-bound-session-sidebar">
791
+ <div slot="sidebar" class="device-bound-session-sidebar" role="region" aria-label=${
792
+ i18nString(UIStrings.eventDetails)}>
782
793
  ${eventDetailsHtml}
783
794
  </div>
784
795
  </devtools-split-view>`,
@@ -4,7 +4,7 @@
4
4
  * found in the LICENSE file.
5
5
  */
6
6
  .device-bound-session-grid-wrapper {
7
- padding: 0 20px 5px;
7
+ margin: 0 20px 5px;
8
8
  }
9
9
 
10
10
  .device-bound-session-grid-wrapper devtools-data-grid {
@@ -55,9 +55,10 @@ const UIStringsNotTranslate = {
55
55
  const lockedString = i18n.i18n.lockedString;
56
56
 
57
57
  export class AiCodeGenerationUpgradeDialog {
58
- static show({noLogging}: {noLogging: boolean}): void {
58
+ static show({noLogging}: {noLogging: boolean}): Promise<boolean> {
59
59
  const dialog = new UI.Dialog.Dialog();
60
60
  dialog.setAriaLabel(lockedString(UIStringsNotTranslate.codeCompletionJustGotBetter));
61
+ const result = Promise.withResolvers<boolean>();
61
62
  // clang-format off
62
63
  Lit.render(html`
63
64
  <div class="ai-code-generation-upgrade-dialog">
@@ -96,7 +97,8 @@ export class AiCodeGenerationUpgradeDialog {
96
97
  <div class="right-buttons">
97
98
  <devtools-button
98
99
  @click=${() => {
99
- void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
100
+ result.resolve(true);
101
+ void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
100
102
  }}
101
103
  jslogcontext="ai-code-generation-upgrade-dialog.manage-in-settings"
102
104
  .variant=${Buttons.Button.Variant.OUTLINED}
@@ -105,6 +107,7 @@ export class AiCodeGenerationUpgradeDialog {
105
107
  </devtools-button>
106
108
  <devtools-button
107
109
  @click=${() => {
110
+ result.resolve(true);
108
111
  dialog.hide();
109
112
  }}
110
113
  jslogcontext="ai-code-generation-upgrade-dialog.continue"
@@ -117,13 +120,18 @@ export class AiCodeGenerationUpgradeDialog {
117
120
  // clang-format on
118
121
 
119
122
  dialog.setOutsideClickCallback(ev => {
120
- ev.consume(true); // true = preventDefault()
121
- dialog.hide();
123
+ ev.consume(true);
124
+ });
125
+
126
+ dialog.setOnHideCallback(() => {
127
+ result.resolve(false);
122
128
  });
123
129
 
124
130
  dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.MEASURE_CONTENT);
125
131
  dialog.setDimmed(true);
126
132
  dialog.show();
133
+
134
+ return result.promise;
127
135
  }
128
136
 
129
137
  private constructor() {
@@ -81,7 +81,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
81
81
  .variant=${Buttons.Button.Variant.ICON}
82
82
  .size=${Buttons.Button.Size.REGULAR}
83
83
  .title=${i18nString(UIStrings.dismiss)}
84
- jslog=${VisualLogging.close().track({click: true})}
84
+ jslog=${VisualLogging.close().track({click: true}).context('gemini-promo-dismiss')}
85
85
  @click=${() => input.onCancelClick()}
86
86
  ></devtools-button>
87
87
  </div>
@@ -111,11 +111,11 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
111
111
  <div class="buttons">
112
112
  <devtools-button
113
113
  .variant=${Buttons.Button.Variant.OUTLINED}
114
- .jslogContext=${'cancel'}
114
+ jslog=${VisualLogging.close().track({click: true}).context('gemini-promo-dismiss')}
115
115
  @click=${input.onCancelClick}>${i18nString(UIStrings.dismiss)}</devtools-button>
116
116
  <devtools-button
117
117
  .variant=${Buttons.Button.Variant.PRIMARY}
118
- .jslogContext=${'get-started'}
118
+ .jslogContext=${'gemini-promo-get-started'}
119
119
  @click=${input.onGetStartedClick}>${i18nString(UIStrings.getStarted)}</devtools-button>
120
120
  </div>
121
121
  `,
@@ -160,7 +160,7 @@ export class GeminiRebrandPromoDialog extends UI.Widget.VBox {
160
160
  }
161
161
 
162
162
  static show(): void {
163
- const dialog = new UI.Dialog.Dialog('gemini-rebranding-dialog');
163
+ const dialog = new UI.Dialog.Dialog('gemini-promo-dialog');
164
164
  dialog.setAriaLabel(i18nString(UIStrings.dialogAriaLabel));
165
165
  dialog.setMaxContentSize(new Geometry.Size(384, 500));
166
166
  dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SET_EXACT_WIDTH_MAX_HEIGHT);
@@ -181,7 +181,7 @@ export class GeminiRebrandPromoDialog extends UI.Widget.VBox {
181
181
  }
182
182
 
183
183
  const setting = Common.Settings.Settings.instance().createSetting<boolean>(
184
- 'gemini-rebranding-dialog-shown', false, Common.Settings.SettingStorageType.SYNCED);
184
+ 'gemini-promo-dialog-shown', false, Common.Settings.SettingStorageType.SYNCED);
185
185
  if (setting.get()) {
186
186
  return;
187
187
  }
@@ -21,6 +21,7 @@ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
21
21
  import * as NetworkComponents from './components/components.js';
22
22
  import {EventSourceMessagesView} from './EventSourceMessagesView.js';
23
23
  import {RequestCookiesView} from './RequestCookiesView.js';
24
+ import {RequestDeviceBoundSessionsView} from './RequestDeviceBoundSessionsView.js';
24
25
  import {RequestInitiatorView} from './RequestInitiatorView.js';
25
26
  import {RequestPayloadView} from './RequestPayloadView.js';
26
27
  import {RequestPreviewView} from './RequestPreviewView.js';
@@ -114,6 +115,17 @@ const UIStrings = {
114
115
  * @description Text for web cookies
115
116
  */
116
117
  cookies: 'Cookies',
118
+ /**
119
+ * @description Title of the Device Bound Sessions tab in the Network panel. A
120
+ * website may decide to create a session for a user, for example when the user
121
+ * logs in. They can use a protocol to make it a "device bound session". That
122
+ * means that when the session expires, it is only possible for it to be
123
+ * extended on the device it was created on. Thus the session is considered
124
+ * to be bound to that device. For more details on the protocol, see
125
+ * https://github.com/w3c/webappsec-dbsc/blob/main/README.md and
126
+ * https://w3c.github.io/webappsec-dbsc/.
127
+ */
128
+ deviceBoundSessions: 'Device bound sessions',
117
129
  /**
118
130
  * @description Text in Network Item View of the Network panel
119
131
  */
@@ -140,6 +152,7 @@ export class NetworkItemView extends UI.TabbedPane.TabbedPane {
140
152
  #payloadView: RequestPayloadView|null = null;
141
153
  readonly #responseView: RequestResponseView|undefined;
142
154
  #cookiesView: RequestCookiesView|null = null;
155
+ #deviceBoundSessionsView: RequestDeviceBoundSessionsView|null = null;
143
156
  #initialTab?: NetworkForward.UIRequestLocation.UIRequestTabs;
144
157
  readonly #firstTab: NetworkForward.UIRequestLocation.UIRequestTabs;
145
158
 
@@ -253,10 +266,10 @@ export class NetworkItemView extends UI.TabbedPane.TabbedPane {
253
266
  super.wasShown();
254
267
  this.#request.addEventListener(SDK.NetworkRequest.Events.REQUEST_HEADERS_CHANGED, this.requestHeadersChanged, this);
255
268
  this.#request.addEventListener(
256
- SDK.NetworkRequest.Events.RESPONSE_HEADERS_CHANGED, this.maybeAppendCookiesPanel, this);
269
+ SDK.NetworkRequest.Events.RESPONSE_HEADERS_CHANGED, this.maybeAppendCookieResponsePanels, this);
257
270
  this.#request.addEventListener(
258
271
  SDK.NetworkRequest.Events.TRUST_TOKEN_RESULT_ADDED, this.maybeShowErrorIconInTrustTokenTabHeader, this);
259
- this.maybeAppendCookiesPanel();
272
+ this.maybeAppendCookieResponsePanels();
260
273
  this.maybeShowErrorIconInTrustTokenTabHeader();
261
274
 
262
275
  // Only select the initial tab the first time the view is shown after construction.
@@ -283,7 +296,7 @@ export class NetworkItemView extends UI.TabbedPane.TabbedPane {
283
296
  this.#request.removeEventListener(
284
297
  SDK.NetworkRequest.Events.REQUEST_HEADERS_CHANGED, this.requestHeadersChanged, this);
285
298
  this.#request.removeEventListener(
286
- SDK.NetworkRequest.Events.RESPONSE_HEADERS_CHANGED, this.maybeAppendCookiesPanel, this);
299
+ SDK.NetworkRequest.Events.RESPONSE_HEADERS_CHANGED, this.maybeAppendCookieResponsePanels, this);
287
300
  this.#request.removeEventListener(
288
301
  SDK.NetworkRequest.Events.TRUST_TOKEN_RESULT_ADDED, this.maybeShowErrorIconInTrustTokenTabHeader, this);
289
302
  }
@@ -293,6 +306,11 @@ export class NetworkItemView extends UI.TabbedPane.TabbedPane {
293
306
  void this.maybeAppendPayloadPanel();
294
307
  }
295
308
 
309
+ private maybeAppendCookieResponsePanels(): void {
310
+ this.maybeAppendCookiesPanel();
311
+ this.maybeAppendDeviceBoundSessionsPanel();
312
+ }
313
+
296
314
  private maybeAppendCookiesPanel(): void {
297
315
  const cookiesPresent = this.#request.hasRequestCookies() || this.#request.responseCookies.length > 0;
298
316
  console.assert(cookiesPresent || !this.#cookiesView, 'Cookies were introduced in headers and then removed!');
@@ -311,6 +329,17 @@ export class NetworkItemView extends UI.TabbedPane.TabbedPane {
311
329
  }
312
330
  }
313
331
 
332
+ private maybeAppendDeviceBoundSessionsPanel(): void {
333
+ const deviceBoundSessionsPresent = this.#request.getDeviceBoundSessionUsages().length > 0;
334
+ if (deviceBoundSessionsPresent && !this.#deviceBoundSessionsView) {
335
+ this.#deviceBoundSessionsView = new RequestDeviceBoundSessionsView(this.#request);
336
+ this.appendTab(
337
+ NetworkForward.UIRequestLocation.UIRequestTabs.DEVICE_BOUND_SESSIONS,
338
+ i18nString(UIStrings.deviceBoundSessions), this.#deviceBoundSessionsView,
339
+ i18nString(UIStrings.deviceBoundSessions));
340
+ }
341
+ }
342
+
314
343
  private async maybeAppendPayloadPanel(): Promise<void> {
315
344
  if (this.hasTab('payload')) {
316
345
  return;