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