chrome-devtools-frontend 1.0.1543082 → 1.0.1544076

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 (149) hide show
  1. package/AUTHORS +2 -0
  2. package/front_end/core/common/Gzip.ts +4 -4
  3. package/front_end/core/common/common.ts +0 -2
  4. package/front_end/core/host/AidaClient.ts +10 -7
  5. package/front_end/core/host/DispatchHttpRequestClient.ts +18 -3
  6. package/front_end/core/root/DevToolsContext.ts +60 -0
  7. package/front_end/core/root/Runtime.ts +8 -7
  8. package/front_end/core/root/root.ts +6 -1
  9. package/front_end/core/sdk/CPUThrottlingManager.ts +0 -4
  10. package/front_end/core/sdk/CSSMatchedStyles.ts +7 -9
  11. package/front_end/core/sdk/CSSModel.ts +1 -1
  12. package/front_end/core/sdk/CSSRule.ts +18 -6
  13. package/front_end/core/sdk/ChildTargetManager.ts +2 -2
  14. package/front_end/core/sdk/TargetManager.ts +5 -6
  15. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +2 -0
  16. package/front_end/entrypoints/inspector_main/InspectorMain.ts +1 -13
  17. package/front_end/entrypoints/main/MainImpl.ts +2 -20
  18. package/front_end/foundation/Universe.ts +24 -1
  19. package/front_end/models/ai_assistance/agents/AiAgent.ts +10 -8
  20. package/front_end/models/ai_assistance/agents/PatchAgent.ts +7 -1
  21. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +0 -5
  22. package/front_end/models/ai_assistance/agents/StylingAgent.ts +4 -8
  23. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -1
  24. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +5 -3
  25. package/front_end/models/bindings/CSSWorkspaceBinding.ts +8 -7
  26. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +19 -15
  27. package/front_end/models/bindings/ResourceMapping.ts +57 -15
  28. package/front_end/models/live-metrics/LiveMetrics.ts +12 -20
  29. package/front_end/models/trace/handlers/SamplesHandler.ts +64 -6
  30. package/front_end/models/trace/types/TraceEvents.ts +16 -0
  31. package/front_end/models/workspace/IgnoreListManager.ts +10 -9
  32. package/front_end/models/workspace/WorkspaceImpl.ts +5 -10
  33. package/front_end/panels/accessibility/AccessibilityNodeView.ts +6 -2
  34. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -1
  35. package/front_end/panels/ai_assistance/components/ChatView.ts +2 -4
  36. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +2 -1
  37. package/front_end/panels/animation/AnimationTimeline.ts +6 -6
  38. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -1
  39. package/front_end/panels/application/OpenedWindowDetailsView.ts +0 -2
  40. package/front_end/panels/application/ServiceWorkersView.ts +0 -2
  41. package/front_end/panels/application/StorageView.ts +0 -1
  42. package/front_end/panels/application/components/FrameDetailsView.ts +468 -447
  43. package/front_end/panels/application/components/ReportsGrid.ts +7 -2
  44. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +5 -3
  45. package/front_end/panels/application/components/TrustTokensView.ts +7 -1
  46. package/front_end/panels/application/preloading/PreloadingView.ts +10 -4
  47. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +7 -11
  48. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +15 -3
  49. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +12 -13
  50. package/front_end/panels/{elements → common}/DOMLinkifier.ts +6 -6
  51. package/front_end/panels/common/common.ts +1 -0
  52. package/front_end/panels/console/ConsoleView.ts +9 -7
  53. package/front_end/panels/console/ConsoleViewMessage.ts +23 -13
  54. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +2 -1
  55. package/front_end/panels/elements/ElementsTreeElement.ts +3 -1
  56. package/front_end/panels/elements/StylePropertiesSection.ts +52 -15
  57. package/front_end/panels/elements/StylePropertyTreeElement.ts +8 -3
  58. package/front_end/panels/elements/StylesSidebarPane.ts +24 -14
  59. package/front_end/panels/elements/elements-meta.ts +11 -2
  60. package/front_end/panels/elements/elements.ts +0 -3
  61. package/front_end/panels/explain/components/ConsoleInsight.ts +333 -318
  62. package/front_end/panels/issues/AffectedResourcesView.ts +2 -1
  63. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +2 -1
  64. package/front_end/panels/network/NetworkLogView.ts +1 -1
  65. package/front_end/panels/recorder/RecorderController.ts +7 -1
  66. package/front_end/panels/settings/SettingsScreen.ts +3 -6
  67. package/front_end/panels/settings/components/SyncSection.ts +218 -226
  68. package/front_end/panels/settings/components/syncSection.css +81 -80
  69. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +42 -294
  70. package/front_end/panels/sources/DebuggerPausedMessage.ts +3 -3
  71. package/front_end/panels/sources/DebuggerPlugin.ts +3 -1
  72. package/front_end/panels/sources/ResourceOriginPlugin.ts +7 -3
  73. package/front_end/panels/sources/SourcesPanel.ts +5 -1
  74. package/front_end/panels/timeline/TimelinePanel.ts +0 -21
  75. package/front_end/panels/timeline/TimelineUIUtils.ts +3 -2
  76. package/front_end/panels/timeline/components/LiveMetricsView.ts +7 -4
  77. package/front_end/panels/timeline/components/insights/NodeLink.ts +3 -2
  78. package/front_end/third_party/puppeteer/README.chromium +2 -2
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.d.ts +1 -0
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.d.ts.map +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.js +4 -1
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.js.map +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.d.ts +1 -0
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.d.ts.map +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.js +8 -0
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.js.map +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js +22 -0
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js.map +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +34 -6
  98. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.d.ts +1 -0
  99. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.d.ts.map +1 -1
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.js +4 -1
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.js.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.d.ts +1 -0
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.d.ts.map +1 -1
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.js +8 -0
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.js.map +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js +22 -0
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js.map +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  114. package/front_end/third_party/puppeteer/package/package.json +2 -2
  115. package/front_end/third_party/puppeteer/package/src/cdp/HTTPRequest.ts +5 -1
  116. package/front_end/third_party/puppeteer/package/src/cdp/NetworkEventManager.ts +16 -1
  117. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +28 -0
  118. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  119. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  120. package/front_end/ui/components/docs/component_docs.ts +0 -4
  121. package/front_end/ui/components/report_view/ReportView.ts +4 -1
  122. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +8 -5
  123. package/front_end/ui/i18n/i18n.ts +16 -0
  124. package/front_end/ui/legacy/ReportView.ts +0 -5
  125. package/front_end/ui/legacy/TextPrompt.ts +65 -19
  126. package/front_end/ui/legacy/UIUtils.ts +1 -1
  127. package/front_end/ui/legacy/Widget.ts +56 -25
  128. package/front_end/ui/legacy/XLink.ts +0 -2
  129. package/front_end/ui/legacy/components/object_ui/JavaScriptREPL.ts +8 -4
  130. package/front_end/ui/legacy/components/object_ui/ObjectPopoverHelper.ts +3 -1
  131. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +239 -284
  132. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +114 -184
  133. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  134. package/front_end/ui/legacy/inspectorCommon.css +0 -4
  135. package/front_end/ui/{components/docs/theme_colors/basic.ts → legacy/theme_support/ThemeColors.docs.ts} +33 -23
  136. package/mcp/mcp.ts +1 -0
  137. package/package.json +1 -1
  138. package/front_end/core/common/QueryParamHandler.ts +0 -7
  139. package/front_end/ui/components/docs/input/basic.html +0 -31
  140. package/front_end/ui/components/docs/input/basic.ts +0 -12
  141. package/front_end/ui/components/docs/report/basic.html +0 -27
  142. package/front_end/ui/components/docs/report/basic.ts +0 -48
  143. package/front_end/ui/components/docs/theme_colors/basic.html +0 -56
  144. package/front_end/ui/components/docs/toggle_dark_mode.ts +0 -36
  145. package/front_end/ui/components/docs/toggle_fonts.ts +0 -74
  146. package/front_end/ui/components/docs/user_agent_client_hints/basic.html +0 -25
  147. package/front_end/ui/components/docs/user_agent_client_hints/basic.ts +0 -26
  148. package/front_end/ui/components/expandable_list/ExpandableList.docs.ts +0 -30
  149. /package/front_end/panels/{elements → common}/domLinkifier.css +0 -0
