chrome-devtools-frontend 1.0.1660788 → 1.0.1662289

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 (177) hide show
  1. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +143 -0
  2. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +104 -0
  3. package/.agents/skills/migrate-chromium-test/SKILL.md +7 -2
  4. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +7 -1
  5. package/docs/ui_engineering.md +75 -42
  6. package/eslint.config.mjs +7 -0
  7. package/extension-api/ExtensionAPI.d.ts +88 -0
  8. package/front_end/core/common/SettingRegistration.ts +0 -5
  9. package/front_end/core/host/InspectorFrontendHost.ts +1 -0
  10. package/front_end/core/host/InspectorFrontendHostStub.ts +2 -0
  11. package/front_end/core/platform/ArrayUtilities.ts +10 -0
  12. package/front_end/core/platform/StringUtilities.ts +20 -4
  13. package/front_end/core/sdk/CPUThrottlingManager.ts +2 -0
  14. package/front_end/core/sdk/DOMDebuggerModel.ts +2 -0
  15. package/front_end/core/sdk/DOMModel.ts +1 -0
  16. package/front_end/core/sdk/DebuggerModel.ts +4 -4
  17. package/front_end/core/sdk/EventBreakpointsModel.ts +2 -0
  18. package/front_end/core/sdk/FrameManager.ts +1 -0
  19. package/front_end/core/sdk/IsolateManager.ts +1 -0
  20. package/front_end/core/sdk/NetworkManager.ts +2 -0
  21. package/front_end/core/sdk/NetworkRequest.ts +1 -0
  22. package/front_end/core/sdk/PageResourceLoader.ts +2 -0
  23. package/front_end/core/sdk/SourceMap.ts +5 -5
  24. package/front_end/core/sdk/SourceMapManager.ts +3 -2
  25. package/front_end/core/sdk/TargetManager.ts +4 -0
  26. package/front_end/entrypoints/main/MainImpl.ts +2 -1
  27. package/front_end/foundation/README.md +86 -0
  28. package/front_end/generated/InspectorBackendCommands.ts +4 -5
  29. package/front_end/generated/protocol.ts +20 -38
  30. package/front_end/models/bindings/CompilerScriptMapping.ts +5 -5
  31. package/front_end/models/bindings/ContentProviderBasedProject.ts +2 -3
  32. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +3 -3
  33. package/front_end/models/bindings/DefaultScriptMapping.ts +3 -3
  34. package/front_end/models/bindings/ResourceMapping.ts +3 -3
  35. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -2
  36. package/front_end/models/bindings/SASSSourceMapping.ts +3 -3
  37. package/front_end/models/bindings/TempFile.ts +8 -3
  38. package/front_end/models/extensions/ExtensionAPI.ts +110 -46
  39. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +7 -5
  40. package/front_end/models/formatter/ScriptFormatter.ts +8 -11
  41. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +14 -5
  42. package/front_end/models/issues_manager/CheckFormsIssuesTrigger.ts +8 -9
  43. package/front_end/models/issues_manager/CookieIssue.ts +21 -9
  44. package/front_end/models/issues_manager/Issue.ts +3 -4
  45. package/front_end/models/issues_manager/IssueAggregator.ts +0 -9
  46. package/front_end/models/issues_manager/IssueResolver.ts +2 -2
  47. package/front_end/models/issues_manager/IssuesManager.ts +136 -137
  48. package/front_end/models/issues_manager/RelatedIssue.ts +10 -8
  49. package/front_end/models/issues_manager/issues_manager.ts +0 -2
  50. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  51. package/front_end/models/logs/RequestResolver.ts +2 -2
  52. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -9
  53. package/front_end/models/trace/handlers/FramesHandler.ts +4 -7
  54. package/front_end/models/trace/handlers/ModelHandlers.ts +0 -1
  55. package/front_end/models/trace/handlers/RendererHandler.ts +2 -15
  56. package/front_end/models/trace/handlers/Threads.ts +4 -11
  57. package/front_end/models/trace/insights/DOMSize.ts +1 -1
  58. package/front_end/models/workspace_diff/WorkspaceDiff.ts +10 -4
  59. package/front_end/panels/ai_assistance/components/ChatMessage.ts +2 -1
  60. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -44
  61. package/front_end/panels/application/CookieItemsView.ts +1 -1
  62. package/front_end/panels/application/ServiceWorkersView.ts +494 -411
  63. package/front_end/panels/application/WebMCPView.ts +38 -43
  64. package/front_end/panels/application/application.ts +0 -6
  65. package/front_end/panels/application/components/AdsView.ts +201 -30
  66. package/front_end/panels/application/components/adsView.css +25 -0
  67. package/front_end/panels/application/components/components.ts +0 -2
  68. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +1 -1
  69. package/front_end/panels/application/preloading/components/RuleSetDetailsView.ts +2 -2
  70. package/front_end/panels/application/serviceWorkersView.css +40 -4
  71. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +147 -123
  72. package/front_end/panels/common/ExtensionServer.ts +3 -3
  73. package/front_end/panels/console/ConsoleView.ts +3 -2
  74. package/front_end/panels/console/PromptBuilder.ts +2 -1
  75. package/front_end/panels/emulation/DeviceModeView.ts +164 -113
  76. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  77. package/front_end/panels/issues/IssueView.ts +0 -2
  78. package/front_end/panels/issues/IssuesPane.ts +1 -8
  79. package/front_end/panels/js_timeline/js_timeline-meta.ts +3 -2
  80. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +53 -12
  81. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +3 -3
  82. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorPane.ts +5 -5
  83. package/front_end/panels/linear_memory_inspector/components/LinearMemoryHighlightChipList.ts +4 -4
  84. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +2 -2
  85. package/front_end/panels/linear_memory_inspector/components/LinearMemoryNavigator.ts +6 -6
  86. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +3 -3
  87. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplay.ts +8 -9
  88. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplayUtils.ts +2 -2
  89. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +1 -1
  90. package/front_end/panels/linear_memory_inspector/linear_memory_inspector-meta.ts +2 -2
  91. package/front_end/panels/network/RequestPayloadView.ts +92 -52
  92. package/front_end/panels/network/RequestTimingView.ts +95 -127
  93. package/front_end/panels/network/components/ResponseHeaderSection.ts +5 -3
  94. package/front_end/panels/profiler/HeapSnapshotView.ts +3 -3
  95. package/front_end/panels/profiler/WritableProfileHeader.ts +2 -2
  96. package/front_end/panels/recorder/{components/ControlButton.ts → ControlButton.ts} +9 -10
  97. package/front_end/panels/recorder/{components/CreateRecordingView.ts → CreateRecordingView.ts} +48 -47
  98. package/front_end/panels/recorder/{components/ExtensionView.ts → ExtensionView.ts} +8 -8
  99. package/front_end/panels/recorder/RecorderController.ts +302 -127
  100. package/front_end/panels/recorder/RecorderPanel.ts +1 -1
  101. package/front_end/panels/recorder/{components/RecordingListView.ts → RecordingListView.ts} +19 -20
  102. package/front_end/panels/recorder/{components/RecordingView.ts → RecordingView.ts} +88 -88
  103. package/front_end/panels/recorder/{components/ReplaySection.ts → ReplaySection.ts} +11 -11
  104. package/front_end/panels/recorder/{components/SelectorPicker.ts → SelectorPicker.ts} +13 -13
  105. package/front_end/panels/recorder/{components/StepEditor.ts → StepEditor.ts} +15 -15
  106. package/front_end/panels/recorder/{components/StepView.ts → StepView.ts} +10 -10
  107. package/front_end/panels/recorder/{components/TimelineSection.ts → TimelineSection.ts} +2 -2
  108. package/front_end/panels/recorder/recorder.ts +23 -1
  109. package/front_end/panels/recorder/util/util.ts +113 -0
  110. package/front_end/panels/sources/SourcesView.ts +0 -17
  111. package/front_end/panels/sources/TabbedEditorContainer.ts +1 -1
  112. package/front_end/panels/sources/UISourceCodeFrame.ts +42 -0
  113. package/front_end/panels/sources/sources.ts +0 -2
  114. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -17
  115. package/front_end/panels/timeline/IsolateSelector.ts +10 -8
  116. package/front_end/panels/timeline/ThreadAppender.ts +0 -106
  117. package/front_end/panels/timeline/TimelinePanel.ts +18 -7
  118. package/front_end/panels/timeline/TimelineUIUtils.ts +144 -129
  119. package/front_end/panels/timeline/timeline-meta.ts +3 -2
  120. package/front_end/third_party/chromium/README.chromium +1 -1
  121. package/front_end/ui/components/report_view/ReportView.ts +11 -5
  122. package/front_end/ui/components/report_view/report.css +4 -1
  123. package/front_end/ui/legacy/TextPrompt.ts +49 -1
  124. package/front_end/ui/legacy/Treeoutline.ts +3 -0
  125. package/front_end/ui/legacy/UIUtils.ts +11 -1
  126. package/front_end/ui/legacy/Widget.ts +14 -15
  127. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +2 -2
  128. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +11 -7
  129. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +2 -1
  130. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +10 -1
  131. package/front_end/ui/legacy/textPrompt.css +10 -0
  132. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  133. package/package.json +1 -1
  134. package/front_end/models/issues_manager/AttributionReportingIssue.ts +0 -247
  135. package/front_end/models/issues_manager/descriptions/arInsecureContext.md +0 -7
  136. package/front_end/models/issues_manager/descriptions/arInvalidInfoHeader.md +0 -5
  137. package/front_end/models/issues_manager/descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
  138. package/front_end/models/issues_manager/descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
  139. package/front_end/models/issues_manager/descriptions/arInvalidRegisterSourceHeader.md +0 -5
  140. package/front_end/models/issues_manager/descriptions/arInvalidRegisterTriggerHeader.md +0 -5
  141. package/front_end/models/issues_manager/descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
  142. package/front_end/models/issues_manager/descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
  143. package/front_end/models/issues_manager/descriptions/arNoRegisterOsSourceHeader.md +0 -5
  144. package/front_end/models/issues_manager/descriptions/arNoRegisterOsTriggerHeader.md +0 -5
  145. package/front_end/models/issues_manager/descriptions/arNoRegisterSourceHeader.md +0 -5
  146. package/front_end/models/issues_manager/descriptions/arNoRegisterTriggerHeader.md +0 -5
  147. package/front_end/models/issues_manager/descriptions/arNoWebOrOsSupport.md +0 -4
  148. package/front_end/models/issues_manager/descriptions/arOsSourceIgnored.md +0 -18
  149. package/front_end/models/issues_manager/descriptions/arOsTriggerIgnored.md +0 -19
  150. package/front_end/models/issues_manager/descriptions/arPermissionPolicyDisabled.md +0 -8
  151. package/front_end/models/issues_manager/descriptions/arSourceAndTriggerHeaders.md +0 -9
  152. package/front_end/models/issues_manager/descriptions/arSourceIgnored.md +0 -13
  153. package/front_end/models/issues_manager/descriptions/arTriggerIgnored.md +0 -12
  154. package/front_end/models/issues_manager/descriptions/arUntrustworthyReportingOrigin.md +0 -10
  155. package/front_end/models/issues_manager/descriptions/arWebAndOsHeaders.md +0 -11
  156. package/front_end/models/trace/handlers/AuctionWorkletsHandler.ts +0 -183
  157. package/front_end/panels/application/InterestGroupStorageModel.ts +0 -81
  158. package/front_end/panels/application/InterestGroupStorageView.ts +0 -151
  159. package/front_end/panels/application/InterestGroupTreeElement.ts +0 -65
  160. package/front_end/panels/application/components/InterestGroupAccessGrid.ts +0 -135
  161. package/front_end/panels/application/components/interestGroupAccessGrid.css +0 -27
  162. package/front_end/panels/application/interestGroupStorageView.css +0 -9
  163. package/front_end/panels/issues/AttributionReportingIssueDetailsView.ts +0 -182
  164. package/front_end/panels/recorder/components/components.ts +0 -25
  165. package/front_end/panels/recorder/components/util.ts +0 -116
  166. package/front_end/panels/sources/InplaceFormatterEditorAction.ts +0 -149
  167. /package/front_end/panels/recorder/{components/controlButton.css → controlButton.css} +0 -0
  168. /package/front_end/panels/recorder/{components/createRecordingView.css → createRecordingView.css} +0 -0
  169. /package/front_end/panels/recorder/{components/extensionView.css → extensionView.css} +0 -0
  170. /package/front_end/panels/recorder/{components/recordingListView.css → recordingListView.css} +0 -0
  171. /package/front_end/panels/recorder/{components/recordingView.css → recordingView.css} +0 -0
  172. /package/front_end/panels/recorder/{components/replaySection.css → replaySection.css} +0 -0
  173. /package/front_end/panels/recorder/{components/selectorPicker.css → selectorPicker.css} +0 -0
  174. /package/front_end/panels/recorder/{components/startView.css → startView.css} +0 -0
  175. /package/front_end/panels/recorder/{components/stepEditor.css → stepEditor.css} +0 -0
  176. /package/front_end/panels/recorder/{components/stepView.css → stepView.css} +0 -0
  177. /package/front_end/panels/recorder/{components/timelineSection.css → timelineSection.css} +0 -0
