1// Copyright 2023 The Chromium Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import {assert} from 'chrome://resources/js/assert.js'; 6 7/** 8 * Key-Value pair for the summary and metrics table. 9 */ 10interface KeyValue { 11 key: string; 12 value: number|string|boolean|[number]; 13} 14 15/** 16 * Sequence information for an event. 17 */ 18interface SequenceMetadata { 19 id: string; 20 systemUptimeMs: string; 21 resetCounter: string; 22} 23 24/** 25 * An event and its data. This includes metadata about the event and sequence 26 * information if applicable. 27 */ 28export interface StructuredMetricEvent { 29 project: string; 30 event: string; 31 type: string; 32 sequenceMetadata?: SequenceMetadata; 33 metrics: KeyValue[]; 34} 35 36/** 37 * Summary about Structured Metrics service. 38 */ 39export interface StructuredMetricsSummary { 40 enabled: boolean; 41 flags: KeyValue[]; 42 crosDeviceId: string; 43} 44 45/** 46 * Updates the Summary table with new information. 47 * 48 * @param summaryBody Body of the summary table. 49 * @param summary Summary object to populate the table. 50 * @param template Key-Value pair HTML template. 51 */ 52export function updateStructuredMetricsSummary( 53 summaryBody: HTMLElement, summary: StructuredMetricsSummary, 54 template: HTMLTemplateElement): void { 55 // Clear the table first. 56 summaryBody.replaceChildren(); 57 58 const enabled = 59 buildKeyValueRow('Enabled', summary.enabled.toString(), template); 60 summaryBody.append(enabled); 61 62 // If we do not get a value, do not display it. This value doesn't make sense 63 // on some platforms. 64 if (summary.crosDeviceId) { 65 const crosDeviceId = 66 buildKeyValueRow('CrOS Device Id', summary.crosDeviceId, template); 67 summaryBody.append(crosDeviceId); 68 } 69} 70 71/** 72 * Updates the events table with the events recorded by the client. 73 * 74 * @param eventBody Body of the event table. 75 * @param events List of events to populate the table. 76 * @param template HTML template for the event table row. 77 * @param kvTemplate Key-Value pair HTML template. 78 */ 79export function updateStructuredMetricsEvents( 80 eventBody: HTMLElement, events: StructuredMetricEvent[], 81 eventTemplate: HTMLTemplateElement, detailsTemplate: HTMLTemplateElement, 82 kvTemplate: HTMLTemplateElement): void { 83 // If chrome://metrics-internal is opened on Windows, Mac, or Linux and 84 // Structured Metrics is disabled, we should do nothing. 85 if (events === null) { 86 return; 87 } 88 89 eventBody.replaceChildren(); 90 91 for (const event of events) { 92 const row = eventTemplate.content.cloneNode(true) as HTMLElement; 93 const [project, evn, type, uptime] = row.querySelectorAll('td'); 94 95 assert(project); 96 project.textContent = event.project; 97 98 assert(evn); 99 evn.textContent = event.event; 100 101 assert(type); 102 type.textContent = event.type; 103 104 assert(uptime); 105 uptime.textContent = event.sequenceMetadata?.systemUptimeMs ?? '-'; 106 107 const detailsRow = detailsTemplate.content.cloneNode(true) as HTMLElement; 108 const metricsRow = detailsRow.querySelector<HTMLElement>('#metrics-row'); 109 assert(metricsRow); 110 111 const [details, metrics] = detailsRow.querySelectorAll('tbody'); 112 assert(details); 113 assert(metrics); 114 115 updateEventDetailsTable(details, event, kvTemplate); 116 updateEventMetricsTable(metrics, event, kvTemplate); 117 118 const eventRow = row.querySelector('#event-row'); 119 assert(eventRow); 120 eventRow.addEventListener('click', () => { 121 if (metricsRow.style.display === 'none') { 122 metricsRow.style.display = 'table-row'; 123 } else { 124 metricsRow.style.display = 'none'; 125 } 126 }, false); 127 128 eventBody.append(row); 129 eventBody.append(detailsRow); 130 } 131} 132 133function updateEventDetailsTable( 134 detailTable: HTMLElement, event: StructuredMetricEvent, 135 template: HTMLTemplateElement): void { 136 detailTable.replaceChildren(); 137 138 const resetCounter = event.sequenceMetadata?.resetCounter ?? '-'; 139 const systemUptime = event.sequenceMetadata?.systemUptimeMs ?? '-'; 140 const eventId = event.sequenceMetadata?.id ?? '-'; 141 142 const resetCounterRow = buildKeyValueRow('Reset Id', resetCounter, template); 143 const systemUptimeRow = 144 buildKeyValueRow('System Uptime', systemUptime, template); 145 const eventIdRow = buildKeyValueRow('Event Id', eventId, template); 146 147 detailTable.append(resetCounterRow); 148 detailTable.append(systemUptimeRow); 149 detailTable.append(eventIdRow); 150} 151 152function updateEventMetricsTable( 153 metricsTable: HTMLElement, event: StructuredMetricEvent, 154 template: HTMLTemplateElement): void { 155 metricsTable.replaceChildren(); 156 for (const metric of event.metrics) { 157 const metricRow = 158 buildKeyValueRow(metric.key, metric.value.toString(), template); 159 metricsTable.append(metricRow); 160 } 161} 162 163function buildKeyValueRow( 164 key: string, value: string, template: HTMLTemplateElement): HTMLElement { 165 const kvRow = template.content.cloneNode(true) as HTMLElement; 166 167 const [k, v] = kvRow.querySelectorAll('td'); 168 assert(k); 169 k.textContent = key; 170 assert(v); 171 v.textContent = value; 172 173 return kvRow; 174} 175