@@ -17,6 +17,7 @@ import * as RequestLinkIcon from '../../ui/components/request_link_icon/request_
17
17
  import * as Components from '../../ui/legacy/components/utils/utils.js';
18
18
  import * as UI from '../../ui/legacy/legacy.js';
19
19
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
20
+ import * as PanelsCommon from '../common/common.js';
20
21
 
21
22
  import type {IssueView} from './IssueView.js';
22
23
 
@@ -230,7 +231,7 @@ export abstract class AffectedResourcesView extends UI.TreeOutline.TreeElement {
230
231
  }
231
232
 
232
233
  const deferredDOMNode = new SDK.DOMModel.DeferredDOMNode(target, backendNodeId);
233
- const anchorElement = (await Common.Linkifier.Linkifier.linkify(deferredDOMNode)) as HTMLElement;
234
+ const anchorElement = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(deferredDOMNode) as HTMLElement;
234
235
  anchorElement.textContent = nodeName;
235
236
  anchorElement.addEventListener('click', () => sendTelemetry());
236
237
  anchorElement.addEventListener('keydown', (event: Event) => {
@@ -13,6 +13,7 @@ import * as Components from '../../ui/legacy/components/utils/utils.js';
13
13
  import * as UI from '../../ui/legacy/legacy.js';
14
14
  import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
15
15
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
16
+ import * as PanelsCommon from '../common/common.js';
16
17
 
17
18
  import type {
18
19
  NodeDetailsJSON, ReportJSON, RunnerResultArtifacts, SourceLocationDetailsJSON} from './LighthouseReporterTypes.js';
@@ -148,7 +149,7 @@ export class LighthouseReportRenderer {
148
149
  continue;
149
150
  }
150
151
 
151
- const element = await Common.Linkifier.Linkifier.linkify(
152
+ const element = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(
152
153
  node, {tooltip: detailsItem.snippet, preventKeyboardFocus: undefined});
153
154
  UI.Tooltip.Tooltip.install(origHTMLElement, '');
154
155
 
@@ -2484,7 +2484,7 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
2484
2484
  .replace(/"/g, '\\"')
2485
2485
  .replace(/[^a-zA-Z0-9\s_\-:=+~'\/.',?;()*`]/g, '^$&')
2486
2486
  .replace(/%(?=[a-zA-Z0-9_])/g, '%^')
2487
- .replace(/[^\S \r\n]/g, ' ')
2487
+ .replace(/[^ -~\r\n]/g, ' ')
2488
2488
  .replace(/\r?\n|\r/g, '^\n\n') +
2489
2489
  encapsChars;
2490
2490
  }
@@ -1179,7 +1179,13 @@ export class RecorderController extends LitElement {
1179
1179
  <div class="empty-state-header">${i18nString(UIStrings.header)}</div>
1180
1180
  <div class="empty-state-description">
1181
1181
  <span>${i18nString(UIStrings.recordingDescription)}</span>
1182
- ${UI.XLink.XLink.create(RECORDER_EXPLANATION_URL, i18nString(UIStrings.learnMore), 'x-link', undefined, 'learn-more')}
1182
+ <x-link
1183
+ class="x-link devtools-link"
1184
+ href=${RECORDER_EXPLANATION_URL}
1185
+ jslog=${VisualLogging.link()
1186
+ .track({ click: true, keydown: 'Enter|Space' })
1187
+ .context('learn-more')}
1188
+ >${i18nString(UIStrings.learnMore)}</x-link>
1183
1189
  </div>
1184
1190
  <devtools-button .variant=${Buttons.Button.Variant.TONAL} jslogContext=${Actions.RecorderActions.CREATE_RECORDING} @click=${this.#onCreateNewRecording}>${i18nString(UIStrings.createRecording)}</devtools-button>
1185
1191
  </div>
@@ -235,6 +235,7 @@ export class GenericSettingsTab extends UI.Widget.VBox implements SettingsTab {
235
235
  this.contentElement.createChild('div', 'settings-card-container-wrapper').createChild('div');
236
236
 
237
237
  this.containerElement.classList.add('settings-multicolumn-card-container');
238
+ this.syncSection.markAsRoot();
238
239
 
239
240
  // AI, GRID, MOBILE, EMULATION, and RENDERING are intentionally excluded from this list.
240
241
  // AI settings are displayed in their own tab.
@@ -316,11 +317,7 @@ export class GenericSettingsTab extends UI.Widget.VBox implements SettingsTab {
316
317
  new Promise<Host.InspectorFrontendHostAPI.SyncInformation>(
317
318
  resolve => Host.InspectorFrontendHost.InspectorFrontendHostInstance.getSyncInformation(resolve))
318
319
  .then(syncInfo => {
319
- this.syncSection.data = {
320
- syncInfo,
321
- syncSetting: Common.Settings.moduleSetting('sync-preferences') as Common.Settings.Setting<boolean>,
322
- receiveBadgesSetting: Common.Settings.Settings.instance().moduleSetting('receive-gdp-badges'),
323
- };
320
+ this.syncSection.syncInfo = syncInfo;
324
321
  if (!syncInfo.isSyncActive || !syncInfo.arePreferencesSynced) {
325
322
  this.#updateSyncSectionTimerId = window.setTimeout(this.updateSyncSection.bind(this), 500);
326
323
  }
@@ -342,7 +339,7 @@ export class GenericSettingsTab extends UI.Widget.VBox implements SettingsTab {
342
339
  } else if (category === Common.Settings.SettingCategory.ACCOUNT && settings.length > 0) {
343
340
  const syncCard = createSettingsCard(
344
341
  Common.SettingRegistration.getLocalizedSettingsCategory(Common.SettingRegistration.SettingCategory.ACCOUNT),
345
- this.syncSection);
342
+ this.syncSection.element);
346
343
  this.containerElement.appendChild(syncCard);
347
344
  } else if (settings.length > 0) {
348
345
  this.createStandardSectionElement(category, settings);
@@ -1,22 +1,20 @@
1
1
  // Copyright 2021 The Chromium Authors
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
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
4
 
6
5
  import '../../../ui/components/chrome_link/chrome_link.js';
7
6
  import '../../../ui/components/settings/settings.js';
8
7
  import '../../../ui/components/tooltips/tooltips.js';
9
8
 
10
- import type * as Common from '../../../core/common/common.js';
9
+ import * as Common from '../../../core/common/common.js';
11
10
  import * as Host from '../../../core/host/host.js';
12
11
  import * as i18n from '../../../core/i18n/i18n.js';
13
12
  import type * as Platform from '../../../core/platform/platform.js';
14
13
  import * as SDK from '../../../core/sdk/sdk.js';
15
14
  import * as Badges from '../../../models/badges/badges.js';
16
15
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
17
- import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
18
16
  import type * as SettingsComponents from '../../../ui/components/settings/settings.js';
19
- import * as uiI18n from '../../../ui/i18n/i18n.js';
17
+ import * as UI from '../../../ui/legacy/legacy.js';
20
18
  import * as Lit from '../../../ui/lit/lit.js';
21
19
  import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
22
20
  import * as PanelCommon from '../../common/common.js';
@@ -90,34 +88,9 @@ const UIStrings = {
90
88
  } as const;
91
89
  const str_ = i18n.i18n.registerUIStrings('panels/settings/components/SyncSection.ts', UIStrings);
92
90
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
91
+ const i18nTemplate = Lit.i18nTemplate.bind(undefined, str_);
93
92
 
94
- const {html, Directives: {ref, createRef}} = Lit;
95
-
96
- let cachedTooltipElement: HTMLElement|undefined;
97
-
98
- function renderDataDisclaimer(): HTMLElement {
99
- if (cachedTooltipElement) {
100
- return cachedTooltipElement;
101
- }
102
-
103
- const relevantDataTooltipTemplate = html`
104
- <span
105
- tabIndex="0"
106
- class="link"
107
- aria-details="gdp-profile-tooltip"
108
- aria-describedby="gdp-profile-tooltip"
109
- >${i18nString(UIStrings.relevantData)}</span>
110
- <devtools-tooltip id="gdp-profile-tooltip" variant="rich">
111
- <div class="tooltip-content" tabindex="0">${i18nString(UIStrings.tooltipDisclaimerText)}</div>
112
- </devtools-tooltip>`;
113
-
114
- const container = document.createElement('span');
115
- Lit.render(relevantDataTooltipTemplate, container);
116
- cachedTooltipElement = uiI18n.getFormatLocalizedString(str_, UIStrings.dataDisclaimer, {
117
- PH1: container,
118
- });
119
- return cachedTooltipElement;
120
- }
93
+ const {html, render, Directives: {ref}} = Lit;
121
94
 
122
95
  function getGdpSubscriptionText(profile: Host.GdpClient.Profile): Platform.UIString.LocalizedString {
123
96
  if (!profile.activeSubscription ||
@@ -137,248 +110,267 @@ function getGdpSubscriptionText(profile: Host.GdpClient.Profile): Platform.UIStr
137
110
  }
138
111
  }
139
112
 
113
+ // clang-format off
114
+ const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement): void => {
115
+ const renderSettingCheckboxIfNeeded = (): Lit.LitTemplate => {
116
+ if (!input.syncInfo.accountEmail) {
117
+ return Lit.nothing;
118
+ }
119
+ const warningText = input.warningType === WarningType.SYNC_DISABLED ? i18nString(UIStrings.syncDisabled) : i18nString(UIStrings.preferencesSyncDisabled);
120
+
121
+ return html`
122
+ <div class="setting-checkbox-container">
123
+ <setting-checkbox class="setting-checkbox"
124
+ .data=${{ setting: input.syncSetting } as SettingsComponents.SettingCheckbox.SettingCheckboxData}>
125
+ </setting-checkbox>
126
+ ${input.warningType ? html`
127
+ <devtools-button
128
+ aria-details="settings-sync-info"
129
+ .iconName=${'info'}
130
+ .variant=${Buttons.Button.Variant.ICON}
131
+ .size=${Buttons.Button.Size.SMALL}
132
+ @click=${input.onWarningClick}>
133
+ </devtools-button>
134
+ <devtools-tooltip
135
+ id="settings-sync-info"
136
+ variant="rich">
137
+ ${warningText}
138
+ </devtools-tooltip>`: Lit.nothing}
139
+ </div>
140
+ `;
141
+ };
142
+
143
+ const renderAccountInfo = (): Lit.LitTemplate => {
144
+ if (!input.syncInfo.accountEmail) {
145
+ return html`
146
+ <div class="not-signed-in">${i18nString(UIStrings.notSignedIn)}</div>
147
+ `;
148
+ }
149
+
150
+ return html`
151
+ <div class="account-info">
152
+ <img class="account-avatar" src="data:image/png;base64, ${input.syncInfo.accountImage}"
153
+ alt="Account avatar" />
154
+ <div class="account-email">
155
+ <span>${i18nString(UIStrings.signedIn)}</span>
156
+ <span>${input.syncInfo.accountEmail}</span>
157
+ </div>
158
+ </div>`;
159
+ };
160
+
161
+ const renderGdpSectionIfNeeded = (): Lit.LitTemplate => {
162
+ if (!input.isEligibleToCreateGdpProfile && !input.gdpProfile) {
163
+ return Lit.nothing;
164
+ }
165
+ const hasReceiveBadgesCheckbox = Host.GdpClient.isBadgesEnabled() && input.receiveBadgesSetting;
166
+ const renderBrand = (): Lit.LitTemplate => {
167
+ return html`
168
+ <div class="gdp-profile-header">
169
+ <div class="gdp-logo" role="img" aria-label="Google Developer Program"></div>
170
+ </div>
171
+ `;
172
+ };
173
+
174
+ return html`
175
+ <div class="gdp-profile-container" .jslog=${VisualLogging.section().context('gdp-profile')}>
176
+ <div class="divider"></div>
177
+ ${input.gdpProfile ? html`
178
+ <div class="gdp-profile-details-content">
179
+ ${renderBrand()}
180
+ <div class="plan-details">
181
+ ${getGdpSubscriptionText(input.gdpProfile)}
182
+ &nbsp;·&nbsp;
183
+ <x-link
184
+ .jslog=${VisualLogging.link().track({click: true, keydown: 'Enter|Space'}).context('view-profile')}
185
+ class="link"
186
+ href=${Host.GdpClient.GOOGLE_DEVELOPER_PROGRAM_PROFILE_LINK}>
187
+ ${i18nString(UIStrings.viewProfile)}
188
+ </x-link></div>
189
+ ${hasReceiveBadgesCheckbox ? html`
190
+ <div class="setting-container" ${ref(el => {
191
+ output.highlightReceiveBadgesSetting = () => {
192
+ if (el) {
193
+ PanelUtils.PanelUtils.highlightElement(el as HTMLElement);
194
+ }
195
+ };
196
+ })}>
197
+ <setting-checkbox class="setting-checkbox"
198
+ .data=${{setting: input.receiveBadgesSetting} as SettingsComponents.SettingCheckbox.SettingCheckboxData}
199
+ @change=${(e: Event) => input.onReceiveBadgesSettingClick(e)}>
200
+ </setting-checkbox>
201
+ <span>${i18nTemplate(UIStrings.dataDisclaimer, {PH1: html`
202
+ <span class="link" tabindex="0" aria-details="gdp-profile-tooltip">
203
+ ${i18nString(UIStrings.relevantData)}</span>
204
+ <devtools-tooltip id="gdp-profile-tooltip" variant="rich">
205
+ <div class="tooltip-content" tabindex="0">
206
+ ${i18nString(UIStrings.tooltipDisclaimerText)}</div>
207
+ </devtools-tooltip>`})}
208
+ </span>
209
+ </div>
210
+ ` : Lit.nothing}
211
+ </div>
212
+ ` : html`
213
+ <div class="gdp-profile-sign-up-content">
214
+ ${renderBrand()}
215
+ <devtools-button
216
+ @click=${input.onSignUpClick}
217
+ .jslogContext=${'open-sign-up-dialog'}
218
+ .variant=${Buttons.Button.Variant.OUTLINED}>
219
+ ${i18nString(UIStrings.signUp)}
220
+ </devtools-button>
221
+ </div>
222
+ `}
223
+ </div>
224
+ `;
225
+ };
226
+
227
+ render(html`
228
+ <style>${syncSectionStyles}</style>
229
+ <fieldset>
230
+ ${renderAccountInfo()}
231
+ ${renderSettingCheckboxIfNeeded()}
232
+ ${renderGdpSectionIfNeeded()}
233
+ </fieldset>
234
+ `, target);
235
+ };
236
+ // clang-format on
237
+
238
+ type View = typeof DEFAULT_VIEW;
239
+
240
+ export const enum WarningType {
241
+ SYNC_DISABLED = 'SYNC_DISABLED',
242
+ PREFERENCES_SYNC_DISABLED = 'PREFERENCES_SYNC_DISABLED',
243
+ }
244
+
140
245
  export interface SyncSectionData {
141
246
  syncInfo: Host.InspectorFrontendHostAPI.SyncInformation;
142
247
  syncSetting: Common.Settings.Setting<boolean>;
143
248
  receiveBadgesSetting: Common.Settings.Setting<boolean>;
144
249
  }
145
250
 
146
- export class SyncSection extends HTMLElement {
147
- readonly #shadow = this.attachShadow({mode: 'open'});
251
+ export interface ViewInput {
252
+ syncInfo: Host.InspectorFrontendHostAPI.SyncInformation;
253
+ syncSetting: Common.Settings.Setting<boolean>;
254
+ receiveBadgesSetting?: Common.Settings.Setting<boolean>;
255
+ isEligibleToCreateGdpProfile: boolean;
256
+ gdpProfile?: Host.GdpClient.Profile;
257
+ onSignUpClick: () => void;
258
+ onReceiveBadgesSettingClick: (e: Event) => void;
259
+ onWarningClick: (e: Event) => void;
260
+ warningType?: WarningType;
261
+ }
148
262
 
263
+ export interface ViewOutput {
264
+ highlightReceiveBadgesSetting?: () => void;
265
+ }
266
+ export class SyncSection extends UI.Widget.Widget {
149
267
  #syncInfo: Host.InspectorFrontendHostAPI.SyncInformation = {isSyncActive: false};
150
- #syncSetting?: Common.Settings.Setting<boolean>;
151
- #receiveBadgesSetting?: Common.Settings.Setting<boolean>;
152
- #receiveBadgesSettingContainerRef = createRef<HTMLElement>();
268
+ #syncSetting: Common.Settings.Setting<boolean>;
269
+ #receiveBadgesSetting: Common.Settings.Setting<boolean>;
153
270
  #isEligibleToCreateGdpProfile = false;
154
271
  #gdpProfile?: Host.GdpClient.Profile;
272
+ #view: View;
273
+ #viewOutput: ViewOutput = {};
274
+
275
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
276
+ super(element);
277
+ this.#view = view;
278
+ this.#receiveBadgesSetting = Common.Settings.Settings.instance().moduleSetting('receive-gdp-badges');
279
+ this.#syncSetting = Common.Settings.moduleSetting('sync-preferences') as Common.Settings.Setting<boolean>;
280
+ }
155
281
 
156
- set data(data: SyncSectionData) {
157
- this.#syncInfo = data.syncInfo;
158
- this.#syncSetting = data.syncSetting;
159
- this.#receiveBadgesSetting = data.receiveBadgesSetting;
160
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
282
+ override wasShown(): void {
283
+ super.wasShown();
284
+ this.requestUpdate();
285
+ }
286
+
287
+ set syncInfo(syncInfo: Host.InspectorFrontendHostAPI.SyncInformation) {
288
+ this.#syncInfo = syncInfo;
289
+ this.requestUpdate();
161
290
 
162
291
  // Trigger fetching GDP profile if the user is signed in.
163
- if (data.syncInfo.accountEmail) {
292
+ if (syncInfo.accountEmail) {
164
293
  void this.#fetchGdpDetails();
165
294
  }
166
295
  }
167
296
 
168
297
  async highlightReceiveBadgesSetting(): Promise<void> {
169
- await ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
170
- const element = this.#receiveBadgesSettingContainerRef.value;
171
- if (element) {
172
- PanelUtils.PanelUtils.highlightElement(element);
173
- }
298
+ this.requestUpdate();
299
+ await this.updateComplete;
300
+ this.#viewOutput.highlightReceiveBadgesSetting?.();
174
301
  }
175
302
 
176
- #render(): void {
177
- if (!this.#syncSetting) {
178
- throw new Error('SyncSection is not properly initialized');
179
- }
180
-
303
+ override performUpdate(): void {
181
304
  // TODO: this should not probably happen in render, instead, the setting
182
305
  // should be disabled.
183
306
  const checkboxDisabled = !this.#syncInfo.isSyncActive || !this.#syncInfo.arePreferencesSynced;
184
307
  this.#syncSetting?.setDisabled(checkboxDisabled);
185
- // Disabled until https://crbug.com/1079231 is fixed.
186
- // clang-format off
187
- Lit.render(html`
188
- <style>${syncSectionStyles}</style>
189
- <fieldset>
190
- ${renderAccountInfo(this.#syncInfo)}
191
- ${renderSettingCheckboxIfNeeded(this.#syncInfo, this.#syncSetting)}
192
- ${renderGdpSectionIfNeeded({
193
- receiveBadgesSetting: this.#receiveBadgesSetting,
194
- receiveBadgesSettingContainerRef: this.#receiveBadgesSettingContainerRef,
195
- gdpProfile: this.#gdpProfile,
196
- isEligibleToCreateProfile: this.#isEligibleToCreateGdpProfile,
197
- onSignUpSuccess: this.#fetchGdpDetails.bind(this),
198
- })}
199
- </fieldset>
200
- `, this.#shadow, {host: this});
201
- // clang-format on
202
- }
203
-
204
- async #fetchGdpDetails(): Promise<void> {
205
- if (!Host.GdpClient.isGdpProfilesAvailable()) {
206
- return;
207
- }
208
308
 
209
- const getProfileResponse = await Host.GdpClient.GdpClient.instance().getProfile();
210
- if (!getProfileResponse) {
211
- return;
309
+ let warningType: WarningType|undefined;
310
+ if (!this.#syncInfo.isSyncActive) {
311
+ warningType = WarningType.SYNC_DISABLED;
312
+ } else if (!this.#syncInfo.arePreferencesSynced) {
313
+ warningType = WarningType.PREFERENCES_SYNC_DISABLED;
212
314
  }
213
315
 
214
- this.#gdpProfile = getProfileResponse.profile ?? undefined;
215
- this.#isEligibleToCreateGdpProfile = getProfileResponse.isEligible;
216
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
316
+ const viewInput: ViewInput = {
317
+ syncInfo: this.#syncInfo,
318
+ syncSetting: this.#syncSetting,
319
+ receiveBadgesSetting: this.#receiveBadgesSetting,
320
+ gdpProfile: this.#gdpProfile,
321
+ isEligibleToCreateGdpProfile: Host.GdpClient.isGdpProfilesAvailable() && this.#isEligibleToCreateGdpProfile,
322
+ onSignUpClick: this.#onSignUpClick.bind(this),
323
+ onReceiveBadgesSettingClick: this.#onReceiveBadgesSettingClick.bind(this),
324
+ onWarningClick: this.#onWarningClick.bind(this),
325
+ warningType,
326
+ };
327
+ this.#view(viewInput, this.#viewOutput, this.contentElement);
217
328
  }
218
- }
219
329
 
220
- function renderSettingCheckboxIfNeeded(
221
- syncInfo: Host.InspectorFrontendHostAPI.SyncInformation,
222
- syncSetting: Common.Settings.Setting<boolean>): Lit.LitTemplate {
223
- if (!syncInfo.accountEmail) {
224
- return Lit.nothing;
330
+ #onSignUpClick(): void {
331
+ PanelCommon.GdpSignUpDialog.show({onSuccess: (this.#fetchGdpDetails.bind(this))});
225
332
  }
226
333
 
227
- // clang-format off
228
- return html`
229
- <div class="setting-checkbox-container">
230
- <setting-checkbox class="setting-checkbox" .data=${{setting: syncSetting}}>
231
- </setting-checkbox>
232
- ${renderWarningIfNeeded(syncInfo)}
233
- </div>
234
- `;
235
- // clang-format on
236
- }
334
+ #onReceiveBadgesSettingClick(e: Event): void {
335
+ const settingCheckbox = e.target as SettingsComponents.SettingCheckbox.SettingCheckbox;
336
+ void Badges.UserBadges.instance().initialize().then(() => {
337
+ if (!settingCheckbox.checked) {
338
+ return;
339
+ }
237
340
 
238
- function renderWarningIfNeeded(syncInfo: Host.InspectorFrontendHostAPI.SyncInformation): Lit.LitTemplate {
239
- const hasWarning = !syncInfo.isSyncActive || !syncInfo.arePreferencesSynced;
240
- if (!hasWarning) {
241
- return Lit.nothing;
341
+ Badges.UserBadges.instance().recordAction(Badges.BadgeAction.RECEIVE_BADGES_SETTING_ENABLED);
342
+ });
242
343
  }
243
344
 
244
- const warningLink = !syncInfo.isSyncActive ?
245
- 'chrome://settings/syncSetup' as Platform.DevToolsPath.UrlString :
246
- 'chrome://settings/syncSetup/advanced' as Platform.DevToolsPath.UrlString;
247
- const warningText =
248
- !syncInfo.isSyncActive ? i18nString(UIStrings.syncDisabled) : i18nString(UIStrings.preferencesSyncDisabled);
249
- const handleClick = (event: Event): void => {
345
+ #onWarningClick(event: Event): void {
250
346
  const rootTarget = SDK.TargetManager.TargetManager.instance().rootTarget();
251
347
  if (rootTarget === null) {
252
348
  return;
253
349
  }
254
-
350
+ // TODO: investigate if /advance link is alive
351
+ const warningLink = !this.#syncInfo.isSyncActive ?
352
+ ('chrome://settings/syncSetup' as Platform.DevToolsPath.UrlString) :
353
+ ('chrome://settings/syncSetup/advanced' as Platform.DevToolsPath.UrlString);
255
354
  void rootTarget.targetAgent().invoke_createTarget({url: warningLink}).then(result => {
256
355
  if (result.getError()) {
257
356
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(warningLink);
258
357
  }
259
358
  });
260
359
  event.consume();
261
- };
262
- // clang-format off
263
- return html`
264
- <devtools-button
265
- aria-describedby=settings-sync-info
266
- .title=${warningText}
267
- .iconName=${'info'}
268
- .variant=${Buttons.Button.Variant.ICON}
269
- .size=${Buttons.Button.Size.SMALL}
270
- @click=${handleClick}>
271
- </devtools-button>
272
- <devtools-tooltip
273
- id=settings-sync-info
274
- variant=simple>
275
- ${warningText}
276
- </devtools-tooltip>
277
- `;
278
- // clang-format on
279
- }
280
-
281
- function renderAccountInfo(syncInfo: Host.InspectorFrontendHostAPI.SyncInformation): Lit.LitTemplate {
282
- if (!syncInfo.accountEmail) {
283
- // clang-format off
284
- return html`
285
- <div class="not-signed-in">${i18nString(UIStrings.notSignedIn)}</div>
286
- `;
287
- // clang-format on
288
- }
289
-
290
- // clang-format off
291
- return html`
292
- <div class="account-info">
293
- <img class="account-avatar" src="data:image/png;base64, ${syncInfo.accountImage}" alt="Account avatar" />
294
- <div class="account-email">
295
- <span>${i18nString(UIStrings.signedIn)}</span>
296
- <span>${syncInfo.accountEmail}</span>
297
- </div>
298
- </div>`;
299
- // clang-format on
300
- }
301
-
302
- function renderGdpSectionIfNeeded({
303
- receiveBadgesSetting,
304
- receiveBadgesSettingContainerRef,
305
- gdpProfile,
306
- isEligibleToCreateProfile,
307
- onSignUpSuccess,
308
- }: {
309
- receiveBadgesSettingContainerRef: Lit.Directives.Ref<HTMLElement>,
310
- onSignUpSuccess: () => void,
311
- receiveBadgesSetting?: Common.Settings.Setting<boolean>,
312
- gdpProfile?: Host.GdpClient.Profile,
313
- isEligibleToCreateProfile?: boolean,
314
- }): Lit.LitTemplate {
315
- if (!Host.GdpClient.isGdpProfilesAvailable() || (!gdpProfile && !isEligibleToCreateProfile)) {
316
- return Lit.nothing;
317
- }
318
- const hasReceiveBadgesCheckbox = Host.GdpClient.isBadgesEnabled() && receiveBadgesSetting;
319
- function renderBrand(): Lit.LitTemplate {
320
- // clang-format off
321
- return html`
322
- <div class="gdp-profile-header">
323
- <div class="gdp-logo" role="img" aria-label="Google Developer Program"></div>
324
- </div>
325
- `;
326
- // clang-format on
327
360
  }
328
361
 
329
- // clang-format off
330
- return html`
331
- <div class="gdp-profile-container" jslog=${VisualLogging.section().context('gdp-profile')}>
332
- <div class="divider"></div>
333
- ${gdpProfile ? html`
334
- <div class="gdp-profile-details-content">
335
- ${renderBrand()}
336
- <div class="plan-details">
337
- ${getGdpSubscriptionText(gdpProfile)}
338
- &nbsp;·&nbsp;
339
- <x-link
340
- jslog=${VisualLogging.link().track({click: true, keydown:'Enter|Space'}).context('view-profile')}
341
- class="link"
342
- href=${Host.GdpClient.GOOGLE_DEVELOPER_PROGRAM_PROFILE_LINK}>
343
- ${i18nString(UIStrings.viewProfile)}
344
- </x-link></div>
345
- ${hasReceiveBadgesCheckbox ? html`
346
- <div class="setting-container" ${ref(receiveBadgesSettingContainerRef)}>
347
- <setting-checkbox class="setting-checkbox" .data=${{setting: receiveBadgesSetting}} @change=${(e: Event) => {
348
- const settingCheckbox = e.target as SettingsComponents.SettingCheckbox.SettingCheckbox;
349
- void Badges.UserBadges.instance().initialize().then(() => {
350
- if (!settingCheckbox.checked) {
351
- return;
352
- }
353
-
354
- Badges.UserBadges.instance().recordAction(Badges.BadgeAction.RECEIVE_BADGES_SETTING_ENABLED);
355
- });
356
- }}></setting-checkbox>
357
- ${renderDataDisclaimer()}
358
- </div>` : Lit.nothing}
359
- </div>
360
- ` : html`
361
- <div class="gdp-profile-sign-up-content">
362
- ${renderBrand()}
363
- <devtools-button
364
- @click=${() => PanelCommon.GdpSignUpDialog.show({
365
- onSuccess: onSignUpSuccess
366
- })}
367
- .jslogContext=${'open-sign-up-dialog'}
368
- .variant=${Buttons.Button.Variant.OUTLINED}>
369
- ${i18nString(UIStrings.signUp)}
370
- </devtools-button>
371
- </div>
372
- `}
373
- </div>
374
- `;
375
- // clang-format on
376
- }
362
+ async #fetchGdpDetails(): Promise<void> {
363
+ if (!Host.GdpClient.isGdpProfilesAvailable()) {
364
+ return;
365
+ }
377
366
 
378
- customElements.define('devtools-sync-section', SyncSection);
367
+ const getProfileResponse = await Host.GdpClient.GdpClient.instance().getProfile();
368
+ if (!getProfileResponse) {
369
+ return;
370
+ }
379
371
 
380
- declare global {
381
- interface HTMLElementTagNameMap {
382
- 'devtools-sync-section': SyncSection;
372
+ this.#gdpProfile = getProfileResponse.profile ?? undefined;
373
+ this.#isEligibleToCreateGdpProfile = getProfileResponse.isEligible;
374
+ this.requestUpdate();
383
375
  }
384
376
  }