@@ -1,7 +1,6 @@
1
1
  // Copyright 2015 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-imperative-dom-api */
5
4
 
6
5
  import '../../ui/components/report_view/report_view.js';
7
6
  import '../../ui/kit/kit.js';
@@ -186,6 +185,116 @@ const {until} = Directives;
186
185
  const {widget} = UI.Widget;
187
186
  const {bindToSetting} = UI.UIUtils;
188
187
 
188
+ export interface SectionData {
189
+ manager: SDK.ServiceWorkerManager.ServiceWorkerManager;
190
+ registration: SDK.ServiceWorkerManager.ServiceWorkerRegistration;
191
+ }
192
+
193
+ export interface ServiceWorkersViewInput {
194
+ canManageServiceWorkers: boolean;
195
+ sections: SectionData[];
196
+ }
197
+
198
+ function renderToolbar(): LitTemplate {
199
+ const updateOnReloadSetting =
200
+ Common.Settings.Settings.instance().createSetting('service-worker-update-on-reload', false);
201
+ const bypassServiceWorkerSetting = Common.Settings.Settings.instance().createSetting('bypass-service-worker', false);
202
+ // clang-format off
203
+ return html`<devtools-toolbar class="service-worker-toolbar">
204
+ ${MobileThrottling.ThrottlingManager.throttlingManager().createOfflineToolbarCheckbox().element}
205
+ <devtools-checkbox title=${i18nString(UIStrings.onPageReloadForceTheService)}
206
+ ${bindToSetting(updateOnReloadSetting)}>
207
+ ${i18nString(UIStrings.updateOnReload)}
208
+ </devtools-checkbox>
209
+ <devtools-checkbox title=${i18nString(UIStrings.bypassTheServiceWorkerAndLoad)}
210
+ ${bindToSetting(bypassServiceWorkerSetting)}>
211
+ ${i18nString(UIStrings.bypassForNetwork)}
212
+ </devtools-checkbox>
213
+ </devtools-toolbar>`;
214
+ // clang-format on
215
+ }
216
+
217
+ function renderOthersOriginView(): LitTemplate {
218
+ // clang-format off
219
+ return html`<div class="service-workers-other-origin"
220
+ jslog=${VisualLogging.section('other-origin')}>
221
+ <devtools-report>
222
+ <devtools-report-section-header>
223
+ ${i18nString(UIStrings.serviceWorkersFromOtherOrigins)}
224
+ </devtools-report-section-header>
225
+ <div class="service-worker-section">
226
+ <devtools-link href="chrome://serviceworker-internals"
227
+ jslogcontext="view-all"
228
+ .allowPrivileged=${true}>
229
+ ${i18nString(UIStrings.seeAllRegistrations)}
230
+ </devtools-link>
231
+ </div>
232
+ </devtools-report>
233
+ </div>`;
234
+ // clang-format on
235
+ }
236
+
237
+ function getTimeStamp(registration: SDK.ServiceWorkerManager.ServiceWorkerRegistration): number {
238
+ const versions = registration.versionsByMode();
239
+ let timestamp: number|undefined = 0;
240
+ const active = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.ACTIVE);
241
+ const installing = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.INSTALLING);
242
+ const waiting = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.WAITING);
243
+ const redundant = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.REDUNDANT);
244
+
245
+ if (active) {
246
+ timestamp = active.scriptResponseTime;
247
+ } else if (waiting) {
248
+ timestamp = waiting.scriptResponseTime;
249
+ } else if (installing) {
250
+ timestamp = installing.scriptResponseTime;
251
+ } else if (redundant) {
252
+ timestamp = redundant.scriptResponseTime;
253
+ }
254
+
255
+ return timestamp || 0;
256
+ }
257
+
258
+ function renderOriginReport(input: ServiceWorkersViewInput): LitTemplate {
259
+ if (!input.canManageServiceWorkers) {
260
+ return nothing;
261
+ }
262
+
263
+ const sortedSections = [...input.sections];
264
+ sortedSections.sort((a, b) => {
265
+ const aTimestamp = getTimeStamp(a.registration);
266
+ const bTimestamp = getTimeStamp(b.registration);
267
+ return bTimestamp - aTimestamp;
268
+ });
269
+
270
+ // clang-format off
271
+ return html`<div class="service-workers-this-origin" jslog=${VisualLogging.section('this-origin')}>
272
+ <devtools-report .data=${{reportTitle: i18n.i18n.lockedString('Service workers')}}>
273
+ <div class="service-worker-toolbar" slot="toolbar">${renderToolbar()}</div>
274
+ ${sortedSections.map(section => html`<devtools-widget class="service-worker-section-container" ${widget(Section, {section})}></devtools-widget>`)}
275
+ </devtools-report>
276
+ </div>`;
277
+ // clang-format on
278
+ }
279
+
280
+ type View = (input: ServiceWorkersViewInput, output: undefined, target: HTMLElement) => void;
281
+ export const DEFAULT_VIEW: View = (input, _output, target): void => {
282
+ // clang-format off
283
+ render(html`
284
+ <!-- This Origin Report -->
285
+ ${renderOriginReport(input)}
286
+ ${renderOthersOriginView()}`,
287
+ target, {
288
+ container: {
289
+ classes: [
290
+ 'service-worker-list',
291
+ (input.sections.length > 0 ? 'service-worker-has-current' : 'service-worker-list-empty')
292
+ ]
293
+ }
294
+ });
295
+ // clang-format on
296
+ };
297
+
189
298
  let throttleDisabledForDebugging = false;
