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