1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {NAMED_ROW} from '../../components/tracks/named_slice_track'; 16import {LONG, NUM, STR} from '../../trace_processor/query_result'; 17import {Slice} from '../../public/track'; 18import { 19 CustomSqlImportConfig, 20 CustomSqlTableDefConfig, 21 CustomSqlTableSliceTrack, 22} from '../../components/tracks/custom_sql_table_slice_track'; 23import {TrackEventDetails, TrackEventSelection} from '../../public/selection'; 24import {Duration, Time} from '../../base/time'; 25import {PageLoadDetailsPanel} from './page_load_details_panel'; 26import {StartupDetailsPanel} from './startup_details_panel'; 27import {WebContentInteractionPanel} from './web_content_interaction_details_panel'; 28import {GenericSliceDetailsTab} from './generic_slice_details_tab'; 29 30export const CRITICAL_USER_INTERACTIONS_KIND = 31 'org.chromium.CriticalUserInteraction.track'; 32 33export const CRITICAL_USER_INTERACTIONS_ROW = { 34 ...NAMED_ROW, 35 scopedId: NUM, 36 type: STR, 37}; 38export type CriticalUserInteractionRow = typeof CRITICAL_USER_INTERACTIONS_ROW; 39 40export interface CriticalUserInteractionSlice extends Slice { 41 scopedId: number; 42 type: string; 43} 44 45export class CriticalUserInteractionTrack extends CustomSqlTableSliceTrack { 46 static readonly kind = `/critical_user_interactions`; 47 48 getSqlDataSource(): CustomSqlTableDefConfig { 49 return { 50 columns: [ 51 // The scoped_id is not a unique identifier within the table; generate 52 // a unique id from type and scoped_id on the fly to use for slice 53 // selection. 54 'hash(type, scoped_id) AS id', 55 'scoped_id AS scopedId', 56 'name', 57 'ts', 58 'dur', 59 'type', 60 ], 61 sqlTableName: 'chrome_interactions', 62 }; 63 } 64 65 async getSelectionDetails( 66 id: number, 67 ): Promise<TrackEventDetails | undefined> { 68 const query = ` 69 SELECT 70 ts, 71 dur, 72 type 73 FROM (${this.getSqlSource()}) 74 WHERE id = ${id} 75 `; 76 77 const result = await this.engine.query(query); 78 if (result.numRows() === 0) { 79 return undefined; 80 } 81 82 const row = result.iter({ 83 ts: LONG, 84 dur: LONG, 85 type: STR, 86 }); 87 88 return { 89 ts: Time.fromRaw(row.ts), 90 dur: Duration.fromRaw(row.dur), 91 interactionType: row.type, 92 }; 93 } 94 95 getSqlImports(): CustomSqlImportConfig { 96 return { 97 modules: ['chrome.interactions'], 98 }; 99 } 100 101 getRowSpec(): CriticalUserInteractionRow { 102 return CRITICAL_USER_INTERACTIONS_ROW; 103 } 104 105 rowToSlice(row: CriticalUserInteractionRow): CriticalUserInteractionSlice { 106 const baseSlice = super.rowToSlice(row); 107 const scopedId = row.scopedId; 108 const type = row.type; 109 return {...baseSlice, scopedId, type}; 110 } 111 112 override detailsPanel(sel: TrackEventSelection) { 113 switch (sel.interactionType) { 114 case 'chrome_page_loads': 115 return new PageLoadDetailsPanel(this.trace, sel.eventId); 116 case 'chrome_startups': 117 return new StartupDetailsPanel(this.trace, sel.eventId); 118 case 'chrome_web_content_interactions': 119 return new WebContentInteractionPanel(this.trace, sel.eventId); 120 default: 121 return new GenericSliceDetailsTab( 122 this.trace, 123 'chrome_interactions', 124 sel.eventId, 125 'Chrome Interaction', 126 ); 127 } 128 } 129} 130