xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.QueryPage/query_page.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2020 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 m from 'mithril';
16import {SimpleResizeObserver} from '../../base/resize_observer';
17import {undoCommonChatAppReplacements} from '../../base/string_utils';
18import {QueryResponse, runQuery} from '../../components/query_table/queries';
19import {Callout} from '../../widgets/callout';
20import {Editor} from '../../widgets/editor';
21import {PageWithTraceAttrs} from '../../public/page';
22import {QueryHistoryComponent, queryHistoryStorage} from './query_history';
23import {Trace, TraceAttrs} from '../../public/trace';
24import {addQueryResultsTab} from '../../components/query_table/query_result_tab';
25import {QueryTable} from '../../components/query_table/query_table';
26
27interface QueryPageState {
28  enteredText: string;
29  executedQuery?: string;
30  queryResult?: QueryResponse;
31  heightPx: string;
32  generation: number;
33}
34
35const state: QueryPageState = {
36  enteredText: '',
37  heightPx: '100px',
38  generation: 0,
39};
40
41function runManualQuery(trace: Trace, query: string) {
42  state.executedQuery = query;
43  state.queryResult = undefined;
44  runQuery(undoCommonChatAppReplacements(query), trace.engine).then(
45    (resp: QueryResponse) => {
46      addQueryResultsTab(
47        trace,
48        {
49          query: query,
50          title: 'Standalone Query',
51          prefetchedResponse: resp,
52        },
53        'analyze_page_query',
54      );
55      // We might have started to execute another query. Ignore it in that
56      // case.
57      if (state.executedQuery !== query) {
58        return;
59      }
60      state.queryResult = resp;
61      trace.scheduleFullRedraw();
62    },
63  );
64}
65
66export type QueryInputAttrs = TraceAttrs;
67
68class QueryInput implements m.ClassComponent<QueryInputAttrs> {
69  private resize?: Disposable;
70
71  oncreate({dom}: m.CVnodeDOM<QueryInputAttrs>): void {
72    this.resize = new SimpleResizeObserver(dom, () => {
73      state.heightPx = (dom as HTMLElement).style.height;
74    });
75    (dom as HTMLElement).style.height = state.heightPx;
76  }
77
78  onremove(): void {
79    if (this.resize) {
80      this.resize[Symbol.dispose]();
81      this.resize = undefined;
82    }
83  }
84
85  view({attrs}: m.CVnode<QueryInputAttrs>) {
86    return m(Editor, {
87      generation: state.generation,
88      initialText: state.enteredText,
89
90      onExecute: (text: string) => {
91        if (!text) {
92          return;
93        }
94        queryHistoryStorage.saveQuery(text);
95        runManualQuery(attrs.trace, text);
96      },
97
98      onUpdate: (text: string) => {
99        state.enteredText = text;
100        attrs.trace.scheduleFullRedraw();
101      },
102    });
103  }
104}
105
106export class QueryPage implements m.ClassComponent<PageWithTraceAttrs> {
107  view({attrs}: m.CVnode<PageWithTraceAttrs>) {
108    return m(
109      '.query-page',
110      m(Callout, 'Enter query and press Cmd/Ctrl + Enter'),
111      state.enteredText.includes('"') &&
112        m(
113          Callout,
114          {icon: 'warning'},
115          `" (double quote) character observed in query; if this is being used to ` +
116            `define a string, please use ' (single quote) instead. Using double quotes ` +
117            `can cause subtle problems which are very hard to debug.`,
118        ),
119      m(QueryInput, attrs),
120      state.executedQuery === undefined
121        ? null
122        : m(QueryTable, {
123            trace: attrs.trace,
124            query: state.executedQuery,
125            resp: state.queryResult,
126            fillParent: false,
127          }),
128      m(QueryHistoryComponent, {
129        trace: attrs.trace,
130        runQuery: (q: string) => runManualQuery(attrs.trace, q),
131        setQuery: (q: string) => {
132          state.enteredText = q;
133          state.generation++;
134          attrs.trace.scheduleFullRedraw();
135        },
136      }),
137    );
138  }
139}
140