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