190
299
  export const setThrottleDisabledForDebugging = (enable: boolean): void => {
191
300
  throttleDisabledForDebugging = enable;
@@ -193,84 +302,55 @@ export const setThrottleDisabledForDebugging = (enable: boolean): void => {
193
302
 
194
303
  export class ServiceWorkersView extends UI.Widget.VBox implements
195
304
  SDK.TargetManager.SDKModelObserver<SDK.ServiceWorkerManager.ServiceWorkerManager> {
196
- currentWorkersView: UI.ReportView.ReportView;
197
- private readonly sections: Map<SDK.ServiceWorkerManager.ServiceWorkerRegistration, Section>;
305
+ private readonly sections: Map<SDK.ServiceWorkerManager.ServiceWorkerRegistration, SectionData>;
198
306
  private manager: SDK.ServiceWorkerManager.ServiceWorkerManager|null;
199
307
  private securityOriginManager: SDK.SecurityOriginManager.SecurityOriginManager|null;
200
- private readonly sectionToRegistration:
201
- WeakMap<UI.ReportView.Section, SDK.ServiceWorkerManager.ServiceWorkerRegistration>;
202
308
  private readonly eventListeners:
203
309
  Map<SDK.ServiceWorkerManager.ServiceWorkerManager, Common.EventTarget.EventDescriptor[]>;
310
+ readonly #output = undefined;
311
+
312
+ #view: (input: ServiceWorkersViewInput, output: undefined, target: HTMLElement) => void;
204
313
 
205
- constructor() {
314
+ constructor(view: View = DEFAULT_VIEW) {
206
315
  super({
207
316
  jslog: `${VisualLogging.pane('service-workers')}`,
208
317
  useShadowDom: true,
209
318
  });
319
+ this.#view = view;
210
320
  this.registerRequiredCSS(serviceWorkersViewStyles);
211
321
 
212
- // TODO(crbug.com/1156978): Replace UI.ReportView.ReportView with ReportView.ts web component.
213
- this.currentWorkersView = new UI.ReportView.ReportView(i18n.i18n.lockedString('Service workers'));
214
- this.currentWorkersView.setBodyScrollable(false);
215
- this.contentElement.classList.add('service-worker-list');
216
- this.currentWorkersView.show(this.contentElement);
217
- this.currentWorkersView.element.classList.add('service-workers-this-origin');
218
- this.currentWorkersView.element.setAttribute('jslog', `${VisualLogging.section('this-origin')}`);
219
-
220
322
  this.sections = new Map();
221
323
 
222
324
  this.manager = null;
223
325
  this.securityOriginManager = null;
224
326
 
225
- this.sectionToRegistration = new WeakMap();
226
-
227
- this.createOthersOriginView();
228
- this.setupToolbar();
229
-
230
327
  this.eventListeners = new Map();
231
328
  SDK.TargetManager.TargetManager.instance().observeModels(SDK.ServiceWorkerManager.ServiceWorkerManager, this);
232
- this.updateListVisibility();
233
329
  }
234
330
 
235
- private createOthersOriginView(): void {
236
- // clang-format off
237
- // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
238
- render(html`<div class="service-workers-other-origin"
239
- jslog=${VisualLogging.section('other-origin')}>
240
- <devtools-report>
241
- <devtools-report-section-header>
242
- ${i18nString(UIStrings.serviceWorkersFromOtherOrigins)}
243
- </devtools-report-section-header>
244
- <devtools-report-section>
245
- <devtools-link href="chrome://serviceworker-internals"
246
- jslogcontext="view-all"
247
- .allowPrivileged=${true}>
248
- ${i18nString(UIStrings.seeAllRegistrations)}
249
- </devtools-link>
250
- </devtools-report-section>
251
- </devtools-report>
252
- </div>`, this.contentElement);
253
- // clang-format on
331
+ override wasShown(): void {
332
+ super.wasShown();
333
+ this.requestUpdate();
254
334
  }
255
- private setupToolbar(): void {
256
- const updateOnReloadSetting =
257
- Common.Settings.Settings.instance().createSetting('service-worker-update-on-reload', false);
258
- const bypassServiceWorkerSetting =
259
- Common.Settings.Settings.instance().createSetting('bypass-service-worker', false);
260
- // clang-format off
261
- // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
262
- render(html`<devtools-toolbar class="service-worker-toolbar">
263
- ${MobileThrottling.ThrottlingManager.throttlingManager().createOfflineToolbarCheckbox().element}
264
- <devtools-checkbox title=${i18nString(UIStrings.onPageReloadForceTheService)}
265
- ${bindToSetting(updateOnReloadSetting)}>
266
- ${i18nString(UIStrings.updateOnReload)}
267
- </devtools-checkbox>
268
- <devtools-checkbox title=${i18nString(UIStrings.bypassTheServiceWorkerAndLoad)}
269
- ${bindToSetting(bypassServiceWorkerSetting)}>
270
- ${i18nString(UIStrings.bypassForNetwork)}
271
- </devtools-checkbox>
272
- </devtools-toolbar>`, this.currentWorkersView.getHeaderElement() as HTMLElement);
273
- // clang-format on
335
+
336
+ override async performUpdate(): Promise<void> {
337
+ if (this.manager) {
338
+ for (const registration of this.manager.registrations().values()) {
339
+ const isCurrent = this.isOriginCurrent(registration.securityOrigin);
340
+ if (isCurrent && !this.sections.has(registration)) {
341
+ this.sections.set(registration, {manager: this.manager, registration});
342
+ } else if (!isCurrent && this.sections.has(registration)) {
343
+ this.sections.delete(registration);
344
+ }
345
+ }
346
+ }
347
+
348
+ const input: ServiceWorkersViewInput = {
349
+ canManageServiceWorkers: this.manager !== null,
350
+ sections: Array.from(this.sections.values()).map(data => ({...data})),
351
+ };
352
+
353
+ this.#view(input, this.#output, this.contentElement);
274
354
  }
275
355
 
276
356
  modelAdded(serviceWorkerManager: SDK.ServiceWorkerManager.ServiceWorkerManager): void {
@@ -292,9 +372,9 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
292
372
  this.manager.addEventListener(SDK.ServiceWorkerManager.Events.REGISTRATION_DELETED, this.registrationDeleted,
293
373
  this),
294
374
  this.securityOriginManager.addEventListener(SDK.SecurityOriginManager.Events.SecurityOriginAdded,
295
- this.updateSectionVisibility, this),
375
+ this.requestUpdate, this),
296
376
  this.securityOriginManager.addEventListener(SDK.SecurityOriginManager.Events.SecurityOriginRemoved,
297
- this.updateSectionVisibility, this),
377
+ this.requestUpdate, this),
298
378
  ]);
299
379
  }
300
380
 
@@ -309,67 +389,6 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
309
389
  this.securityOriginManager = null;
310
390
  }
311
391
 
312
- private getTimeStamp(registration: SDK.ServiceWorkerManager.ServiceWorkerRegistration): number {
313
- const versions = registration.versionsByMode();
314
-
315
- let timestamp: number|undefined = 0;
316
-
317
- const active = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.ACTIVE);
318
- const installing = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.INSTALLING);
319
- const waiting = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.WAITING);
320
- const redundant = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.REDUNDANT);
321
-
322
- if (active) {
323
- timestamp = active.scriptResponseTime;
324
- } else if (waiting) {
325
- timestamp = waiting.scriptResponseTime;
326
- } else if (installing) {
327
- timestamp = installing.scriptResponseTime;
328
- } else if (redundant) {
329
- timestamp = redundant.scriptResponseTime;
330
- }
331
-
332
- return timestamp || 0;
333
- }
334
-
335
- private updateSectionVisibility(): void {
336
- let hasThis = false;
337
- const movedSections = [];
338
- for (const section of this.sections.values()) {
339
- const expectedView = this.getReportViewForOrigin(section.registration.securityOrigin);
340
- hasThis = hasThis || expectedView === this.currentWorkersView;
341
- if (section.section.parentWidget() !== expectedView) {
342
- movedSections.push(section);
343
- }
344
- }
345
-
346
- for (const section of movedSections) {
347
- const registration = section.registration;
348
- this.removeRegistrationFromList(registration, true);
349
- this.updateRegistration(registration, true);
350
- }
351
-
352
- this.currentWorkersView.sortSections((aSection, bSection) => {
353
- const aRegistration = this.sectionToRegistration.get(aSection);
354
- const bRegistration = this.sectionToRegistration.get(bSection);
355
- const aTimestamp = aRegistration ? this.getTimeStamp(aRegistration) : 0;
356
- const bTimestamp = bRegistration ? this.getTimeStamp(bRegistration) : 0;
357
- // the newest (largest timestamp value) should be the first
358
- return bTimestamp - aTimestamp;
359
- });
360
-
361
- for (const section of this.sections.values()) {
362
- if (section.section.parentWidget() === this.currentWorkersView ||
363
- this.isRegistrationVisible(section.registration)) {
364
- section.section.showWidget();
365
- } else {
366
- section.section.hideWidget();
367
- }
368
- }
369
- this.contentElement.classList.toggle('service-worker-has-current', Boolean(hasThis));
370
- this.updateListVisibility();
371
- }
372
-
373
392
  private registrationUpdated(
374
393
  event: Common.EventTarget.EventTargetEvent<SDK.ServiceWorkerManager.ServiceWorkerRegistration>): void {
375
394
  this.updateRegistration(event.data);
@@ -404,35 +423,32 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
404
423
  }
405
424
  }
406
425
 
407
- private getReportViewForOrigin(origin: string): UI.ReportView.ReportView|null {
426
+ private isOriginCurrent(origin: string): boolean {
408
427
  if (this.securityOriginManager &&
409
428
  (this.securityOriginManager.securityOrigins().includes(origin) ||
410
429
  this.securityOriginManager.unreachableMainSecurityOrigin() === origin)) {
411
- return this.currentWorkersView;
430
+ return true;
412
431
  }
413
- return null;
432
+ return false;
414
433
  }
415
434
 
416
435
  private updateRegistration(registration: SDK.ServiceWorkerManager.ServiceWorkerRegistration,
417
436
  skipUpdate?: boolean): void {
418
- let section = this.sections.get(registration);
419
- if (!section) {
420
- const title = registration.scopeURL;
421
- const reportView = this.getReportViewForOrigin(registration.securityOrigin);
422
- if (!reportView) {
437
+ if (!this.manager) {
438
+ return;
439
+ }
440
+ let sectionData = this.sections.get(registration);
441
+ if (!sectionData) {
442
+ if (!this.isOriginCurrent(registration.securityOrigin)) {
423
443
  return;
424
444
  }
425
- const uiSection = reportView.appendSection(title);
426
- uiSection.setUiGroupTitle(i18nString(UIStrings.serviceWorkerForS, {PH1: title}));
427
- this.sectionToRegistration.set(uiSection, registration);
428
- section = new Section((this.manager as SDK.ServiceWorkerManager.ServiceWorkerManager), uiSection, registration);
429
- this.sections.set(registration, section);
445
+ sectionData = {manager: this.manager, registration};
446
+ this.sections.set(registration, sectionData);
430
447
  }
431
448
  if (skipUpdate) {
432
449
  return;
433
450
  }
434
- this.updateSectionVisibility();
435
- section.scheduleUpdate();
451
+ this.requestUpdate();
436
452
  }
437
453
 
438
454
  private registrationDeleted(
@@ -442,13 +458,9 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
442
458
 
443
459
  private removeRegistrationFromList(registration: SDK.ServiceWorkerManager.ServiceWorkerRegistration,
444
460
  skipVisibilityUpdate = false): void {
445
- const section = this.sections.get(registration);
446
- if (section) {
447
- section.section.detach();
448
- }
449
461
  this.sections.delete(registration);
450
462
  if (!skipVisibilityUpdate) {
451
- this.updateSectionVisibility();
463
+ this.requestUpdate();
452
464
  }
453
465
  }
454
466
 
@@ -458,253 +470,349 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
458
470
  }
459
471
  return false;
460
472
  }
473
+ }
461
474
 
462
- private updateListVisibility(): void {
463
- this.contentElement.classList.toggle('service-worker-list-empty', this.sections.size === 0);
464
- }
475
+ export interface SectionViewInput {
476
+ title: string;
477
+ isDeleted: boolean;
478
+ errorsLength: number;
479
+ pushData: string;
480
+ syncTag: string;
481
+ periodicSyncTag: string;
482
+ updateCycleTable: HTMLElement;
483
+ activeVersion?: SDK.ServiceWorkerManager.ServiceWorkerVersion;
484
+ waitingVersion?: SDK.ServiceWorkerManager.ServiceWorkerVersion;
485
+ installingVersion?: SDK.ServiceWorkerManager.ServiceWorkerVersion;
486
+ redundantVersion?: SDK.ServiceWorkerManager.ServiceWorkerVersion;
487
+ renderClientInfo: (clientId: Protocol.Target.TargetID) => Promise<LitTemplate|typeof nothing>;
488
+ onNetworkRequests: () => void;
489
+ onUpdate: () => void;
490
+ onUnregister: () => void;
491
+ onPush: (data: string) => void;
492
+ onSync: (tag: string) => void;
493
+ onPeriodicSync: (tag: string) => void;
494
+ onStop: (versionId: string) => void;
495
+ onStart: () => void;
496
+ onSkipWaiting: () => void;
465
497
  }
466
498
 
467
- export class Section extends UI.Widget.VBox {
468
- private manager: SDK.ServiceWorkerManager.ServiceWorkerManager;
469
- section: UI.ReportView.Section;
470
- registration: SDK.ServiceWorkerManager.ServiceWorkerRegistration;
471
- private fingerprint: symbol|null;
472
- private readonly pushNotificationDataSetting: Common.Settings.Setting<string>;
473
- private readonly syncTagNameSetting: Common.Settings.Setting<string>;
474
- private readonly periodicSyncTagNameSetting: Common.Settings.Setting<string>;
475
- private readonly updateCycleView: ServiceWorkerUpdateCycleView;
476
- private readonly clientInfoCache: Map<string, Protocol.Target.TargetInfo>;
477
- private readonly throttler: Common.Throttler.Throttler;
499
+ function renderHeaderButtons(input: SectionViewInput): LitTemplate {
500
+ // clang-format off
501
+ return html`
502
+ <devtools-button .data=${{
503
+ variant: Buttons.Button.Variant.TEXT,
504
+ title: i18nString(UIStrings.networkRequests),
505
+ jslogContext: 'show-network-requests'
506
+ } as Buttons.Button.ButtonData}
507
+ .disabled=${input.isDeleted}
508
+ @click=${input.onNetworkRequests}>
509
+ ${i18nString(UIStrings.networkRequests)}
510
+ </devtools-button>
511
+ <devtools-button .data=${{
512
+ variant: Buttons.Button.Variant.TEXT,
513
+ title: i18nString(UIStrings.update),
514
+ jslogContext: 'update'
515
+ } as Buttons.Button.ButtonData}
516
+ .disabled=${input.isDeleted}
517
+ @click=${input.onUpdate}>
518
+ ${i18nString(UIStrings.update)}
519
+ </devtools-button>
520
+ <devtools-button .data=${{
521
+ variant: Buttons.Button.Variant.TEXT,
522
+ title: i18nString(UIStrings.unregisterServiceWorker),
523
+ jslogContext: 'unregister'
524
+ } as Buttons.Button.ButtonData}
525
+ .disabled=${input.isDeleted}
526
+ @click=${input.onUnregister}>
527
+ ${i18nString(UIStrings.unregister)}
528
+ </devtools-button>`;
529
+ // clang-format on
530
+ }
478
531
 
479
- constructor(manager: SDK.ServiceWorkerManager.ServiceWorkerManager, section: UI.ReportView.Section,
480
- registration: SDK.ServiceWorkerManager.ServiceWorkerRegistration) {
481
- super();
482
- this.manager = manager;
483
- this.section = section;
484
- this.registration = registration;
485
- this.fingerprint = null;
486
- this.pushNotificationDataSetting = Common.Settings.Settings.instance().createLocalSetting(
487
- 'push-data', i18nString(UIStrings.testPushMessageFromDevtools));
488
- this.syncTagNameSetting =
489
- Common.Settings.Settings.instance().createLocalSetting('sync-tag-name', 'test-tag-from-devtools');
490
- this.periodicSyncTagNameSetting =
491
- Common.Settings.Settings.instance().createLocalSetting('periodic-sync-tag-name', 'test-tag-from-devtools');
532
+ function renderSyncNotificationField(label: string, initialValue: string, placeholder: string,
533
+ callback: (arg0: string) => void, jslogContext: string): LitTemplate {
534
+ // clang-format off
535
+ return html`
536
+ <div class="report-field">
537
+ <div class="report-field-name">${label}</div>
538
+ <div class="report-field-value">
539
+ <form class="service-worker-editor-with-button" @submit=${(e: Event) => {
540
+ const {editor} = e.target as HTMLFormElement;
541
+ callback(editor.value || '');
542
+ e.consume(true);
543
+ }}>
544
+ <input name="editor" class="source-code service-worker-notification-editor harmony-input" type="text"
545
+ .value=${initialValue}
546
+ placeholder=${placeholder}
547
+ aria-label=${label}
548
+ .spellcheck=${false}
549
+ jslog=${VisualLogging.textField().track({change: true}).context(jslogContext)}
550
+ >
551
+ <devtools-button .data=${{
552
+ type: 'submit',
553
+ variant: Buttons.Button.Variant.OUTLINED,
554
+ jslogContext} as Buttons.Button.ButtonData}>
555
+ ${label}
556
+ </devtools-button>
557
+ </form>
558
+ </div>
559
+ </div>`;
560
+ // clang-format on
561
+ }
492
562
 
493
- this.updateCycleView = new ServiceWorkerUpdateCycleView(registration);
563
+ function renderVersion(icon: string, label: string, content: LitTemplate = nothing): LitTemplate {
564
+ // clang-format off
565
+ return html`
566
+ <div class="service-worker-version">
567
+ <div class=${icon}></div>
568
+ <span class="service-worker-version-string" role="alert" aria-live="polite">
569
+ ${label}
570
+ </span>
571
+ ${content}
572
+ </div>`;
573
+ // clang-format on
574
+ }
494
575
 
495
- this.registerRequiredCSS(serviceWorkersViewStyles, serviceWorkerUpdateCycleViewStyles);
496
- this.show(this.section.getFieldElement());
576
+ function renderClientsField(input: SectionViewInput,
577
+ version?: SDK.ServiceWorkerManager.ServiceWorkerVersion): LitTemplate {
578
+ if (!version?.controlledClients?.length) {
579
+ return html`<div class="report-field">
580
+ <div class="report-field-name">${i18nString(UIStrings.clients)}</div>
581
+ <div class="report-field-value"></div>
582
+ </div>`;
583
+ }
584
+ // clang-format off
585
+ return html`<div class="report-field">
586
+ <div class="report-field-name">${i18nString(UIStrings.clients)}</div>
587
+ <div class="report-field-value">
588
+ ${version.controlledClients.map(client => html`
589
+ <div class="service-worker-client">
590
+ ${until(input.renderClientInfo(client))}
591
+ </div>`)}
592
+ </div>
593
+ </div>`;
594
+ // clang-format on
595
+ }
497
596
 
498
- this.clientInfoCache = new Map();
499
- this.throttler = new Common.Throttler.Throttler(500);
597
+ function renderSourceField(input: SectionViewInput,
598
+ version?: SDK.ServiceWorkerManager.ServiceWorkerVersion): LitTemplate {
599
+ if (!version) {
600
+ return html`<div class="report-field">
601
+ <div class="report-field-name">${i18nString(UIStrings.source)}</div>
602
+ <div class="report-field-value"></div>
603
+ </div>`;
500
604
  }
605
+ const fileName = Common.ParsedURL.ParsedURL.extractName(version.scriptURL);
606
+ // clang-format off
607
+
608
+ return html`<div class="report-field">
609
+ <div class="report-field-name">${i18nString(UIStrings.source)}</div>
610
+ <div class="report-field-value">
611
+ <div class="report-field-value-filename">
612
+ ${Components.Linkifier.Linkifier.renderLinkifiedUrl(version.scriptURL, {
613
+ text: fileName, tabStop: true, jslogContext: 'source-location'})}
614
+ ${input.errorsLength ? html`
615
+ <button
616
+ class="devtools-link link"
617
+ tabindex="0"
618
+ aria-label=${i18nString(UIStrings.sRegistrationErrors, {PH1: input.errorsLength})}
619
+ @click=${() => Common.Console.Console.instance().show()}>
620
+ <devtools-icon name="cross-circle-filled" class="error-icon">
621
+ </devtools-icon>
622
+ ${input.errorsLength}
623
+ </button>` : nothing}
624
+ </div>
625
+ ${version.scriptResponseTime !== undefined ? html`
626
+ <div class="report-field-value-subtitle">
627
+ ${i18nString(UIStrings.receivedS, {PH1: new Date(version.scriptResponseTime * 1000).toLocaleString()})}
628
+ </div>
629
+ ` : nothing}
630
+ </div>
631
+ </div>`;
632
+ // clang-format on
633
+ }
501
634
 
502
- private renderHeaderButtons(): LitTemplate {
503
- // clang-format off
635
+ function renderStatusField(input: SectionViewInput, active?: SDK.ServiceWorkerManager.ServiceWorkerVersion,
636
+ waiting?: SDK.ServiceWorkerManager.ServiceWorkerVersion,
637
+ installing?: SDK.ServiceWorkerManager.ServiceWorkerVersion,
638
+ redundant?: SDK.ServiceWorkerManager.ServiceWorkerVersion): LitTemplate {
639
+ // clang-format off
640
+
641
+ return html`<div class="report-field">
642
+ <div class="report-field-name">${i18nString(UIStrings.status)}</div>
643
+ <div class="report-field-value">
644
+ <div class="service-worker-version-stack">
645
+ <div class="service-worker-version-stack-bar"></div>
646
+ ${active ? renderVersion(
647
+ 'service-worker-active-circle',
648
+ i18nString(UIStrings.sActivatedAndIsS, {
649
+ PH1: active.id,
650
+ PH2: SDK.ServiceWorkerManager.ServiceWorkerVersion.RunningStatus[active.currentState.runningStatus](),
651
+ }),
652
+ active.isRunning() || active.isStarting() ? html`
653
+ <devtools-button .data=${{jslogContext: 'stop', variant: Buttons.Button.Variant.OUTLINED} as Buttons.Button.ButtonData}
654
+ @click=${() => input.onStop(active.id)}>
655
+ ${i18nString(UIStrings.stopString)}
656
+ </devtools-button>`
657
+ : active.isStartable() ? html`
658
+ <devtools-button .data=${{jslogContext: 'start', variant: Buttons.Button.Variant.OUTLINED} as Buttons.Button.ButtonData}
659
+ @click=${input.onStart}>
660
+ ${i18nString(UIStrings.startString)}
661
+ </devtools-button>`
662
+ : nothing)
663
+ : redundant ? renderVersion(
664
+ 'service-worker-redundant-circle',
665
+ i18nString(UIStrings.sIsRedundant, {PH1: redundant.id}))
666
+ : nothing}
667
+ ${waiting ? renderVersion(
668
+ 'service-worker-waiting-circle',
669
+ i18nString(UIStrings.sWaitingToActivate, {PH1: waiting.id}), html`
670
+ <devtools-button .data=${{
671
+ jslogContext: 'skip-waiting',
672
+ title: i18n.i18n.lockedString('skipWaiting'),
673
+ variant: Buttons.Button.Variant.OUTLINED} as Buttons.Button.ButtonData}
674
+ @click=${input.onSkipWaiting}>
675
+ ${i18n.i18n.lockedString('skipWaiting')}
676
+ </devtools-button>
677
+ ${waiting.scriptResponseTime !== undefined ? html`
678
+ <div class="service-worker-subtitle">
679
+ ${i18nString(UIStrings.receivedS, {PH1: new Date(waiting.scriptResponseTime * 1000).toLocaleString()})}
680
+ </div>
681
+ ` : nothing}
682
+ `,
683
+ ) : nothing}
684
+ ${installing ? renderVersion(
685
+ 'service-worker-installing-circle',
686
+ i18nString(UIStrings.sTryingToInstall, {PH1: installing.id}),
687
+ installing.scriptResponseTime !== undefined ? html`
688
+ <div class="service-worker-subtitle">
689
+ ${i18nString(UIStrings.receivedS, {PH1: new Date(installing.scriptResponseTime * 1000).toLocaleString()})}
690
+ </div>` : nothing) : nothing}
691
+ </div>
692
+ </div>
693
+ </div>`;
694
+ // clang-format on
695
+ }
504
696
 
505
- return html`
506
- <devtools-button .data=${{
507
- variant: Buttons.Button.Variant.TEXT,
508
- title: i18nString(UIStrings.networkRequests),
509
- jslogContext: 'show-network-requests'
510
- } as Buttons.Button.ButtonData}
511
- .disabled=${this.registration.isDeleted}
512
- @click=${this.networkRequestsClicked.bind(this)}>
513
- ${i18nString(UIStrings.networkRequests)}
514
- </devtools-button>
515
- <devtools-button .data=${{
516
- variant: Buttons.Button.Variant.TEXT,
517
- title: i18nString(UIStrings.update),
518
- jslogContext: 'update'
519
- } as Buttons.Button.ButtonData}
520
- .disabled=${this.registration.isDeleted}
521
- @click=${this.updateButtonClicked.bind(this)}>
522
- ${i18nString(UIStrings.update)}
523
- </devtools-button>
524
- <devtools-button .data=${{
525
- variant: Buttons.Button.Variant.TEXT,
526
- title: i18nString(UIStrings.unregisterServiceWorker),
527
- jslogContext: 'unregister'
528
- } as Buttons.Button.ButtonData}
529
- .disabled=${this.registration.isDeleted}
530
- @click=${this.unregisterButtonClicked.bind(this)}>
531
- ${i18nString(UIStrings.unregister)}
532
- </devtools-button>`;
533
- // clang-format on
534
- }
697
+ function renderUpdateCycleField(input: SectionViewInput): LitTemplate {
698
+ return html`
699
+ <div class="report-field">
700
+ <div class="report-field-name">${i18nString(UIStrings.updateCycle)}</div>
701
+ <div class="report-field-value">
702
+ ${input.updateCycleTable}
703
+ </div>
704
+ </div>`;
705
+ }
535
706
 
536
- private renderSyncNotificationField(label: string, initialValue: string, placeholder: string,
537
- callback: (arg0: string) => void, jslogContext: string): LitTemplate {
707
+ function renderRouterField(input: SectionViewInput): LitTemplate {
708
+ const active = input.activeVersion;
709
+ const title = i18nString(UIStrings.routers);
710
+ if (active?.routerRules && active.routerRules.length > 0) {
711
+ // If there is at least one registered rule in the active version, append the router filed.
538
712
  // clang-format off
539
713
  return html`
540
714
  <div class="report-field">
541
- <div class="report-field-name">${label}</div>
715
+ <div class="report-field-name">${title}</div>
542
716
  <div class="report-field-value">
543
- <form class="service-worker-editor-with-button" @submit=${(e: Event) => {
544
- const {editor} = e.target as HTMLFormElement;
545
- callback(editor.value || '');
546
- e.consume(true);
547
- }}>
548
- <input name="editor" class="source-code service-worker-notification-editor harmony-input" type="text"
549
- .value=${initialValue}
550
- placeholder=${placeholder}
551
- aria-label=${label}
552
- .spellcheck=${false}
553
- jslog=${VisualLogging.textField().track({change: true}).context(jslogContext)}
554
- >
555
- <devtools-button .data=${{
556
- type: 'submit',
557
- variant: Buttons.Button.Variant.OUTLINED,
558
- jslogContext} as Buttons.Button.ButtonData}>
559
- ${label}
560
- </devtools-button>
561
- </form>
717
+ ${widget(ApplicationComponents.ServiceWorkerRouterView.ServiceWorkerRouterView, { rules: active.routerRules })}
562
718
  </div>
563
719
  </div>`;
564
720
  // clang-format on
565
721
  }
722
+ return nothing;
723
+ }
566
724
 
567
- scheduleUpdate(): void {
568
- if (throttleDisabledForDebugging) {
569
- void this.performUpdate();
570
- return;
571
- }
572
- void this.throttler.schedule(() => {
573
- this.requestUpdate();
574
- return this.updateComplete;
575
- });
576
- }
725
+ type SectionView = (input: SectionViewInput, _output: undefined, target: HTMLElement) => void;
577
726
 
578
- private renderVersion(icon: string, label: string, content: LitTemplate = nothing): LitTemplate {
579
- // clang-format off
580
- return html`
581
- <div class="service-worker-version">
582
- <div class=${icon}></div>
583
- <span class="service-worker-version-string" role="alert" aria-live="polite">
584
- ${label}
585
- </span>
586
- ${content}
587
- </div>`;
588
- // clang-format on
589
- }
727
+ export const DEFAULT_SECTION_VIEW: SectionView =
728
+ (input: SectionViewInput, _output: undefined, target: HTMLElement): void => {
729
+ // clang-format off
730
+ render(html`
731
+ <style>${serviceWorkersViewStyles}</style>
732
+ <style>${serviceWorkerUpdateCycleViewStyles}</style>
733
+ <devtools-report-section-header role="heading" aria-level="2"
734
+ aria-label=${i18nString(UIStrings.serviceWorkerForS, { PH1: input.title })}>
735
+ <span style="flex: 1 1 auto">${input.title}</span>
736
+ ${renderHeaderButtons(input)}
737
+ </devtools-report-section-header>
738
+ <div class="service-worker-section">
739
+ ${renderSourceField(input, input.activeVersion ?? input.redundantVersion)}
740
+ ${renderStatusField(input, input.activeVersion, input.waitingVersion, input.installingVersion, input.redundantVersion)}
741
+ ${renderClientsField(input, input.activeVersion ?? input.redundantVersion)}
742
+ ${renderSyncNotificationField(i18nString(UIStrings.pushString), input.pushData,
743
+ i18nString(UIStrings.pushData), input.onPush, 'push-message')}
744
+ ${renderSyncNotificationField(i18nString(UIStrings.syncString), input.syncTag,
745
+ i18nString(UIStrings.syncTag), input.onSync, 'sync-tag')}
746
+ ${renderSyncNotificationField(i18nString(UIStrings.periodicSync), input.periodicSyncTag,
747
+ i18nString(UIStrings.periodicSyncTag), input.onPeriodicSync,
748
+ 'periodic-sync-tag')}
749
+ ${renderUpdateCycleField(input)}
750
+ ${renderRouterField(input)}
751
+ </div>
752
+ `, target);
753
+ // clang-format on
754
+ };
590
755
 
591
- private renderClientsField(version?: SDK.ServiceWorkerManager.ServiceWorkerVersion): LitTemplate {
592
- if (!version?.controlledClients?.length) {
593
- return html`<div class="report-field">
594
- <div class="report-field-name">${i18nString(UIStrings.clients)}</div>
595
- <div class="report-field-value"></div>
596
- </div>`;
756
+ export class Section extends UI.Widget.VBox {
757
+ private manager!: SDK.ServiceWorkerManager.ServiceWorkerManager;
758
+ registration!: SDK.ServiceWorkerManager.ServiceWorkerRegistration;
759
+ private sectionInternal!: SectionData;
760
+ private fingerprint: symbol|null;
761
+ private pushNotificationDataSetting!: Common.Settings.Setting<string>;
762
+ private syncTagNameSetting!: Common.Settings.Setting<string>;
763
+ private periodicSyncTagNameSetting!: Common.Settings.Setting<string>;
764
+ private updateCycleView!: ServiceWorkerUpdateCycleView;
765
+ private readonly clientInfoCache: Map<string, Protocol.Target.TargetInfo>;
766
+ private readonly throttler: Common.Throttler.Throttler;
767
+ #view: SectionView;
768
+
769
+ constructor(element: HTMLElement, view = DEFAULT_SECTION_VIEW) {
770
+ super(element);
771
+ this.fingerprint = null;
772
+ this.clientInfoCache = new Map();
773
+ this.throttler = new Common.Throttler.Throttler(500);
774
+ this.#view = view;
775
+ }
776
+
777
+ set section(data: SectionData) {
778
+ const registrationChanged = !this.registration || this.registration !== data.registration;
779
+ this.sectionInternal = data;
780
+ this.manager = data.manager;
781
+ this.registration = data.registration;
782
+
783
+ if (!this.pushNotificationDataSetting) {
784
+ this.pushNotificationDataSetting = Common.Settings.Settings.instance().createLocalSetting(
785
+ 'push-data', i18nString(UIStrings.testPushMessageFromDevtools));
786
+ this.syncTagNameSetting =
787
+ Common.Settings.Settings.instance().createLocalSetting('sync-tag-name', 'test-tag-from-devtools');
788
+ this.periodicSyncTagNameSetting =
789
+ Common.Settings.Settings.instance().createLocalSetting('periodic-sync-tag-name', 'test-tag-from-devtools');
597
790
  }
598
- // clang-format off
599
- return html`<div class="report-field">
600
- <div class="report-field-name">${i18nString(UIStrings.clients)}</div>
601
- <div class="report-field-value">
602
- ${version.controlledClients.map(client => html`
603
- <div class="service-worker-client">
604
- ${until(this.renderClientInfo(client))}
605
- </div>`)}
606
- </div>
607
- </div>`;
608
- // clang-format on
609
- }
610
791
 
611
- private renderSourceField(version?: SDK.ServiceWorkerManager.ServiceWorkerVersion): LitTemplate {
612
- if (!version) {
613
- return html`<div class="report-field">
614
- <div class="report-field-name">${i18nString(UIStrings.source)}</div>
615
- <div class="report-field-value"></div>
616
- </div>`;
792
+ if (registrationChanged) {
793
+ this.updateCycleView = new ServiceWorkerUpdateCycleView(this.registration);
794
+ this.clientInfoCache.clear();
617
795
  }
618
- const fileName = Common.ParsedURL.ParsedURL.extractName(version.scriptURL);
619
- // clang-format off
796
+ }
620
797
 
621
- return html`<div class="report-field">
622
- <div class="report-field-name">${i18nString(UIStrings.source)}</div>
623
- <div class="report-field-value">
624
- <div class="report-field-value-filename">
625
- ${Components.Linkifier.Linkifier.renderLinkifiedUrl(version.scriptURL, {
626
- text: fileName, tabStop: true, jslogContext: 'source-location'})}
627
- ${this.registration.errors.length ? html`
628
- <button
629
- class="devtools-link link"
630
- tabindex="0"
631
- aria-label=${i18nString(UIStrings.sRegistrationErrors, {PH1: this.registration.errors.length})}
632
- @click=${() => Common.Console.Console.instance().show()}>
633
- <devtools-icon name="cross-circle-filled" class="error-icon">
634
- </devtools-icon>
635
- ${this.registration.errors.length}
636
- </button>` : nothing}
637
- </div>
638
- ${version.scriptResponseTime !== undefined ? html`
639
- <div class="report-field-value-subtitle">
640
- ${i18nString(UIStrings.receivedS, {PH1: new Date(version.scriptResponseTime * 1000).toLocaleString()})}
641
- </div>
642
- ` : nothing}
643
- </div>
644
- </div>`;
645
- // clang-format on
798
+ get section(): SectionData {
799
+ return this.sectionInternal;
646
800
  }
647
801
 
648
- private renderStatusField(active?: SDK.ServiceWorkerManager.ServiceWorkerVersion,
649
- waiting?: SDK.ServiceWorkerManager.ServiceWorkerVersion,
650
- installing?: SDK.ServiceWorkerManager.ServiceWorkerVersion,
651
- redundant?: SDK.ServiceWorkerManager.ServiceWorkerVersion): LitTemplate {
652
- // clang-format off
802
+ getTitle(): string {
803
+ const scopeURL = this.registration.scopeURL;
804
+ return this.registration.isDeleted ? i18nString(UIStrings.sDeleted, {PH1: scopeURL}) : scopeURL;
805
+ }
653
806
 
654
- return html`<div class="report-field">
655
- <div class="report-field-name">${i18nString(UIStrings.status)}</div>
656
- <div class="report-field-value">
657
- <div class="service-worker-version-stack">
658
- <div class="service-worker-version-stack-bar"></div>
659
- ${active ? this.renderVersion(
660
- 'service-worker-active-circle',
661
- i18nString(UIStrings.sActivatedAndIsS, {
662
- PH1: active.id,
663
- PH2: SDK.ServiceWorkerManager.ServiceWorkerVersion.RunningStatus[active.currentState.runningStatus](),
664
- }),
665
- active.isRunning() || active.isStarting() ? html`
666
- <devtools-button .data=${{jslogContext: 'stop', variant: Buttons.Button.Variant.OUTLINED} as Buttons.Button.ButtonData}
667
- @click=${this.stopButtonClicked.bind(this, active.id)}>
668
- ${i18nString(UIStrings.stopString)}
669
- </devtools-button>`
670
- : active.isStartable() ? html`
671
- <devtools-button .data=${{jslogContext: 'start', variant: Buttons.Button.Variant.OUTLINED} as Buttons.Button.ButtonData}
672
- @click=${this.startButtonClicked.bind(this)}>
673
- ${i18nString(UIStrings.startString)}
674
- </devtools-button>`
675
- : nothing)
676
- : redundant ? this.renderVersion(
677
- 'service-worker-redundant-circle',
678
- i18nString(UIStrings.sIsRedundant, {PH1: redundant.id}))
679
- : nothing}
680
- ${waiting ? this.renderVersion(
681
- 'service-worker-waiting-circle',
682
- i18nString(UIStrings.sWaitingToActivate, {PH1: waiting.id}), html`
683
- <devtools-button .data=${{
684
- jslogContext: 'skip-waiting',
685
- title: i18n.i18n.lockedString('skipWaiting'),
686
- variant: Buttons.Button.Variant.OUTLINED} as Buttons.Button.ButtonData}
687
- @click=${this.skipButtonClicked.bind(this)}>
688
- ${i18n.i18n.lockedString('skipWaiting')}
689
- </devtools-button>
690
- ${waiting.scriptResponseTime !== undefined ? html`
691
- <div class="service-worker-subtitle">
692
- ${i18nString(UIStrings.receivedS, {PH1: new Date(waiting.scriptResponseTime * 1000).toLocaleString()})}
693
- </div>
694
- ` : nothing}
695
- `,
696
- ) : nothing}
697
- ${installing ? this.renderVersion(
698
- 'service-worker-installing-circle',
699
- i18nString(UIStrings.sTryingToInstall, {PH1: installing.id}),
700
- installing.scriptResponseTime !== undefined ? html`
701
- <div class="service-worker-subtitle">
702
- ${i18nString(UIStrings.receivedS, {PH1: new Date(installing.scriptResponseTime * 1000).toLocaleString()})}
703
- </div>` : nothing) : nothing}
704
- </div>
705
- </div>
706
- </div>`;
707
- // clang-format on
807
+ override requestUpdate(): void {
808
+ if (throttleDisabledForDebugging) {
809
+ super.requestUpdate();
810
+ return;
811
+ }
812
+ void this.throttler.schedule(() => {
813
+ super.requestUpdate();
814
+ return Promise.resolve();
815
+ });
708
816
  }
709
817
 
710
818
  override performUpdate(): Promise<void> {
@@ -714,36 +822,40 @@ export class Section extends UI.Widget.VBox {
714
822
  }
715
823
  this.fingerprint = fingerprint;
716
824
 
717
- // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
718
- render(this.renderHeaderButtons(), this.section.getHeaderElement());
719
-
720
825
  const versions = this.registration.versionsByMode();
721
- const scopeURL = this.registration.scopeURL;
722
- const title = this.registration.isDeleted ? i18nString(UIStrings.sDeleted, {PH1: scopeURL}) : scopeURL;
723
- this.section.setTitle(title);
724
826
 
725
827
  const active = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.ACTIVE);
726
828
  const waiting = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.WAITING);
727
829
  const installing = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.INSTALLING);
728
830
  const redundant = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.REDUNDANT);
729
831
 
730
- // clang-format off
731
- // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
732
- render(html`
733
- ${this.renderSourceField(active ?? redundant)}
734
- ${this.renderStatusField(active, waiting, installing, redundant)}
735
- ${this.renderClientsField(active ?? redundant)}
736
- ${this.renderSyncNotificationField(i18nString(UIStrings.pushString), this.pushNotificationDataSetting.get(),
737
- i18nString(UIStrings.pushData), this.push.bind(this), 'push-message')}
738
- ${this.renderSyncNotificationField(i18nString(UIStrings.syncString), this.syncTagNameSetting.get(),
739
- i18nString(UIStrings.syncTag), this.sync.bind(this), 'sync-tag')}
740
- ${this.renderSyncNotificationField(i18nString(UIStrings.periodicSync), this.periodicSyncTagNameSetting.get(),
741
- i18nString(UIStrings.periodicSyncTag), tag => this.periodicSync(tag),
742
- 'periodic-sync-tag')}
743
- ${this.renderUpdateCycleField()}
744
- ${this.renderRouterField()}
745
- `, this.contentElement);
746
- // clang-format on
832
+ const title = this.getTitle();
833
+
834
+ const input: SectionViewInput = {
835
+ title,
836
+ isDeleted: this.registration.isDeleted,
837
+ errorsLength: this.registration.errors?.length ?? 0,
838
+ pushData: this.pushNotificationDataSetting.get(),
839
+ syncTag: this.syncTagNameSetting.get(),
840
+ periodicSyncTag: this.periodicSyncTagNameSetting.get(),
841
+ updateCycleTable: this.updateCycleView.tableElement,
842
+ activeVersion: active,
843
+ waitingVersion: waiting,
844
+ installingVersion: installing,
845
+ redundantVersion: redundant,
846
+ renderClientInfo: this.renderClientInfo.bind(this),
847
+ onNetworkRequests: this.networkRequestsClicked.bind(this),
848
+ onUpdate: this.updateButtonClicked.bind(this),
849
+ onUnregister: this.unregisterButtonClicked.bind(this),
850
+ onPush: this.push.bind(this),
851
+ onSync: this.sync.bind(this),
852
+ onPeriodicSync: this.periodicSync.bind(this),
853
+ onStop: this.stopButtonClicked.bind(this),
854
+ onStart: this.startButtonClicked.bind(this),
855
+ onSkipWaiting: this.skipButtonClicked.bind(this),
856
+ };
857
+
858
+ this.#view(input, undefined, this.contentElement);
747
859
  this.updateCycleView.refresh();
748
860
 
749
861
  return Promise.resolve();
@@ -753,35 +865,6 @@ export class Section extends UI.Widget.VBox {
753
865
  this.manager.deleteRegistration(this.registration.id);
754
866
  }
755
867
 
756
- private renderUpdateCycleField(): LitTemplate {
757
- return html`
758
- <div class="report-field">
759
- <div class="report-field-name">${i18nString(UIStrings.updateCycle)}</div>
760
- <div class="report-field-value">
761
- ${this.updateCycleView.tableElement}
762
- </div>
763
- </div>`;
764
- }
765
-
766
- private renderRouterField(): LitTemplate {
767
- const versions = this.registration.versionsByMode();
768
- const active = versions.get(SDK.ServiceWorkerManager.ServiceWorkerVersion.Modes.ACTIVE);
769
- const title = i18nString(UIStrings.routers);
770
- if (active?.routerRules && active.routerRules.length > 0) {
771
- // If there is at least one registered rule in the active version, append the router filed.
772
- // clang-format off
773
- return html`
774
- <div class="report-field">
775
- <div class="report-field-name">${title}</div>
776
- <div class="report-field-value">
777
- ${widget(ApplicationComponents.ServiceWorkerRouterView.ServiceWorkerRouterView, { rules: active.routerRules })}
778
- </div>
779
- </div>`;
780
- // clang-format on
781
- }
782
- return nothing;
783
- }
784
-
785
868
  private updateButtonClicked(): void {
786
869
  void this.manager.updateRegistration(this.registration.id);
787
870
  }